diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index fe9c27e95d4..469b94256c1 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -41,9 +41,9 @@ jobs: ref: refs/pull/${{ github.event.issue.number || github.event.number }}/head fetch-depth: 2 - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 + uses: mozilla-actions/sccache-action@v0.0.4 - name: Install taplo - uses: baptiste0928/cargo-install@v2 + uses: baptiste0928/cargo-install@v3 with: crate: taplo-cli locked: true @@ -52,7 +52,7 @@ jobs: - name: Bootstrap dependencies run: sudo apt update && python3 ./mach bootstrap - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 9d0fdb18c88..158ddee053b 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -83,7 +83,7 @@ jobs: ref: refs/pull/${{ github.event.number }}/head fetch-depth: 2 # This is necessary for `test-tidy`. - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 + uses: mozilla-actions/sccache-action@v0.0.4 - name: Set LIBCLANG_PATH env # needed for bindgen in mozangle if: ${{ !inputs.upload }} # not needed on ubuntu 20.04 used for nightly run: echo "LIBCLANG_PATH=/usr/lib/llvm-14/lib" >> $GITHUB_ENV @@ -91,11 +91,10 @@ jobs: with: python-version: '3.10' - name: Install taplo - uses: baptiste0928/cargo-install@v2 + uses: baptiste0928/cargo-install@v3 with: crate: taplo-cli locked: true - cache-key: ${{ inputs.upload && '20.04' || '22.04' }} - name: Bootstrap Python run: python3 -m pip install --upgrade pip - name: Bootstrap dependencies @@ -114,7 +113,7 @@ jobs: run: ./mach test-scripts - name: Unit tests if: ${{ inputs.unit-tests }} - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 20 max_attempts: 2 # https://github.com/servo/servo/issues/30683 diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index dd78f526bfb..c48dab55873 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -77,9 +77,9 @@ jobs: with: ref: refs/pull/${{ github.event.number }}/head - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 + uses: mozilla-actions/sccache-action@v0.0.4 - name: Install taplo - uses: baptiste0928/cargo-install@v2 + uses: baptiste0928/cargo-install@v3 with: crate: taplo-cli locked: true @@ -93,7 +93,7 @@ jobs: python3 ./mach build --${{ inputs.profile }} cp -r target/cargo-timings target/cargo-timings-macos - name: Smoketest - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: # See https://github.com/servo/servo/issues/30757 timeout_minutes: 5 max_attempts: 2 @@ -102,7 +102,7 @@ jobs: run: ./mach test-scripts - name: Unit tests if: ${{ inputs.unit-tests }} - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 20 # https://github.com/servo/servo/issues/30275 max_attempts: 3 # https://github.com/servo/servo/issues/30683 @@ -110,7 +110,7 @@ jobs: - name: Build mach package run: python3 ./mach package --${{ inputs.profile }} - name: Run DMG smoketest - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: # See https://github.com/servo/servo/issues/30757 timeout_minutes: 5 max_attempts: 2 diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml index 2834565e26d..a8fa298c01f 100644 --- a/.github/workflows/try.yml +++ b/.github/workflows/try.yml @@ -48,7 +48,7 @@ jobs: } >> $GITHUB_OUTPUT - name: Configuration id: configuration - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | // When triggered via a push try the `try` branch, search the last commit for a configuration object. diff --git a/.github/workflows/try_labels.yml b/.github/workflows/try_labels.yml index 1beae2a9d51..f7e8124e09a 100644 --- a/.github/workflows/try_labels.yml +++ b/.github/workflows/try_labels.yml @@ -15,7 +15,7 @@ jobs: try_string: ${{ steps.try_string.outputs.result }} steps: - name: Collect Labels - uses: actions/github-script@v6 + uses: actions/github-script@v7 id: try_string with: result-encoding: string @@ -99,7 +99,7 @@ jobs: } >> $GITHUB_OUTPUT - name: Comment Run Start if: ${{ steps.try_string.outputs.result }} - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: result-encoding: string script: | @@ -149,7 +149,7 @@ jobs: steps: - name: Success if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const url = context.serverUrl + @@ -165,7 +165,7 @@ jobs: }); - name: Failure if: ${{ contains(needs.*.result, 'failure') }} - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const url = context.serverUrl + diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 2aa6b0c5d50..4d203980f27 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -61,7 +61,7 @@ jobs: - name: ccache uses: hendrikmuhs/ccache-action@v1.2 - name: Install taplo - uses: baptiste0928/cargo-install@v2 + uses: baptiste0928/cargo-install@v3 with: crate: taplo-cli locked: true @@ -78,6 +78,11 @@ jobs: python mach fetch python mach bootstrap-gstreamer cargo install --path support/crown + # For some reason WiX isn't currently on the GitHub runner path. This is a + # temporary workaround until that is fixed. + - name: Add WiX to Path + run: | + "$env:WIX\bin" >> $env:GITHUB_PATH - name: Build (${{ inputs.profile }}) run: | python mach build --${{ inputs.profile }} @@ -88,7 +93,7 @@ jobs: run: python mach smoketest --angle --${{ inputs.profile }} - name: Unit tests if: ${{ inputs.unit-tests }} - uses: nick-fields/retry@v2 + uses: nick-fields/retry@v3 with: timeout_minutes: 30 max_attempts: 3 # https://github.com/servo/servo/issues/30683 diff --git a/Cargo.lock b/Cargo.lock index c1b0b5afa18..e1b22896860 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,9 +50,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -242,7 +242,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -345,7 +345,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.50", + "syn 2.0.52", "which", ] @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.2" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3b1be7772ee4501dba05acbe66bb1e8760f6a6c474a36035631638e4415f130" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "byte-slice-cast" @@ -541,7 +541,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -628,9 +628,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" dependencies = [ "jobserver", "libc", @@ -684,9 +684,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" dependencies = [ "android-tzdata", "iana-time-zone", @@ -704,7 +704,7 @@ checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading 0.8.1", + "libloading 0.8.3", ] [[package]] @@ -1021,9 +1021,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -1086,7 +1086,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf", + "phf 0.11.2", "serde", "smallvec", ] @@ -1098,7 +1098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1118,15 +1118,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20" dependencies = [ "bitflags 2.4.2", - "libloading 0.8.1", + "libloading 0.7.4", "winapi", ] [[package]] name = "darling" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ "darling_core", "darling_macro", @@ -1134,26 +1134,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] name = "darling_macro" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1185,7 +1185,7 @@ dependencies = [ name = "deny_public_fields" version = "0.0.1" dependencies = [ - "syn 2.0.50", + "syn 2.0.52", "synstructure 0.13.1", ] @@ -1208,11 +1208,12 @@ dependencies = [ [[package]] name = "derive_common" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "synstructure 0.13.1", ] @@ -1315,7 +1316,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.1", + "libloading 0.7.4", ] [[package]] @@ -1323,7 +1324,7 @@ name = "dom_struct" version = "0.0.1" dependencies = [ "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1332,7 +1333,7 @@ version = "0.0.1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1732,7 +1733,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1849,7 +1850,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -1997,9 +1998,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -2020,9 +2021,9 @@ dependencies = [ [[package]] name = "gilrs-core" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0af1827b7dd2f36d740ae804c1b3ea0d64c12533fb61ff91883005143a0e8c5a" +checksum = "85c132270a155f2548e67d66e731075c336c39098afc555752f3df8f882c720e" dependencies = [ "core-foundation", "inotify", @@ -2031,7 +2032,7 @@ dependencies = [ "libc", "libudev-sys", "log", - "nix 0.27.1", + "nix 0.28.0", "uuid", "vec_map", "wasm-bindgen", @@ -2047,15 +2048,15 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2093,9 +2094,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +checksum = "ab9e86540b5d8402e905ad4ce7d6aa544092131ab564f3102175af176b90a053" dependencies = [ "bitflags 2.4.2", "futures-channel", @@ -2109,30 +2110,28 @@ dependencies = [ "gobject-sys", "libc", "memchr", - "once_cell", "smallvec", "thiserror", ] [[package]] name = "glib-macros" -version = "0.18.5" +version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +checksum = "0f5897ca27a83e4cdc7b4666850bade0a2e73e17689aabafcc9acddad9d823b8" dependencies = [ "heck", - "proc-macro-crate 2.0.1", - "proc-macro-error", + "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] name = "glib-sys" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4" dependencies = [ "libc", "system-deps", @@ -2167,9 +2166,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979" dependencies = [ "glib-sys", "libc", @@ -2217,9 +2216,9 @@ dependencies = [ [[package]] name = "gstreamer" -version = "0.21.3" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de95703f4c8e79f4f4e42279cf1ab0e5a46b7ece4a9dfcd16424164af7be9055" +checksum = "48a5e10c539f8b594c50f6cd1bd1cd07785e06d701a077bff397ad211bc92e88" dependencies = [ "cfg-if", "futures-channel", @@ -2232,19 +2231,19 @@ dependencies = [ "muldiv", "num-integer", "num-rational", + "once_cell", "option-operations", "paste", "pin-project-lite", - "pretty-hex", "smallvec", "thiserror", ] [[package]] name = "gstreamer-app" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bc8090a8806193237e7b6531ee429ff6e39686425f5c3eb06dfa75875390fb" +checksum = "50184e88d3462a796a5924fb329839c102b22f9383c1636323fa4ef5255dea92" dependencies = [ "futures-core", "futures-sink", @@ -2257,9 +2256,9 @@ dependencies = [ [[package]] name = "gstreamer-app-sys" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aea07f07a3f17278e6998390ecaea127e476f0af0360c2d83d96e6d3a97fb75e" +checksum = "6771c0939f286fb261525494a0aad29435b37e802284756bab24afe3bbca7476" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -2270,9 +2269,9 @@ dependencies = [ [[package]] name = "gstreamer-audio" -version = "0.21.3" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a97c8a4e9c889b4a7b2feda4be70edc5c3e012ddbf97928cf0a020ca1e4212d" +checksum = "e7529f913cb6cbf1305ebc58ace01391cf9bb5a833810cf6e7c09e9a37d130f2" dependencies = [ "cfg-if", "glib", @@ -2280,13 +2279,15 @@ dependencies = [ "gstreamer-audio-sys", "gstreamer-base", "libc", + "once_cell", + "smallvec", ] [[package]] name = "gstreamer-audio-sys" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bd94ae8b177377855b38c3d809c686526786cdb771e6d68510509634b955d1" +checksum = "34d92a1e2a915874f70f0a33c3ea4589bc6b66a138b6ec8bb6acedf49bdec2c3" dependencies = [ "glib-sys", "gobject-sys", @@ -2298,9 +2299,9 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb150b6904a49052237fede7cc2e6479df6ced5043d95e6af8134bc141a3167f" +checksum = "514c71195b53c7eced4842b66ca9149833e41cf6a1d949e45e2ca4a4fa929850" dependencies = [ "atomic_refcell", "cfg-if", @@ -2312,9 +2313,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ca701f9078fe115b29b24c80910b577f9cb5b039182f050dbadf5933594b64" +checksum = "286591e0f85bbda1adf9bab6f21d015acd9ca0a4d4acb61da65e3d0487e23c4e" dependencies = [ "glib-sys", "gobject-sys", @@ -2325,9 +2326,9 @@ dependencies = [ [[package]] name = "gstreamer-gl" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ecfb91128263c160448a915a15e430cfdc69317b1b087316222e0693bb51b90" +checksum = "7d21c0c5fbf74018a0254b3ab77bca0a5b2c0f002bcfd910c09113ae90a95d98" dependencies = [ "glib", "gstreamer", @@ -2335,13 +2336,14 @@ dependencies = [ "gstreamer-gl-sys", "gstreamer-video", "libc", + "once_cell", ] [[package]] name = "gstreamer-gl-egl" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a173ab223aa3c46d5f2be02b494c2086096fa3e932e2953d6392fcc07da5dc82" +checksum = "dfde7bf67f5f7c87e1ff29cdeea4918530d677b51e3f4847121ada44f1fab139" dependencies = [ "glib", "gstreamer", @@ -2352,9 +2354,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-egl-sys" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee3c88b6f01c8a5f9f7ba39f9805ac7026fd269fccb04d2a4b488bb928a8e954" +checksum = "7c9ec3c03af5d4ed3e58ddbca4eea13e90e01b88e37f6c0689b26e05168eb7bf" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -2364,9 +2366,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-sys" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580971b3c99a667c9739812d499e6c5cadbb92873f984cd0d1d0b4e7346f1cd" +checksum = "61d1e3b9b02abc23835e9d770f2bd705b67a50406ea37e963b4526a77c6a7cd8" dependencies = [ "glib-sys", "gobject-sys", @@ -2379,9 +2381,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-x11" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7021b9e32c9d2c2830898cfdb60f8b1e7fea560b7cd3e221b9fad01a32af67d" +checksum = "42ed82941c84668d89dbf81f220083422268c22ec6ab4991806649ed6758cec8" dependencies = [ "glib", "gstreamer", @@ -2392,9 +2394,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-x11-sys" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49aef03c7b9e159b5f4c557efe4a597437ea6e5e10f934ae8b214386c6ebcdc" +checksum = "54b59f2782f5e71e3ef5fd534598938966a4dc3911f2540807f7d13b586e4ed1" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -2404,9 +2406,9 @@ dependencies = [ [[package]] name = "gstreamer-player" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276900527b2f8323c6ed97ab3ed42854bb169a993fb8cef946fb43d1e9097a66" +checksum = "5950d1d194935b27a0b2af99afe83dc9697149a1b55230f16c51eefe798c4a51" dependencies = [ "glib", "gstreamer", @@ -2417,9 +2419,9 @@ dependencies = [ [[package]] name = "gstreamer-player-sys" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5ef4d00b43d0aa94e9a518e6ef4a4c504b4b855304a0a5f4ed1493d5e5ca66c" +checksum = "e0ff7af3d3c4692a36ce1c4695b2a3a6563d8e5724feded38def679bd755202f" dependencies = [ "glib-sys", "gobject-sys", @@ -2431,9 +2433,9 @@ dependencies = [ [[package]] name = "gstreamer-sdp" -version = "0.21.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87946f9c6fabb44a73fb0b3b3d611c7d4468f741dd8e4f9a3d54e1ab2a6a8b36" +checksum = "2f9aa459d119aa787efc18edcc591736e1e6cd488835825d1807532456ba2dbd" dependencies = [ "glib", "gstreamer", @@ -2442,9 +2444,9 @@ dependencies = [ [[package]] name = "gstreamer-sdp-sys" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006e4bdf4618c70a2cb440ca4adf1a4140fc118b1b314684903a48f0ef5e1da1" +checksum = "5d80dfc4811c67ca66586b4e470f85ab25aca4ea92bd2ba2665552c1dd0e5609" dependencies = [ "glib-sys", "gstreamer-sys", @@ -2454,9 +2456,9 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.21.2" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "564cda782b3e6eed1b81cb4798a06794db56440fb05b422505be689f34ce3bc4" +checksum = "d5ddf526b3bf90ea627224c804f00b8bcb0452e3b447978b4d5092f8e8ff5918" dependencies = [ "glib-sys", "gobject-sys", @@ -2466,9 +2468,9 @@ dependencies = [ [[package]] name = "gstreamer-video" -version = "0.21.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e85b2a4d1d3b7a98ae03806c3ed5c2db89d6b37a5f138780b48de015d68715e5" +checksum = "5ab3f4045ddb92bf2b469f5db8825d4f5eb46e4beff661fc97f50bb4e2b2c626" dependencies = [ "cfg-if", "futures-channel", @@ -2477,14 +2479,15 @@ dependencies = [ "gstreamer-base", "gstreamer-video-sys", "libc", + "once_cell", "thiserror", ] [[package]] name = "gstreamer-video-sys" -version = "0.21.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302318d98e6b054501e485b6bb4ee20225823218f4a8660c182f115a33b16ee" +checksum = "c1ea7996ba44fbbf563aeeda96e24259efc9f06b407854d837ee58e260d7ba78" dependencies = [ "glib-sys", "gobject-sys", @@ -2496,9 +2499,9 @@ dependencies = [ [[package]] name = "gstreamer-webrtc" -version = "0.21.2" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7200f2eb49d609afcb3e064b1ba75cbd041012a1855ea921b222e772634ff370" +checksum = "8c4b120a15362865dedc4417ca605dd734f5699b75f42cfef08a9140dd333426" dependencies = [ "glib", "gstreamer", @@ -2509,9 +2512,9 @@ dependencies = [ [[package]] name = "gstreamer-webrtc-sys" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32df9bf9d801e6f874de2476fd3dcf2a1ca9247e8979bd4de034a45fd399663" +checksum = "8968383c25abb00bdc434d21d92947654e3656503ccb24c8f1218cd96342c073" dependencies = [ "glib-sys", "gstreamer-sdp-sys", @@ -2538,7 +2541,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.3", + "indexmap 2.2.5", "slab", "tokio", "tokio-util", @@ -2547,9 +2550,9 @@ dependencies = [ [[package]] name = "half" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" dependencies = [ "cfg-if", "crunchy", @@ -2617,9 +2620,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -2664,9 +2667,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -2798,9 +2801,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -2832,9 +2835,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -3009,9 +3012,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -3021,7 +3024,7 @@ name = "jstraceable_derive" version = "0.0.1" dependencies = [ "proc-macro2", - "syn 2.0.50", + "syn 2.0.52", "synstructure 0.13.1", ] @@ -3126,6 +3129,7 @@ dependencies = [ "serde_json", "servo_arc", "servo_config", + "servo_geometry", "servo_url", "style", "style_traits", @@ -3295,12 +3299,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -3417,9 +3421,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lyon_geom" @@ -3438,15 +3442,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - [[package]] name = "mach2" version = "0.4.2" @@ -3468,6 +3463,7 @@ dependencies = [ [[package]] name = "malloc_size_of" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "accountable-refcell", "app_units", @@ -3476,7 +3472,7 @@ dependencies = [ "cssparser", "euclid", "http", - "indexmap 2.2.3", + "indexmap 2.2.5", "keyboard-types", "selectors", "serde", @@ -3521,8 +3517,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", - "phf", - "phf_codegen", + "phf 0.10.1", + "phf_codegen 0.10.0", "string_cache", "string_cache_codegen", "tendril", @@ -3676,9 +3672,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -3709,12 +3705,6 @@ dependencies = [ "walkdir", ] -[[package]] -name = "mozbuild" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903970ae2f248d7275214cf8f387f8ba0c4ea7e3d87a320e85493db60ce28616" - [[package]] name = "mozjs" version = "0.14.1" @@ -3773,7 +3763,7 @@ dependencies = [ "bitflags 2.4.2", "codespan-reporting", "hexf-parse", - "indexmap 2.2.3", + "indexmap 2.2.5", "log", "num-traits", "rustc-hash", @@ -3938,12 +3928,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.4.2", "cfg-if", + "cfg_aliases", "libc", ] @@ -3987,13 +3978,13 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-derive" -version = "0.3.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -4278,10 +4269,18 @@ name = "phf" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ "phf_macros", - "phf_shared", - "proc-macro-hack", + "phf_shared 0.11.2", ] [[package]] @@ -4290,8 +4289,18 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", ] [[package]] @@ -4300,22 +4309,31 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ - "phf_shared", + "phf_shared 0.10.0", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", "rand", ] [[package]] name = "phf_macros" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro-hack", + "phf_generator 0.11.2", + "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -4328,23 +4346,32 @@ dependencies = [ ] [[package]] -name = "pin-project" -version = "1.1.4" +name = "phf_shared" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -4418,12 +4445,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" -[[package]] -name = "pretty-hex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc83ee4a840062f368f9096d80077a9841ec117e17e7f700df81958f1451254" - [[package]] name = "prettyplease" version = "0.2.16" @@ -4431,7 +4452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -4447,44 +4468,13 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "toml_datetime", "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - [[package]] name = "proc-macro2" version = "1.0.78" @@ -4645,9 +4635,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -4721,9 +4711,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -4915,7 +4905,7 @@ dependencies = [ "http", "hyper_serde", "image", - "indexmap 2.2.3", + "indexmap 2.2.5", "ipc-channel", "itertools 0.12.1", "jstraceable_derive", @@ -4937,9 +4927,9 @@ dependencies = [ "num_cpus", "parking_lot", "percent-encoding", - "phf", - "phf_codegen", - "phf_shared", + "phf 0.10.1", + "phf_codegen 0.10.0", + "phf_shared 0.10.0", "pixels", "profile_traits", "range", @@ -5085,6 +5075,7 @@ dependencies = [ [[package]] name = "selectors" version = "0.24.0" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "bitflags 1.3.2", "cssparser", @@ -5092,8 +5083,8 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf", - "phf_codegen", + "phf 0.11.2", + "phf_codegen 0.11.2", "precomputed-hash", "servo_arc", "size_of_test", @@ -5134,7 +5125,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5194,7 +5185,7 @@ dependencies = [ [[package]] name = "servo-media" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "once_cell", "servo-media-audio", @@ -5207,7 +5198,7 @@ dependencies = [ [[package]] name = "servo-media-audio" version = "0.2.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "byte-slice-cast", "euclid", @@ -5228,7 +5219,7 @@ dependencies = [ [[package]] name = "servo-media-derive" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "proc-macro2", "quote", @@ -5238,7 +5229,7 @@ dependencies = [ [[package]] name = "servo-media-dummy" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "ipc-channel", "servo-media", @@ -5252,7 +5243,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "byte-slice-cast", "glib", @@ -5286,7 +5277,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "gstreamer", "gstreamer-video", @@ -5296,7 +5287,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-android" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "glib", "gstreamer", @@ -5310,7 +5301,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-unix" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "glib", "gstreamer", @@ -5325,7 +5316,7 @@ dependencies = [ [[package]] name = "servo-media-player" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "ipc-channel", "serde", @@ -5337,7 +5328,7 @@ dependencies = [ [[package]] name = "servo-media-streams" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "lazy_static", "uuid", @@ -5346,12 +5337,12 @@ dependencies = [ [[package]] name = "servo-media-traits" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" [[package]] name = "servo-media-webrtc" version = "0.1.0" -source = "git+https://github.com/servo/media#a77995f1c8fe7d221ce3afef8bd8b92342e42321" +source = "git+https://github.com/servo/media#c04bb3025320cf8cd75ecf00ab8cacb39ff81843" dependencies = [ "lazy_static", "log", @@ -5372,6 +5363,7 @@ dependencies = [ [[package]] name = "servo_arc" version = "0.2.0" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "nodrop", "serde", @@ -5381,6 +5373,7 @@ dependencies = [ [[package]] name = "servo_atoms" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "string_cache", "string_cache_codegen", @@ -5413,7 +5406,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5485,6 +5478,7 @@ dependencies = [ "log", "raw-window-handle", "servo-media", + "servo_allocator", "shellwords", "sig", "surfman", @@ -5566,7 +5560,7 @@ dependencies = [ "ipc-channel", "jni", "libc", - "libloading 0.8.1", + "libloading 0.8.3", "libservo", "log", "serde_json", @@ -5585,6 +5579,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "size_of_test" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "static_assertions", ] @@ -5653,12 +5648,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -5710,6 +5705,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "static_prefs" version = "0.1.0" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" [[package]] name = "str-buf" @@ -5732,7 +5728,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", - "phf_shared", + "phf_shared 0.10.0", "precomputed-hash", "serde", ] @@ -5743,8 +5739,8 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro2", "quote", ] @@ -5752,11 +5748,11 @@ dependencies = [ [[package]] name = "style" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "app_units", "arrayvec", "atomic_refcell", - "bindgen", "bitflags 1.3.2", "byteorder", "cssparser", @@ -5772,8 +5768,8 @@ dependencies = [ "log", "malloc_size_of", "malloc_size_of_derive", + "matches", "mime", - "mozbuild", "new_debug_unreachable", "num-derive", "num-integer", @@ -5783,7 +5779,6 @@ dependencies = [ "parking_lot", "precomputed-hash", "rayon", - "regex", "selectors", "serde", "servo_arc", @@ -5800,7 +5795,6 @@ dependencies = [ "time 0.1.45", "to_shmem", "to_shmem_derive", - "toml 0.5.11", "uluru", "unicode-bidi", "unicode-segmentation", @@ -5812,6 +5806,7 @@ dependencies = [ [[package]] name = "style_config" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "lazy_static", ] @@ -5819,12 +5814,13 @@ dependencies = [ [[package]] name = "style_derive" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "darling", "derive_common", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "synstructure 0.13.1", ] @@ -5849,6 +5845,7 @@ dependencies = [ [[package]] name = "style_traits" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "app_units", "bitflags 1.3.2", @@ -5870,9 +5867,9 @@ dependencies = [ [[package]] name = "surfman" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29362235cba051e9e6eb5a136b32c1ab6933d2545e4fffd0ba22ac904498d0e2" +checksum = "2a10a79c436583753fa108971470dd42f2111ce068f1b6fdebd981a96f920bdf" dependencies = [ "bitflags 1.3.2", "cfg_aliases", @@ -5887,7 +5884,7 @@ dependencies = [ "lazy_static", "libc", "log", - "mach", + "mach2", "metal 0.24.0", "objc", "raw-window-handle", @@ -5901,9 +5898,9 @@ dependencies = [ [[package]] name = "svg_fmt" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" +checksum = "f83ba502a3265efb76efb89b0a2f7782ad6f2675015d4ce37e4b547dda42b499" [[package]] name = "sw-composite" @@ -5930,9 +5927,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -5959,7 +5956,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -5971,7 +5968,7 @@ dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 0.8.2", + "toml 0.8.9", "version-compare", ] @@ -5996,9 +5993,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", @@ -6074,7 +6071,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -6191,6 +6188,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "to_shmem" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "cssparser", "servo_arc", @@ -6203,12 +6201,13 @@ dependencies = [ [[package]] name = "to_shmem_derive" version = "0.0.1" +source = "git+https://github.com/servo/stylo.git?branch=2023-07-23#3f129fb95a345f0057a6b72901eb7eb795840a76" dependencies = [ "darling", "derive_common", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "synstructure 0.13.1", ] @@ -6237,7 +6236,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] @@ -6299,9 +6298,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "c6a4b9e8023eb94392d3dca65d717c53abc5dad49c07cb65bb8fcd87115fa325" dependencies = [ "serde", "serde_spanned", @@ -6311,20 +6310,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.20.2" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.3", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", @@ -6583,9 +6582,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -6643,9 +6642,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -6653,24 +6652,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6678,22 +6677,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wayland-client" @@ -6782,9 +6781,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -7024,7 +7023,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.1", + "libloading 0.7.4", "log", "metal 0.27.0", "naga", @@ -7495,7 +7494,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.50", + "syn 2.0.52", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 00b606a941d..17e2aefb1d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,14 +39,14 @@ fxhash = "0.2" getopts = "0.2.11" gfx_traits = { path = "components/shared/gfx" } gleam = "0.15" -glib = "0.18" -gstreamer = { version = "0.21", features = ["v1_18"] } -gstreamer-base = "0.21" -gstreamer-gl = "0.21" -gstreamer-gl-sys = { version = "0.21" } -gstreamer-gl-wayland = { version = "0.21" } -gstreamer-sys = "0.21" -gstreamer-video = "0.21" +glib = "0.19" +gstreamer = { version = "0.22", features = ["v1_18"] } +gstreamer-base = "0.22" +gstreamer-gl = "0.22" +gstreamer-gl-sys = { version = "0.22" } +gstreamer-gl-wayland = { version = "0.22" } +gstreamer-sys = "0.22" +gstreamer-video = "0.22" headers = "0.3" html5ever = "0.26" http = "0.2" @@ -55,16 +55,16 @@ hyper-rustls = { version = "0.24", default-features = false, features = ["accept hyper_serde = { path = "components/hyper_serde" } image = "0.24" imsz = "0.2" -indexmap = { version = "2.2.3", features = ["std"] } +indexmap = { version = "2.2.5", features = ["std"] } ipc-channel = "0.18" itertools = "0.12" jemallocator = "0.5.4" jemalloc-sys = "0.5.4" keyboard-types = "0.6" -layout_traits = { path = "components/shared/layout" } lazy_static = "1.4" libc = "0.2" log = "0.4" +malloc_size_of = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23", features = ["servo"] } malloc_size_of_derive = "0.1" mime = "0.3.13" mime_guess = "2.0.3" @@ -87,22 +87,31 @@ rustls = { version = "0.21.10", features = ["dangerous_configuration"] } rustls-pemfile = "1.0.4" script_layout_interface = { path = "components/shared/script_layout" } script_traits = { path = "components/shared/script" } +selectors = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } serde = "1.0.197" serde_bytes = "0.11" serde_json = "1.0" +servo-media = { git = "https://github.com/servo/media" } +servo-media-dummy = { git = "https://github.com/servo/media" } +servo-media-gstreamer = { git = "https://github.com/servo/media" } +servo_arc = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } +servo_atoms = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } +size_of_test = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } smallbitvec = "2.3.0" smallvec = "1.13" sparkle = "0.1.26" string_cache = "0.8" string_cache_codegen = "0.5" -style_config = { path = "components/style_config" } -style_traits = { path = "components/style_traits", features = ["servo"] } +style = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23", features = ["servo"] } +style_config = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } +style_traits = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23", features = ["servo"] } # NOTE: the sm-angle feature only enables ANGLE on Windows, not other platforms! surfman = { version = "0.9", features = ["chains", "sm-angle", "sm-angle-default"] } syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] } synstructure = "0.13" thin-vec = "0.2.13" time = "0.1.41" +to_shmem = { git = "https://github.com/servo/stylo.git", branch = "2023-07-23" } tokio = "1" tokio-rustls = "0.24" tungstenite = "0.20" @@ -140,7 +149,23 @@ debug-assertions = false # # = { path = "/path/to/local/checkout" } # -# Or for a git dependency: +# Or for Stylo: +# +# [patch."https://github.com/servo/stylo.git"] +# derive_common = { path = "../stylo/derive_common" } +# malloc_size_of = { path = "../stylo/malloc_size_of" } +# selectors = { path = "../stylo/selectors" } +# servo_arc = { path = "../stylo/servo_arc" } +# servo_atoms = { path = "../stylo/atoms" } +# size_of_test = { path = "../stylo/size_of_test" } +# static_prefs = { path = "../stylo/style_static_prefs" } +# style_config = { path = "../stylo/style_config" } +# style_derive = { path = "../stylo/style_derive" } +# style = { path = "../stylo/style" } +# style_traits = { path = "../stylo/style_traits" } +# to_shmem = { path = "../stylo/to_shmem" } +# +# Or for another git dependency: # # [patch."https://github.com/servo/"] # = { path = "/path/to/local/checkout" } diff --git a/README.md b/README.md index acdaa386bb4..cdd3f52a5e9 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,13 @@ for help getting started. Visit the [Servo Project page](https://servo.org/) for news and guides. +## Getting Servo +``` sh +git clone https://github.com/servo/servo +cd servo +``` +Your CARGO_HOME needs to point to (or be in) the same drive as your Servo repository (See [#28530](https://github.com/servo/servo/issues/28530)). + ## Build Setup * [macOS](#macos) @@ -82,13 +89,6 @@ See also [Windows Troubleshooting Tips][windows-tips]. For information about building and running the Android build, see the [Android documentation][android-docs]. -### Cloning the Repo -Your CARGO_HOME needs to point to (or be in) the same drive as your Servo repository (See [#28530](https://github.com/servo/servo/issues/28530)). -``` sh -git clone https://github.com/servo/servo -cd servo -``` - ## Building Servo is built with [Cargo](https://crates.io/), the Rust package manager. @@ -193,7 +193,6 @@ Run Servo with the command: * `libXi` * `libxkbcommon` * `vulkan-loader` -* `libegl1-mesa-dev` ## Developing diff --git a/components/allocator/Cargo.toml b/components/allocator/Cargo.toml index 7a017e4cf92..e6467da3737 100644 --- a/components/allocator/Cargo.toml +++ b/components/allocator/Cargo.toml @@ -9,9 +9,13 @@ publish = false [lib] path = "lib.rs" +[features] +use-system-allocator = ["libc"] + [target.'cfg(not(any(windows, target_os = "android")))'.dependencies] jemallocator = { workspace = true } jemalloc-sys = { workspace = true } +libc = { workspace = true, optional = true } [target.'cfg(windows)'.dependencies] winapi = { workspace = true, features = ["heapapi"] } diff --git a/components/allocator/lib.rs b/components/allocator/lib.rs index 9d7c0b466f0..f6cf47eada5 100644 --- a/components/allocator/lib.rs +++ b/components/allocator/lib.rs @@ -9,7 +9,7 @@ static ALLOC: Allocator = Allocator; pub use crate::platform::*; -#[cfg(not(any(windows, target_os = "android")))] +#[cfg(not(any(windows, target_os = "android", feature = "use-system-allocator")))] mod platform { use std::os::raw::c_void; @@ -28,14 +28,21 @@ mod platform { } } -#[cfg(target_os = "android")] +#[cfg(all( + not(windows), + any(target_os = "android", feature = "use-system-allocator") +))] mod platform { pub use std::alloc::System as Allocator; use std::os::raw::c_void; /// Get the size of a heap block. pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize { - libc::malloc_usable_size(ptr) + #[cfg(target_os = "linux")] + return libc::malloc_usable_size(ptr as *mut _); + + #[cfg(not(target_os = "linux"))] + return libc::malloc_usable_size(ptr); } pub mod libc_compat { diff --git a/components/atoms/Cargo.toml b/components/atoms/Cargo.toml deleted file mode 100644 index d48dbf0a350..00000000000 --- a/components/atoms/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "servo_atoms" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -edition = "2018" -publish = false -build = "build.rs" - -[lib] -path = "lib.rs" - -[dependencies] -string_cache = { workspace = true } - -[build-dependencies] -string_cache_codegen = { workspace = true } diff --git a/components/atoms/build.rs b/components/atoms/build.rs deleted file mode 100644 index 6bd2de37034..00000000000 --- a/components/atoms/build.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::env; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::path::Path; - -fn main() { - let static_atoms = - Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt"); - let static_atoms = BufReader::new(File::open(&static_atoms).unwrap()); - let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!"); - - macro_rules! predefined { - ($($name: expr,)+) => { - { - $( - atom_type.atom($name); - )+ - } - } - } - include!("../style/counter_style/predefined.rs"); - - atom_type - .atoms(static_atoms.lines().map(Result::unwrap)) - .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs")) - .unwrap(); -} diff --git a/components/atoms/lib.rs b/components/atoms/lib.rs deleted file mode 100644 index 03560a40c0b..00000000000 --- a/components/atoms/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -include!(concat!(env!("OUT_DIR"), "/atom.rs")); diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt deleted file mode 100644 index 5d8f7754270..00000000000 --- a/components/atoms/static_atoms.txt +++ /dev/null @@ -1,174 +0,0 @@ --moz-content-preferred-color-scheme --moz-device-pixel-ratio --moz-gtk-csd-close-button-position --moz-gtk-csd-maximize-button-position --moz-gtk-csd-menu-radius --moz-gtk-csd-minimize-button-position --moz-gtk-csd-titlebar-radius --moz-gtk-menu-radius -DOMContentLoaded -abort -activate -addtrack -animationcancel -animationend -animationiteration -animationstart -aspect-ratio -beforeunload -block-size -button -canplay -canplaythrough -center -change -characteristicvaluechanged -checkbox -click -close -closing -color -complete -compositionend -compositionstart -compositionupdate -controllerchange -cursive -dark -datachannel -date -datetime-local -dir -device-pixel-ratio -durationchange -email -emptied -end -ended -error -fantasy -fetch -file -fill -fill-opacity -formdata -fullscreenchange -fullscreenerror -gattserverdisconnected -hashchange -height -hidden -icecandidate -iceconnectionstatechange -icegatheringstatechange -image -inline-size -input -inputsourceschange -invalid -keydown -keypress -kind -left -light -ltr -load -loadeddata -loadedmetadata -loadend -loadstart -message -message -messageerror -monospace -month -mousedown -mousemove -mouseover -mouseup -negotiationneeded -none -normal -number -onchange -open -orientation -pagehide -pageshow -password -pause -play -playing -popstate -postershown -print -progress -radio -range -ratechange -readystatechange -referrer -reftest-wait -rejectionhandled -removetrack -reset -resize -resolution -resourcetimingbufferfull -right -rtl -sans-serif -safe-area-inset-top -safe-area-inset-bottom -safe-area-inset-left -safe-area-inset-right -scan -screen -scroll-position -scrollbar-inline-size -search -seeked -seeking -select -selectend -selectionchange -selectstart -serif -sessionavailable -signalingstatechange -squeeze -squeezeend -squeezestart -srclang -statechange -stroke -stroke-opacity -storage -submit -suspend -system-ui -tel -text -time -timeupdate -toggle -track -transitioncancel -transitionend -transitionrun -transitionstart -uncapturederror -unhandledrejection -unload -url -visibilitychange -volumechange -waiting -webglcontextcreationerror -webkitAnimationEnd -webkitAnimationIteration -webkitAnimationStart -webkitTransitionEnd -webkitTransitionRun -week -width diff --git a/components/bluetooth/adapter.rs b/components/bluetooth/adapter.rs index e252e2434a6..44a763c9442 100644 --- a/components/bluetooth/adapter.rs +++ b/components/bluetooth/adapter.rs @@ -197,6 +197,7 @@ impl BluetoothAdapter { pub fn create_discovery_session(&self) -> Result> { let discovery_session = match self { #[cfg(all(target_os = "linux", feature = "native-bluetooth"))] + #[allow(clippy::arc_with_non_send_sync)] // Problem with underlying library BluetoothAdapter::Bluez(inner) => BluetoothDiscoverySession::Bluez(Arc::new( BluetoothDiscoverySessionBluez::create_session(inner.get_id())?, )), @@ -292,7 +293,10 @@ impl BluetoothAdapter { pub fn set_id(&self, id: String) -> Result<(), Box> { match self { #[cfg(feature = "bluetooth-test")] - BluetoothAdapter::Mock(inner) => Ok(inner.set_id(id)), + BluetoothAdapter::Mock(inner) => { + inner.set_id(id); + Ok(()) + }, _ => Err(Box::from( "Error! Test functions are not supported on real devices!", )), diff --git a/components/bluetooth/bluetooth.rs b/components/bluetooth/bluetooth.rs index f15ae09d0ef..f3c0a7270b1 100644 --- a/components/bluetooth/bluetooth.rs +++ b/components/bluetooth/bluetooth.rs @@ -83,8 +83,7 @@ use super::macros::get_inner_and_call; use super::macros::get_inner_and_call_test_func; #[cfg(feature = "bluetooth-test")] -const NOT_SUPPORTED_ON_MOCK_ERROR: &'static str = - "Error! The first parameter must be a mock structure!"; +const NOT_SUPPORTED_ON_MOCK_ERROR: &str = "Error! The first parameter must be a mock structure!"; #[derive(Debug)] pub enum BluetoothDiscoverySession { @@ -193,9 +192,8 @@ impl BluetoothDevice { #[cfg(feature = "bluetooth-test")] pub fn set_id(&self, id: String) { - match self { - &BluetoothDevice::Mock(ref fake_adapter) => fake_adapter.set_id(id), - _ => (), + if let BluetoothDevice::Mock(fake_adapter) = self { + fake_adapter.set_id(id) } } @@ -473,9 +471,8 @@ impl BluetoothGATTService { #[cfg(feature = "bluetooth-test")] pub fn set_id(&self, id: String) { - match self { - &BluetoothGATTService::Mock(ref fake_service) => fake_service.set_id(id), - _ => (), + if let BluetoothGATTService::Mock(fake_service) = self { + fake_service.set_id(id) } } @@ -578,11 +575,8 @@ impl BluetoothGATTCharacteristic { #[cfg(feature = "bluetooth-test")] pub fn set_id(&self, id: String) { - match self { - &BluetoothGATTCharacteristic::Mock(ref fake_characteristic) => { - fake_characteristic.set_id(id) - }, - _ => (), + if let BluetoothGATTCharacteristic::Mock(fake_characteristic) = self { + fake_characteristic.set_id(id) } } @@ -710,9 +704,8 @@ impl BluetoothGATTDescriptor { #[cfg(feature = "bluetooth-test")] pub fn set_id(&self, id: String) { - match self { - &BluetoothGATTDescriptor::Mock(ref fake_descriptor) => fake_descriptor.set_id(id), - _ => (), + if let BluetoothGATTDescriptor::Mock(fake_descriptor) = self { + fake_descriptor.set_id(id) } } diff --git a/components/bluetooth/lib.rs b/components/bluetooth/lib.rs index ae1fd0bb631..b7bf79ed561 100644 --- a/components/bluetooth/lib.rs +++ b/components/bluetooth/lib.rs @@ -120,7 +120,7 @@ fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> boo if !filter.get_services().is_empty() { if let Ok(device_uuids) = device.get_uuids() { for service in filter.get_services() { - if device_uuids.iter().find(|x| x == &service).is_none() { + if !device_uuids.iter().any(|x| x == service) { return false; } } @@ -128,12 +128,12 @@ fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> boo } // Step 4. - if let Some(ref manufacturer_data) = filter.get_manufacturer_data() { + if let Some(manufacturer_data) = filter.get_manufacturer_data() { let advertised_manufacturer_data = match device.get_manufacturer_data() { Ok(data) => data, Err(_) => return false, }; - for (ref id, &(ref prefix, ref mask)) in manufacturer_data.iter() { + for (id, (prefix, mask)) in manufacturer_data.iter() { if let Some(advertised_data) = advertised_manufacturer_data.get(id) { if !data_filter_matches(advertised_data, prefix, mask) { return false; @@ -145,12 +145,12 @@ fn matches_filter(device: &BluetoothDevice, filter: &BluetoothScanfilter) -> boo } // Step 5. - if let Some(ref service_data) = filter.get_service_data() { + if let Some(service_data) = filter.get_service_data() { let advertised_service_data = match device.get_service_data() { Ok(data) => data, Err(_) => return false, }; - for (uuid, &(ref prefix, ref mask)) in service_data.iter() { + for (uuid, (prefix, mask)) in service_data.iter() { if let Some(advertised_data) = advertised_service_data.get(uuid.as_str()) { if !data_filter_matches(advertised_data, prefix, mask) { return false; @@ -193,10 +193,7 @@ fn matches_filters(device: &BluetoothDevice, filters: &BluetoothScanfilterSequen } fn is_mock_adapter(adapter: &BluetoothAdapter) -> bool { - match adapter { - &BluetoothAdapter::Mock(_) => true, - _ => false, - } + matches!(adapter, &BluetoothAdapter::Mock(_)) } pub struct BluetoothManager { @@ -299,7 +296,7 @@ impl BluetoothManager { self.allowed_services.clear(); self.adapter = BluetoothAdapter::new_mock().ok(); match test::test(self, data_set_name) { - Ok(_) => return Ok(()), + Ok(_) => Ok(()), Err(error) => Err(BluetoothError::Type(error.to_string())), } } @@ -352,18 +349,19 @@ impl BluetoothManager { if !adapter.is_powered().unwrap_or(false) { return Err(BluetoothError::NotFound); } - return Ok(adapter); + Ok(adapter) }, - None => return Err(BluetoothError::NotFound), + None => Err(BluetoothError::NotFound), } } // Device fn get_and_cache_devices(&mut self, adapter: &mut BluetoothAdapter) -> Vec { - let devices = adapter.get_devices().unwrap_or(vec![]); + let devices = adapter.get_devices().unwrap_or_default(); for device in &devices { if let Ok(address) = device.get_address() { + #[allow(clippy::map_entry)] // False positive, the fix creates a borrowing error if !self.address_to_id.contains_key(&address) { let generated_id = self.generate_device_id(); self.address_to_id.insert(address, generated_id.clone()); @@ -373,7 +371,7 @@ impl BluetoothManager { } } } - self.cached_devices.iter().map(|(_, d)| d.clone()).collect() + self.cached_devices.values().cloned().collect() } fn get_device( @@ -454,7 +452,7 @@ impl BluetoothManager { ) -> BluetoothResult { let mut adapter = self.get_adapter()?; match self.get_device(&mut adapter, device_id) { - Some(ref device) => Ok(matches_filters(device, filters)), + Some(device) => Ok(matches_filters(device, filters)), None => Ok(false), } } @@ -467,14 +465,14 @@ impl BluetoothManager { device_id: &str, ) -> Vec { let mut services = match self.get_device(adapter, device_id) { - Some(d) => d.get_gatt_services().unwrap_or(vec![]), + Some(d) => d.get_gatt_services().unwrap_or_default(), None => vec![], }; services.retain(|s| { - !uuid_is_blocklisted(&s.get_uuid().unwrap_or(String::new()), Blocklist::All) && + !uuid_is_blocklisted(&s.get_uuid().unwrap_or_default(), Blocklist::All) && self.allowed_services.get(device_id).map_or(false, |uuids| { - uuids.contains(&s.get_uuid().unwrap_or(String::new())) + uuids.contains(&s.get_uuid().unwrap_or_default()) }) }); for service in &services { @@ -511,13 +509,12 @@ impl BluetoothManager { service_id: &str, ) -> Vec { let mut characteristics = match self.get_gatt_service(adapter, service_id) { - Some(s) => s.get_gatt_characteristics().unwrap_or(vec![]), + Some(s) => s.get_gatt_characteristics().unwrap_or_default(), None => vec![], }; - characteristics.retain(|c| { - !uuid_is_blocklisted(&c.get_uuid().unwrap_or(String::new()), Blocklist::All) - }); + characteristics + .retain(|c| !uuid_is_blocklisted(&c.get_uuid().unwrap_or_default(), Blocklist::All)); for characteristic in &characteristics { self.cached_characteristics .insert(characteristic.get_id(), characteristic.clone()); @@ -544,7 +541,7 @@ impl BluetoothManager { fn get_characteristic_properties(&self, characteristic: &BluetoothGATTCharacteristic) -> Flags { let mut props: Flags = Flags::empty(); - let flags = characteristic.get_flags().unwrap_or(vec![]); + let flags = characteristic.get_flags().unwrap_or_default(); for flag in flags { match flag.as_ref() { "broadcast" => props.insert(Flags::BROADCAST), @@ -576,13 +573,12 @@ impl BluetoothManager { characteristic_id: &str, ) -> Vec { let mut descriptors = match self.get_gatt_characteristic(adapter, characteristic_id) { - Some(c) => c.get_gatt_descriptors().unwrap_or(vec![]), + Some(c) => c.get_gatt_descriptors().unwrap_or_default(), None => vec![], }; - descriptors.retain(|d| { - !uuid_is_blocklisted(&d.get_uuid().unwrap_or(String::new()), Blocklist::All) - }); + descriptors + .retain(|d| !uuid_is_blocklisted(&d.get_uuid().unwrap_or_default(), Blocklist::All)); for descriptor in &descriptors { self.cached_descriptors .insert(descriptor.get_id(), descriptor.clone()); @@ -617,11 +613,10 @@ impl BluetoothManager { // Step 7. // Note: There are no requiredServiceUUIDS, we scan for all devices. if let Ok(ref session) = adapter.create_discovery_session() { - if session.start_discovery().is_ok() { - if !is_mock_adapter(&adapter) { - thread::sleep(Duration::from_millis(DISCOVERY_TIMEOUT_MS)); - } + if session.start_discovery().is_ok() && !is_mock_adapter(&adapter) { + thread::sleep(Duration::from_millis(DISCOVERY_TIMEOUT_MS)); } + let _ = session.stop_discovery(); } @@ -629,10 +624,7 @@ impl BluetoothManager { // Step 8. if !options.is_accepting_all_devices() { - matched_devices = matched_devices - .into_iter() - .filter(|d| matches_filters(d, options.get_filters())) - .collect(); + matched_devices.retain(|d| matches_filters(d, options.get_filters())); } // Step 9. @@ -655,7 +647,7 @@ impl BluetoothManager { } } // Step 10. - return Err(BluetoothError::NotFound); + Err(BluetoothError::NotFound) // Step 12: Missing, because it is optional. } @@ -686,9 +678,9 @@ impl BluetoothManager { // TODO: Step 5.1.4: Use the exchange MTU procedure. } // Step 5.1.3. - return Err(BluetoothError::Network); + Err(BluetoothError::Network) }, - None => return Err(BluetoothError::NotFound), + None => Err(BluetoothError::NotFound), } } @@ -709,9 +701,9 @@ impl BluetoothManager { return Ok(()); } } - return Err(BluetoothError::Network); + Err(BluetoothError::Network) }, - None => return Err(BluetoothError::NotFound), + None => Err(BluetoothError::NotFound), } } @@ -742,14 +734,14 @@ impl BluetoothManager { } let mut services = self.get_and_cache_gatt_services(&mut adapter, &id); if let Some(uuid) = uuid { - services.retain(|ref e| e.get_uuid().unwrap_or(String::new()) == uuid); + services.retain(|e| e.get_uuid().unwrap_or_default() == uuid); } let mut services_vec = vec![]; for service in services { if service.is_primary().unwrap_or(false) { if let Ok(uuid) = service.get_uuid() { services_vec.push(BluetoothServiceMsg { - uuid: uuid, + uuid, is_primary: true, instance_id: service.get_id(), }); @@ -761,7 +753,7 @@ impl BluetoothManager { return Err(BluetoothError::NotFound); } - return Ok(BluetoothResponse::GetPrimaryServices(services_vec, single)); + Ok(BluetoothResponse::GetPrimaryServices(services_vec, single)) }, GATTType::Characteristic => { // Step 5. @@ -772,14 +764,14 @@ impl BluetoothManager { let mut characteristics = self.get_and_cache_gatt_characteristics(&mut adapter, &id); if let Some(uuid) = uuid { - characteristics.retain(|ref e| e.get_uuid().unwrap_or(String::new()) == uuid); + characteristics.retain(|e| e.get_uuid().unwrap_or_default() == uuid); } let mut characteristics_vec = vec![]; for characteristic in characteristics { if let Ok(uuid) = characteristic.get_uuid() { let properties = self.get_characteristic_properties(&characteristic); characteristics_vec.push(BluetoothCharacteristicMsg { - uuid: uuid, + uuid, instance_id: characteristic.get_id(), broadcast: properties.contains(Flags::BROADCAST), read: properties.contains(Flags::READ), @@ -801,10 +793,10 @@ impl BluetoothManager { return Err(BluetoothError::NotFound); } - return Ok(BluetoothResponse::GetCharacteristics( + Ok(BluetoothResponse::GetCharacteristics( characteristics_vec, single, - )); + )) }, GATTType::IncludedService => { // Step 5. @@ -832,7 +824,7 @@ impl BluetoothManager { } } if let Some(uuid) = uuid { - services_vec.retain(|ref s| s.uuid == uuid); + services_vec.retain(|s| s.uuid == uuid); } services_vec.retain(|s| !uuid_is_blocklisted(&s.uuid, Blocklist::All)); @@ -841,7 +833,7 @@ impl BluetoothManager { return Err(BluetoothError::NotFound); } - return Ok(BluetoothResponse::GetIncludedServices(services_vec, single)); + Ok(BluetoothResponse::GetIncludedServices(services_vec, single)) }, GATTType::Descriptor => { // Step 5. @@ -851,13 +843,13 @@ impl BluetoothManager { // Step 6. let mut descriptors = self.get_and_cache_gatt_descriptors(&mut adapter, &id); if let Some(uuid) = uuid { - descriptors.retain(|ref e| e.get_uuid().unwrap_or(String::new()) == uuid); + descriptors.retain(|e| e.get_uuid().unwrap_or_default() == uuid); } let mut descriptors_vec = vec![]; for descriptor in descriptors { if let Ok(uuid) = descriptor.get_uuid() { descriptors_vec.push(BluetoothDescriptorMsg { - uuid: uuid, + uuid, instance_id: descriptor.get_id(), }); } @@ -867,7 +859,7 @@ impl BluetoothManager { if descriptors_vec.is_empty() { return Err(BluetoothError::NotFound); } - return Ok(BluetoothResponse::GetDescriptors(descriptors_vec, single)); + Ok(BluetoothResponse::GetDescriptors(descriptors_vec, single)) }, } } @@ -882,7 +874,7 @@ impl BluetoothManager { // (Characteristic) Step 5.3. let mut value = self .get_gatt_characteristic(&mut adapter, &id) - .map(|c| c.read_value().unwrap_or(vec![])); + .map(|c| c.read_value().unwrap_or_default()); // (Characteristic) TODO: Step 5.4: Handle all the errors returned from the read_value call. @@ -890,7 +882,7 @@ impl BluetoothManager { if value.is_none() { value = self .get_gatt_descriptor(&mut adapter, &id) - .map(|d| d.read_value().unwrap_or(vec![])); + .map(|d| d.read_value().unwrap_or_default()); } // (Descriptor) TODO: Step 5.3: Handle all the errors returned from the read_value call. @@ -898,11 +890,11 @@ impl BluetoothManager { match value { // (Characteristic) Step 5.5.4. // (Descriptor) Step 5.4.3. - Some(v) => return Ok(BluetoothResponse::ReadValue(v)), + Some(v) => Ok(BluetoothResponse::ReadValue(v)), // (Characteristic) Step 4. // (Descriptor) Step 4. - None => return Err(BluetoothError::InvalidState), + None => Err(BluetoothError::InvalidState), } } @@ -933,15 +925,15 @@ impl BluetoothManager { Some(v) => match v { // (Characteristic) Step 7.5.3. // (Descriptor) Step 7.4.3. - Ok(_) => return Ok(BluetoothResponse::WriteValue(value)), + Ok(_) => Ok(BluetoothResponse::WriteValue(value)), // (Characteristic) Step 7.1. - Err(_) => return Err(BluetoothError::NotSupported), + Err(_) => Err(BluetoothError::NotSupported), }, // (Characteristic) Step 6. // (Descriptor) Step 6. - None => return Err(BluetoothError::InvalidState), + None => Err(BluetoothError::InvalidState), } } @@ -969,14 +961,14 @@ impl BluetoothManager { match result { // (StartNotification) Step 11. // (StopNotification) Step 5. - Ok(_) => return Ok(BluetoothResponse::EnableNotification(())), + Ok(_) => Ok(BluetoothResponse::EnableNotification(())), // (StartNotification) Step 5. - Err(_) => return Err(BluetoothError::NotSupported), + Err(_) => Err(BluetoothError::NotSupported), } }, // (StartNotification) Step 4. - None => return Err(BluetoothError::InvalidState), + None => Err(BluetoothError::InvalidState), } } @@ -984,7 +976,7 @@ impl BluetoothManager { fn watch_advertisements(&mut self, _device_id: String) -> BluetoothResponseResult { // Step 2. // TODO: Implement this when supported in lower level - return Err(BluetoothError::NotSupported); + Err(BluetoothError::NotSupported) } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-getavailability diff --git a/components/bluetooth/test.rs b/components/bluetooth/test.rs index bf3b19b82cc..91f122950d9 100644 --- a/components/bluetooth/test.rs +++ b/components/bluetooth/test.rs @@ -18,110 +18,103 @@ use crate::BluetoothManager; thread_local!(pub static CACHED_IDS: RefCell> = RefCell::new(HashSet::new())); -const ADAPTER_ERROR: &'static str = "No adapter found"; -const WRONG_DATA_SET_ERROR: &'static str = "Wrong data set name was provided"; -const READ_FLAG: &'static str = "read"; -const WRITE_FLAG: &'static str = "write"; -const NOTIFY_FLAG: &'static str = "notify"; +const ADAPTER_ERROR: &str = "No adapter found"; +const WRONG_DATA_SET_ERROR: &str = "Wrong data set name was provided"; +const READ_FLAG: &str = "read"; +const WRITE_FLAG: &str = "write"; +const NOTIFY_FLAG: &str = "notify"; // Adapter names // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=65 -const NOT_PRESENT_ADAPTER: &'static str = "NotPresentAdapter"; +const NOT_PRESENT_ADAPTER: &str = "NotPresentAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=83 -const NOT_POWERED_ADAPTER: &'static str = "NotPoweredAdapter"; +const NOT_POWERED_ADAPTER: &str = "NotPoweredAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=118 -const EMPTY_ADAPTER: &'static str = "EmptyAdapter"; +const EMPTY_ADAPTER: &str = "EmptyAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=126 -const GLUCOSE_HEART_RATE_ADAPTER: &'static str = "GlucoseHeartRateAdapter"; +const GLUCOSE_HEART_RATE_ADAPTER: &str = "GlucoseHeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=135 -const UNICODE_DEVICE_ADAPTER: &'static str = "UnicodeDeviceAdapter"; +const UNICODE_DEVICE_ADAPTER: &str = "UnicodeDeviceAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=205 -const MISSING_SERVICE_HEART_RATE_ADAPTER: &'static str = "MissingServiceHeartRateAdapter"; +const MISSING_SERVICE_HEART_RATE_ADAPTER: &str = "MissingServiceHeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=219 -const MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER: &'static str = - "MissingCharacteristicHeartRateAdapter"; -const MISSING_DESCRIPTOR_HEART_RATE_ADAPTER: &'static str = "MissingDescriptorHeartRateAdapter"; +const MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER: &str = "MissingCharacteristicHeartRateAdapter"; +const MISSING_DESCRIPTOR_HEART_RATE_ADAPTER: &str = "MissingDescriptorHeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=234 -const HEART_RATE_ADAPTER: &'static str = "HeartRateAdapter"; +const HEART_RATE_ADAPTER: &str = "HeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=250 -const EMPTY_NAME_HEART_RATE_ADAPTER: &'static str = "EmptyNameHeartRateAdapter"; +const EMPTY_NAME_HEART_RATE_ADAPTER: &str = "EmptyNameHeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=267 -const NO_NAME_HEART_RATE_ADAPTER: &'static str = "NoNameHeartRateAdapter"; +const NO_NAME_HEART_RATE_ADAPTER: &str = "NoNameHeartRateAdapter"; // https://cs.chromium.org/chromium/src/content/shell/browser/layout_test/layout_test_bluetooth_adapter_provider.h?l=284 -const TWO_HEART_RATE_SERVICES_ADAPTER: &'static str = "TwoHeartRateServicesAdapter"; -const BLOCKLIST_TEST_ADAPTER: &'static str = "BlocklistTestAdapter"; +const TWO_HEART_RATE_SERVICES_ADAPTER: &str = "TwoHeartRateServicesAdapter"; +const BLOCKLIST_TEST_ADAPTER: &str = "BlocklistTestAdapter"; // Device names -const CONNECTABLE_DEVICE_NAME: &'static str = "Connectable Device"; -const EMPTY_DEVICE_NAME: &'static str = ""; +const CONNECTABLE_DEVICE_NAME: &str = "Connectable Device"; +const EMPTY_DEVICE_NAME: &str = ""; // https://webbluetoothcg.github.io/web-bluetooth/tests.html#glucosedevice -const GLUCOSE_DEVICE_NAME: &'static str = "Glucose Device"; +const GLUCOSE_DEVICE_NAME: &str = "Glucose Device"; // https://webbluetoothcg.github.io/web-bluetooth/tests.html#heartratedevice -const HEART_RATE_DEVICE_NAME: &'static str = "Heart Rate Device"; -const UNICODE_DEVICE_NAME: &'static str = "❤❤❤❤❤❤❤❤❤"; +const HEART_RATE_DEVICE_NAME: &str = "Heart Rate Device"; +const UNICODE_DEVICE_NAME: &str = "❤❤❤❤❤❤❤❤❤"; // Device addresses -const CONNECTABLE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:04"; +const CONNECTABLE_DEVICE_ADDRESS: &str = "00:00:00:00:00:04"; // https://webbluetoothcg.github.io/web-bluetooth/tests.html#glucosedevice -const GLUCOSE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:02"; +const GLUCOSE_DEVICE_ADDRESS: &str = "00:00:00:00:00:02"; // https://webbluetoothcg.github.io/web-bluetooth/tests.html#heartratedevice -const HEART_RATE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:03"; -const UNICODE_DEVICE_ADDRESS: &'static str = "00:00:00:00:00:01"; +const HEART_RATE_DEVICE_ADDRESS: &str = "00:00:00:00:00:03"; +const UNICODE_DEVICE_ADDRESS: &str = "00:00:00:00:00:01"; // Service UUIDs -const BLOCKLIST_TEST_SERVICE_UUID: &'static str = "611c954a-263b-4f4a-aab6-01ddb953f985"; +const BLOCKLIST_TEST_SERVICE_UUID: &str = "611c954a-263b-4f4a-aab6-01ddb953f985"; // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.device_information.xml -const DEVICE_INFORMATION_UUID: &'static str = "0000180a-0000-1000-8000-00805f9b34fb"; +const DEVICE_INFORMATION_UUID: &str = "0000180a-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.generic_access.xml -const GENERIC_ACCESS_SERVICE_UUID: &'static str = "00001800-0000-1000-8000-00805f9b34fb"; +const GENERIC_ACCESS_SERVICE_UUID: &str = "00001800-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.glucose.xml -const GLUCOSE_SERVICE_UUID: &'static str = "00001808-0000-1000-8000-00805f9b34fb"; +const GLUCOSE_SERVICE_UUID: &str = "00001808-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml -const HEART_RATE_SERVICE_UUID: &'static str = "0000180d-0000-1000-8000-00805f9b34fb"; +const HEART_RATE_SERVICE_UUID: &str = "0000180d-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.service.human_interface_device.xml -const HUMAN_INTERFACE_DEVICE_SERVICE_UUID: &'static str = "00001812-0000-1000-8000-00805f9b34fb"; +const HUMAN_INTERFACE_DEVICE_SERVICE_UUID: &str = "00001812-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.tx_power.xml -const TX_POWER_SERVICE_UUID: &'static str = "00001804-0000-1000-8000-00805f9b34fb"; +const TX_POWER_SERVICE_UUID: &str = "00001804-0000-1000-8000-00805f9b34fb"; // Characteristic UUIDs -const BLOCKLIST_EXCLUDE_READS_CHARACTERISTIC_UUID: &'static str = - "bad1c9a2-9a5b-4015-8b60-1579bbbf2135"; +const BLOCKLIST_EXCLUDE_READS_CHARACTERISTIC_UUID: &str = "bad1c9a2-9a5b-4015-8b60-1579bbbf2135"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml -const BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID: &'static str = - "00002a38-0000-1000-8000-00805f9b34fb"; +const BODY_SENSOR_LOCATION_CHARACTERISTIC_UUID: &str = "00002a38-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.characteristic.gap.device_name.xml -const DEVICE_NAME_CHARACTERISTIC_UUID: &'static str = "00002a00-0000-1000-8000-00805f9b34fb"; +const DEVICE_NAME_CHARACTERISTIC_UUID: &str = "00002a00-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml -const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID: &'static str = - "00002a37-0000-1000-8000-00805f9b34fb"; +const HEART_RATE_MEASUREMENT_CHARACTERISTIC_UUID: &str = "00002a37-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.characteristic.gap.peripheral_privacy_flag.xml -const PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC_UUID: &'static str = - "00002a02-0000-1000-8000-00805f9b34fb"; +const PERIPHERAL_PRIVACY_FLAG_CHARACTERISTIC_UUID: &str = "00002a02-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.characteristic.serial_number_string.xml -const SERIAL_NUMBER_STRING_UUID: &'static str = "00002a25-0000-1000-8000-00805f9b34fb"; +const SERIAL_NUMBER_STRING_UUID: &str = "00002a25-0000-1000-8000-00805f9b34fb"; // Descriptor UUIDs -const BLOCKLIST_EXCLUDE_READS_DESCRIPTOR_UUID: &'static str = - "aaaaaaaa-aaaa-1181-0510-810819516110"; -const BLOCKLIST_DESCRIPTOR_UUID: &'static str = "07711111-6104-0970-7011-1107105110aa"; +const BLOCKLIST_EXCLUDE_READS_DESCRIPTOR_UUID: &str = "aaaaaaaa-aaaa-1181-0510-810819516110"; +const BLOCKLIST_DESCRIPTOR_UUID: &str = "07711111-6104-0970-7011-1107105110aa"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_user_description.xml -const CHARACTERISTIC_USER_DESCRIPTION_UUID: &'static str = "00002901-0000-1000-8000-00805f9b34fb"; +const CHARACTERISTIC_USER_DESCRIPTION_UUID: &str = "00002901-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml -const CLIENT_CHARACTERISTIC_CONFIGURATION_UUID: &'static str = - "00002902-0000-1000-8000-00805f9b34fb"; +const CLIENT_CHARACTERISTIC_CONFIGURATION_UUID: &str = "00002902-0000-1000-8000-00805f9b34fb"; // https://www.bluetooth.com/specifications/gatt/ // viewer?attributeXmlFile=org.bluetooth.descriptor.number_of_digitals.xml -const NUMBER_OF_DIGITALS_UUID: &'static str = "00002909-0000-1000-8000-00805f9b34fb"; +const NUMBER_OF_DIGITALS_UUID: &str = "00002909-0000-1000-8000-00805f9b34fb"; -const HEART_RATE_DEVICE_NAME_DESCRIPTION: &'static str = "The name of this device."; +const HEART_RATE_DEVICE_NAME_DESCRIPTION: &str = "The name of this device."; fn generate_id() -> Uuid { let mut id = Uuid::nil(); @@ -130,7 +123,7 @@ fn generate_id() -> Uuid { id = Uuid::new_v4(); CACHED_IDS.with(|cache| { if !cache.borrow().contains(&id) { - cache.borrow_mut().insert(id.clone()); + cache.borrow_mut().insert(id); generated = true; } }); @@ -539,7 +532,7 @@ pub fn test(manager: &mut BluetoothManager, data_set_name: String) -> Result<(), }, GLUCOSE_HEART_RATE_ADAPTER => { set_adapter(adapter, GLUCOSE_HEART_RATE_ADAPTER.to_owned())?; - let _ = create_glucose_heart_rate_devices(adapter)?; + create_glucose_heart_rate_devices(adapter)?; }, UNICODE_DEVICE_ADAPTER => { set_adapter(adapter, UNICODE_DEVICE_ADAPTER.to_owned())?; @@ -561,12 +554,12 @@ pub fn test(manager: &mut BluetoothManager, data_set_name: String) -> Result<(), MISSING_CHARACTERISTIC_HEART_RATE_ADAPTER.to_owned(), )?; - let _ = create_missing_characterisitc_heart_rate_device(adapter)?; + create_missing_characterisitc_heart_rate_device(adapter)?; }, MISSING_DESCRIPTOR_HEART_RATE_ADAPTER => { set_adapter(adapter, MISSING_DESCRIPTOR_HEART_RATE_ADAPTER.to_owned())?; - let _ = create_missing_descriptor_heart_rate_device(adapter)?; + create_missing_descriptor_heart_rate_device(adapter)?; }, HEART_RATE_ADAPTER => { set_adapter(adapter, HEART_RATE_ADAPTER.to_owned())?; @@ -588,14 +581,14 @@ pub fn test(manager: &mut BluetoothManager, data_set_name: String) -> Result<(), TWO_HEART_RATE_SERVICES_ADAPTER => { set_adapter(adapter, TWO_HEART_RATE_SERVICES_ADAPTER.to_owned())?; - let _ = create_two_heart_rate_services_device(adapter)?; + create_two_heart_rate_services_device(adapter)?; }, BLOCKLIST_TEST_ADAPTER => { set_adapter(adapter, BLOCKLIST_TEST_ADAPTER.to_owned())?; - let _ = create_blocklisted_device(adapter)?; + create_blocklisted_device(adapter)?; }, _ => return Err(Box::from(WRONG_DATA_SET_ERROR.to_string())), } - return Ok(()); + Ok(()) } diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 692d2efdfce..ce420ae0c01 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -33,9 +33,9 @@ num-traits = { workspace = true } pathfinder_geometry = "0.5" pixels = { path = "../pixels" } raqote = "0.8.2" -servo_arc = { path = "../servo_arc" } +servo_arc = { workspace = true } sparkle = { workspace = true } -style = { path = "../style" } +style = { workspace = true } style_traits = { workspace = true } surfman = { workspace = true } time = { workspace = true, optional = true } diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 10c651bcb81..e1c0f0c7042 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -36,7 +36,7 @@ pixels = { path = "../pixels" } profile_traits = { workspace = true } script_traits = { workspace = true } servo_config = { path = "../config" } -servo-media = { git = "https://github.com/servo/media" } +servo-media = { workspace = true } servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } style_traits = { workspace = true } diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index ad9adf81d87..333511facd4 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -9,13 +9,12 @@ use std::fs::{create_dir_all, File}; use std::io::Write; use std::num::NonZeroU32; use std::rc::Rc; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use canvas::canvas_paint_thread::ImageUpdate; use compositing_traits::{ - CanvasToCompositorMsg, CompositingReason, CompositionPipeline, CompositorMsg, - CompositorReceiver, ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, - SendableFrameTree, + CanvasToCompositorMsg, CompositionPipeline, CompositorMsg, CompositorReceiver, + ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, SendableFrameTree, }; use crossbeam_channel::Sender; use embedder_traits::Cursor; @@ -187,9 +186,6 @@ pub struct IOCompositor { /// Pending scroll/zoom events. pending_scroll_zoom_events: Vec, - /// Whether we're waiting on a recomposite after dispatching a scroll. - waiting_for_results_of_scroll: bool, - /// Used by the logic that determines when it is safe to output an /// image for the reftest framework. ready_to_save_state: ReadyState, @@ -249,13 +245,15 @@ pub struct IOCompositor { /// True to translate mouse input into touch events. convert_mouse_to_touch: bool, - /// True if a WR frame render has been requested. Screenshots - /// taken before the render is complete will not reflect the - /// most up to date rendering. - waiting_on_pending_frame: bool, + /// The number of frames pending to receive from WebRender. + pending_frames: usize, /// Waiting for external code to call present. waiting_on_present: bool, + + /// The [`Instant`] of the last animation tick, used to avoid flooding the Constellation and + /// ScriptThread with a deluge of animation ticks. + last_animation_tick: Instant, } #[derive(Clone, Copy)] @@ -277,6 +275,23 @@ enum ScrollZoomEvent { Scroll(ScrollEvent), } +/// Why we performed a composite. This is used for debugging. +/// +/// TODO: It would be good to have a bit more precision here about why a composite +/// was originally triggered, but that would require tracking the reason when a +/// frame is queued in WebRender and then remembering when the frame is ready. +#[derive(Clone, Copy, Debug, PartialEq)] +enum CompositingReason { + /// We're performing the single composite in headless mode. + Headless, + /// We're performing a composite to run an animation. + Animation, + /// A new WebRender frame has arrived. + NewWebRenderFrame, + /// The window has been resized and will need to be synchronously repainted. + Resize, +} + #[derive(Debug, PartialEq)] enum CompositionRequest { NoCompositingNecessary, @@ -414,7 +429,6 @@ impl IOCompositor { composition_request: CompositionRequest::NoCompositingNecessary, touch_handler: TouchHandler::new(), pending_scroll_zoom_events: Vec::new(), - waiting_for_results_of_scroll: false, composite_target, shutdown_state: ShutdownState::NotShuttingDown, page_zoom: Scale::new(1.0), @@ -442,8 +456,9 @@ impl IOCompositor { is_running_problem_test, exit_after_load, convert_mouse_to_touch, - waiting_on_pending_frame: false, + pending_frames: 0, waiting_on_present: false, + last_animation_tick: Instant::now(), } } @@ -556,6 +571,10 @@ impl IOCompositor { } fn handle_browser_message(&mut self, msg: CompositorMsg) -> bool { + if matches!(msg, CompositorMsg::NewWebRenderFrameReady(..)) { + self.pending_frames -= 1; + } + match (msg, self.shutdown_state) { (_, ShutdownState::FinishedShuttingDown) => { error!("compositor shouldn't be handling messages after shutting down"); @@ -641,11 +660,6 @@ impl IOCompositor { } }, - (CompositorMsg::Recomposite(reason), ShutdownState::NotShuttingDown) => { - self.waiting_on_pending_frame = false; - self.composition_request = CompositionRequest::CompositeNow(reason) - }, - (CompositorMsg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => { self.touch_handler.on_event_processed(result); }, @@ -666,8 +680,7 @@ impl IOCompositor { self.ready_to_save_state, ReadyState::WaitingForConstellationReply ); - if is_ready && !self.waiting_on_pending_frame && !self.waiting_for_results_of_scroll - { + if is_ready && self.pending_frames == 0 { self.ready_to_save_state = ReadyState::ReadyToSaveImage; if self.is_running_problem_test { println!("ready to save image!"); @@ -686,7 +699,7 @@ impl IOCompositor { ShutdownState::NotShuttingDown, ) => { self.pipeline_details(pipeline_id).visible = visible; - self.process_animations(); + self.process_animations(true); }, (CompositorMsg::PipelineExited(pipeline_id, sender), _) => { @@ -696,17 +709,19 @@ impl IOCompositor { }, ( - CompositorMsg::NewScrollFrameReady(recomposite_needed), + CompositorMsg::NewWebRenderFrameReady(recomposite_needed), ShutdownState::NotShuttingDown, ) => { - self.waiting_for_results_of_scroll = false; - if let Some(result) = self.hit_test_at_device_point(self.cursor_pos) { - self.update_cursor(result); - } if recomposite_needed { - self.composition_request = CompositionRequest::CompositeNow( - CompositingReason::NewWebRenderScrollFrame, - ); + if let Some(result) = self.hit_test_at_device_point(self.cursor_pos) { + self.update_cursor(result); + } + self.composition_request = + CompositionRequest::CompositeNow(CompositingReason::NewWebRenderFrame); + } + + if recomposite_needed || self.animation_callbacks_active() { + self.composite_if_necessary(CompositingReason::NewWebRenderFrame) } }, @@ -789,7 +804,6 @@ impl IOCompositor { ForwardedToCompositorMsg::Layout( script_traits::ScriptToCompositorMsg::SendInitialTransaction(pipeline), ) => { - self.waiting_on_pending_frame = true; let mut txn = Transaction::new(); txn.set_display_list( WebRenderEpoch(0), @@ -799,6 +813,7 @@ impl IOCompositor { false, ); + self.generate_frame(&mut txn); self.webrender_api .send_transaction(self.webrender_document, txn); }, @@ -806,11 +821,9 @@ impl IOCompositor { ForwardedToCompositorMsg::Layout( script_traits::ScriptToCompositorMsg::SendScrollNode(point, scroll_id), ) => { - self.waiting_for_results_of_scroll = true; - let mut txn = Transaction::new(); txn.scroll_node_with_id(point, scroll_id, ScrollClamping::NoClamping); - txn.generate_frame(0); + self.generate_frame(&mut txn); self.webrender_api .send_transaction(self.webrender_document, txn); }, @@ -827,8 +840,6 @@ impl IOCompositor { _ => return warn!("Could not recieve WebRender display list."), }; - self.waiting_on_pending_frame = true; - let pipeline_id = display_list_info.pipeline_id; let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id)); details.most_recent_display_list_epoch = Some(display_list_info.epoch); @@ -846,7 +857,7 @@ impl IOCompositor { ), true, ); - txn.generate_frame(0); + self.generate_frame(&mut txn); self.webrender_api .send_transaction(self.webrender_document, txn); }, @@ -968,6 +979,12 @@ impl IOCompositor { } } + /// Queue a new frame in the transaction and increase the pending frames count. + fn generate_frame(&mut self, transaction: &mut Transaction) { + self.pending_frames += 1; + transaction.generate_frame(0); + } + /// Sets or unsets the animations-running flag for the given pipeline, and schedules a /// recomposite if necessary. fn change_running_animations_state( @@ -1029,7 +1046,7 @@ impl IOCompositor { fn update_root_pipeline(&mut self) { let mut txn = Transaction::new(); self.update_root_pipeline_with_txn(&mut txn); - txn.generate_frame(0); + self.generate_frame(&mut txn); self.webrender_api .send_transaction(self.webrender_document, txn); } @@ -1696,10 +1713,9 @@ impl IOCompositor { let scroll_origin = LayoutPoint::new(-offset.x, -offset.y); transaction.scroll_node_with_id(scroll_origin, external_id, ScrollClamping::NoClamping); self.send_scroll_positions_to_layout_for_pipeline(&pipeline_id); - self.waiting_for_results_of_scroll = true } - transaction.generate_frame(0); + self.generate_frame(&mut transaction); self.webrender_api .send_transaction(self.webrender_document, transaction); } @@ -1740,7 +1756,18 @@ impl IOCompositor { } /// If there are any animations running, dispatches appropriate messages to the constellation. - fn process_animations(&mut self) { + fn process_animations(&mut self, force: bool) { + // When running animations in order to dump a screenshot (not after a full composite), don't send + // animation ticks faster than about 60Hz. + // + // TODO: This should be based on the refresh rate of the screen and also apply to all + // animation ticks, not just ones sent while waiting to dump screenshots. This requires + // something like a refresh driver concept though. + if !force && (Instant::now() - self.last_animation_tick) < Duration::from_millis(16) { + return; + } + self.last_animation_tick = Instant::now(); + let mut pipeline_ids = vec![]; for (pipeline_id, pipeline_details) in &self.pipeline_details { if (pipeline_details.animations_running || pipeline_details.animation_callbacks_running) && @@ -1885,6 +1912,13 @@ impl IOCompositor { false } + /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response. + fn animation_callbacks_active(&self) -> bool { + self.pipeline_details + .values() + .any(|details| details.animation_callbacks_running) + } + /// Query the constellation to see if the current compositor /// output matches the current frame tree output, and if the /// associated script threads are idle. @@ -1999,7 +2033,7 @@ impl IOCompositor { // tick those instead and continue waiting for the image output to be stable AND // all active animations to complete. if self.animations_active() { - self.process_animations(); + self.process_animations(false); return Err(UnableToComposite::NotReadyToPaintImage( NotReadyToPaint::AnimationsActive, )); @@ -2188,8 +2222,7 @@ impl IOCompositor { self.composition_request = CompositionRequest::NoCompositingNecessary; - self.process_animations(); - self.waiting_for_results_of_scroll = false; + self.process_animations(true); Ok(rv) } @@ -2291,8 +2324,12 @@ impl IOCompositor { let mut found_recomposite_msg = false; while let Some(msg) = self.port.try_recv_compositor_msg() { match msg { - CompositorMsg::Recomposite(_) if found_recomposite_msg => {}, - CompositorMsg::Recomposite(_) => { + CompositorMsg::NewWebRenderFrameReady(_) if found_recomposite_msg => { + // Only take one of duplicate NewWebRendeFrameReady messages, but do subtract + // one frame from the pending frames. + self.pending_frames -= 1; + }, + CompositorMsg::NewWebRenderFrameReady(_) => { found_recomposite_msg = true; compositor_messages.push(msg) }, @@ -2332,7 +2369,7 @@ impl IOCompositor { // The WebXR thread may make a different context current let _ = self.rendering_context.make_gl_context_current(); - if !self.pending_scroll_zoom_events.is_empty() && !self.waiting_for_results_of_scroll { + if !self.pending_scroll_zoom_events.is_empty() { self.process_pending_scroll_events() } self.shutdown_state != ShutdownState::FinishedShuttingDown @@ -2346,7 +2383,7 @@ impl IOCompositor { while self.shutdown_state != ShutdownState::ShuttingDown { let msg = self.port.recv_compositor_msg(); let need_recomposite = match msg { - CompositorMsg::Recomposite(_) => true, + CompositorMsg::NewWebRenderFrameReady(_) => true, _ => false, }; let keep_going = self.handle_browser_message(msg); @@ -2391,7 +2428,7 @@ impl IOCompositor { self.webrender.set_debug_flags(flags); let mut txn = Transaction::new(); - txn.generate_frame(0); + self.generate_frame(&mut txn); self.webrender_api .send_transaction(self.webrender_document, txn); } diff --git a/components/config/Cargo.toml b/components/config/Cargo.toml index 8befa01cb54..ea92a1d99f8 100644 --- a/components/config/Cargo.toml +++ b/components/config/Cargo.toml @@ -22,7 +22,7 @@ serde_json = { workspace = true } servo_config_plugins = { path = "../config_plugins" } servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } -style_config = { path = "../style_config" } +style_config = { workspace = true } url = { workspace = true } [target.'cfg(not(target_os = "android"))'.dependencies] diff --git a/components/config/prefs.rs b/components/config/prefs.rs index e5ec7c3aeec..826605da6d9 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -519,9 +519,6 @@ mod gen { enabled: bool, }, legacy_layout: bool, - tables: { - enabled: bool, - }, #[serde(default = "default_layout_threads")] threads: i64, writing_mode: { diff --git a/components/derive_common/Cargo.toml b/components/derive_common/Cargo.toml deleted file mode 100644 index 5677069ad56..00000000000 --- a/components/derive_common/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "derive_common" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -publish = false - -[lib] -path = "lib.rs" - -[dependencies] -darling = { workspace = true } -proc-macro2 = { workspace = true } -quote = { workspace = true } -syn = { workspace = true } -synstructure = { workspace = true } diff --git a/components/derive_common/cg.rs b/components/derive_common/cg.rs deleted file mode 100644 index 73301af2ff2..00000000000 --- a/components/derive_common/cg.rs +++ /dev/null @@ -1,396 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use darling::{FromDeriveInput, FromField, FromVariant}; -use proc_macro2::{Span, TokenStream}; -use quote::TokenStreamExt; -use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field}; -use syn::{GenericArgument, GenericParam, Ident, Path}; -use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup}; -use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple}; -use syn::{Variant, WherePredicate}; -use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo}; - -/// Given an input type which has some where clauses already, like: -/// -/// struct InputType -/// where -/// T: Zero, -/// { -/// ... -/// } -/// -/// Add the necessary `where` clauses so that the output type of a trait -/// fulfils them. -/// -/// For example: -/// -/// ```ignore -/// ::ComputedValue: Zero, -/// ``` -/// -/// This needs to run before adding other bounds to the type parameters. -pub fn propagate_clauses_to_output_type( - where_clause: &mut Option, - generics: &syn::Generics, - trait_path: &Path, - trait_output: &Ident, -) { - let where_clause = match *where_clause { - Some(ref mut clause) => clause, - None => return, - }; - let mut extra_bounds = vec![]; - for pred in &where_clause.predicates { - let ty = match *pred { - syn::WherePredicate::Type(ref ty) => ty, - ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate), - }; - - let path = match ty.bounded_ty { - syn::Type::Path(ref p) => &p.path, - ref ty => panic!("Unhanded complex where type: {:?}", ty), - }; - - assert!( - ty.lifetimes.is_none(), - "Unhanded complex lifetime bound: {:?}", - ty, - ); - - let ident = match path_to_ident(path) { - Some(i) => i, - None => panic!("Unhanded complex where type path: {:?}", path), - }; - - if generics.type_params().any(|param| param.ident == *ident) { - extra_bounds.push(ty.clone()); - } - } - - for bound in extra_bounds { - let ty = bound.bounded_ty; - let bounds = bound.bounds; - where_clause - .predicates - .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds)) - } -} - -pub fn add_predicate(where_clause: &mut Option, pred: WherePredicate) { - where_clause - .get_or_insert(parse_quote!(where)) - .predicates - .push(pred); -} - -pub fn fmap_match(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream -where - F: FnMut(&BindingInfo) -> TokenStream, -{ - fmap2_match(input, bind_style, f, |_| None) -} - -pub fn fmap2_match( - input: &DeriveInput, - bind_style: BindStyle, - mut f: F, - mut g: G, -) -> TokenStream -where - F: FnMut(&BindingInfo) -> TokenStream, - G: FnMut(&BindingInfo) -> Option, -{ - let mut s = synstructure::Structure::new(input); - s.variants_mut().iter_mut().for_each(|v| { - v.bind_with(|_| bind_style); - }); - s.each_variant(|variant| { - let (mapped, mapped_fields) = value(variant, "mapped"); - let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter()); - let mut computations = quote!(); - computations.append_all(fields_pairs.map(|(field, mapped_field)| { - let expr = f(field); - quote! { let #mapped_field = #expr; } - })); - computations.append_all( - mapped_fields - .iter() - .map(|mapped_field| match g(mapped_field) { - Some(expr) => quote! { let #mapped_field = #expr; }, - None => quote!(), - }), - ); - computations.append_all(mapped); - Some(computations) - }) -} - -pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &Ident) -> Path { - let segment = PathSegment { - ident: input.ident.clone(), - arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args: input - .generics - .params - .iter() - .map(|arg| match arg { - &GenericParam::Lifetime(ref data) => { - GenericArgument::Lifetime(data.lifetime.clone()) - }, - &GenericParam::Type(ref data) => { - let ident = &data.ident; - GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output)) - }, - &GenericParam::Const(ref inner) => { - let ident = &inner.ident; - GenericArgument::Const(parse_quote!(#ident)) - }, - }) - .collect(), - colon2_token: Default::default(), - gt_token: Default::default(), - lt_token: Default::default(), - }), - }; - segment.into() -} - -pub fn map_type_params(ty: &Type, params: &[&TypeParam], self_type: &Path, f: &mut F) -> Type -where - F: FnMut(&Ident) -> Type, -{ - match *ty { - Type::Slice(ref inner) => Type::from(TypeSlice { - elem: Box::new(map_type_params(&inner.elem, params, self_type, f)), - ..inner.clone() - }), - Type::Array(ref inner) => { - //ref ty, ref expr) => { - Type::from(TypeArray { - elem: Box::new(map_type_params(&inner.elem, params, self_type, f)), - ..inner.clone() - }) - }, - ref ty @ Type::Never(_) => ty.clone(), - Type::Tuple(ref inner) => Type::from(TypeTuple { - elems: inner - .elems - .iter() - .map(|ty| map_type_params(&ty, params, self_type, f)) - .collect(), - ..inner.clone() - }), - Type::Path(TypePath { - qself: None, - ref path, - }) => { - if let Some(ident) = path_to_ident(path) { - if params.iter().any(|ref param| ¶m.ident == ident) { - return f(ident); - } - if ident == "Self" { - return Type::from(TypePath { - qself: None, - path: self_type.clone(), - }); - } - } - Type::from(TypePath { - qself: None, - path: map_type_params_in_path(path, params, self_type, f), - }) - }, - Type::Path(TypePath { - ref qself, - ref path, - }) => Type::from(TypePath { - qself: qself.as_ref().map(|qself| QSelf { - ty: Box::new(map_type_params(&qself.ty, params, self_type, f)), - position: qself.position, - ..qself.clone() - }), - path: map_type_params_in_path(path, params, self_type, f), - }), - Type::Paren(ref inner) => Type::from(TypeParen { - elem: Box::new(map_type_params(&inner.elem, params, self_type, f)), - ..inner.clone() - }), - Type::Group(ref inner) => Type::from(TypeGroup { - elem: Box::new(map_type_params(&inner.elem, params, self_type, f)), - ..inner.clone() - }), - ref ty => panic!("type {:?} cannot be mapped yet", ty), - } -} - -fn map_type_params_in_path( - path: &Path, - params: &[&TypeParam], - self_type: &Path, - f: &mut F, -) -> Path -where - F: FnMut(&Ident) -> Type, -{ - Path { - leading_colon: path.leading_colon, - segments: path - .segments - .iter() - .map(|segment| PathSegment { - ident: segment.ident.clone(), - arguments: match segment.arguments { - PathArguments::AngleBracketed(ref data) => { - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - args: data - .args - .iter() - .map(|arg| match arg { - ty @ &GenericArgument::Lifetime(_) => ty.clone(), - &GenericArgument::Type(ref data) => GenericArgument::Type( - map_type_params(data, params, self_type, f), - ), - &GenericArgument::AssocType(ref data) => { - GenericArgument::AssocType(AssocType { - ty: map_type_params(&data.ty, params, self_type, f), - ..data.clone() - }) - }, - ref arg => panic!("arguments {:?} cannot be mapped yet", arg), - }) - .collect(), - ..data.clone() - }) - }, - ref arg @ PathArguments::None => arg.clone(), - ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters), - }, - }) - .collect(), - } -} - -fn path_to_ident(path: &Path) -> Option<&Ident> { - match *path { - Path { - leading_colon: None, - ref segments, - } if segments.len() == 1 => { - if segments[0].arguments.is_empty() { - Some(&segments[0].ident) - } else { - None - } - }, - _ => None, - } -} - -pub fn parse_field_attrs(field: &Field) -> A -where - A: FromField, -{ - match A::from_field(field) { - Ok(attrs) => attrs, - Err(e) => panic!("failed to parse field attributes: {}", e), - } -} - -pub fn parse_input_attrs(input: &DeriveInput) -> A -where - A: FromDeriveInput, -{ - match A::from_derive_input(input) { - Ok(attrs) => attrs, - Err(e) => panic!("failed to parse input attributes: {}", e), - } -} - -pub fn parse_variant_attrs_from_ast(variant: &VariantAst) -> A -where - A: FromVariant, -{ - let v = Variant { - ident: variant.ident.clone(), - attrs: variant.attrs.to_vec(), - fields: variant.fields.clone(), - discriminant: variant.discriminant.clone(), - }; - parse_variant_attrs(&v) -} - -pub fn parse_variant_attrs(variant: &Variant) -> A -where - A: FromVariant, -{ - match A::from_variant(variant) { - Ok(attrs) => attrs, - Err(e) => panic!("failed to parse variant attributes: {}", e), - } -} - -pub fn ref_pattern<'a>( - variant: &'a VariantInfo, - prefix: &str, -) -> (TokenStream, Vec>) { - let mut v = variant.clone(); - v.bind_with(|_| BindStyle::Ref); - v.bindings_mut().iter_mut().for_each(|b| { - b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site()) - }); - (v.pat(), v.bindings().to_vec()) -} - -pub fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec>) { - let mut v = variant.clone(); - v.bindings_mut().iter_mut().for_each(|b| { - b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site()) - }); - v.bind_with(|_| BindStyle::Move); - (v.pat(), v.bindings().to_vec()) -} - -/// Transforms "FooBar" to "foo-bar". -/// -/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string -/// is prepended with "-". -pub fn to_css_identifier(mut camel_case: &str) -> String { - camel_case = camel_case.trim_end_matches('_'); - let mut first = true; - let mut result = String::with_capacity(camel_case.len()); - while let Some(segment) = split_camel_segment(&mut camel_case) { - if first { - match segment { - "Moz" | "Webkit" | "Servo" => first = false, - _ => {}, - } - } - if !first { - result.push('-'); - } - first = false; - result.push_str(&segment.to_lowercase()); - } - result -} - -/// Transforms foo-bar to FOO_BAR. -pub fn to_scream_case(css_case: &str) -> String { - css_case.to_uppercase().replace('-', "_") -} - -/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar". -fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> { - let index = match camel_case.chars().next() { - None => return None, - Some(ch) => ch.len_utf8(), - }; - let end_position = camel_case[index..] - .find(char::is_uppercase) - .map_or(camel_case.len(), |pos| index + pos); - let result = &camel_case[..end_position]; - *camel_case = &camel_case[end_position..]; - Some(result) -} diff --git a/components/derive_common/lib.rs b/components/derive_common/lib.rs deleted file mode 100644 index 14415351449..00000000000 --- a/components/derive_common/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -extern crate darling; -extern crate proc_macro2; -#[macro_use] -extern crate quote; -#[macro_use] -extern crate syn; -extern crate synstructure; - -pub mod cg; diff --git a/components/derive_common/rustfmt.toml b/components/derive_common/rustfmt.toml deleted file mode 100644 index c7ad93bafe3..00000000000 --- a/components/derive_common/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -disable_all_formatting = true diff --git a/components/devtools/actor.rs b/components/devtools/actor.rs index 2f53e87a2ad..64b6064faa6 100644 --- a/components/devtools/actor.rs +++ b/components/devtools/actor.rs @@ -5,7 +5,7 @@ use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; -use std::mem::replace; +use std::mem; use std::net::TcpStream; use std::sync::{Arc, Mutex}; @@ -105,7 +105,7 @@ impl ActorRegistry { /// Get start stamp when registry was started pub fn start_stamp(&self) -> PreciseTime { - self.start_stamp.clone() + self.start_stamp } pub fn register_script_actor(&self, script_id: String, actor: String) { @@ -194,12 +194,12 @@ impl ActorRegistry { } }, } - let new_actors = replace(&mut *self.new_actors.borrow_mut(), vec![]); + let new_actors = mem::take(&mut *self.new_actors.borrow_mut()); for actor in new_actors.into_iter() { self.actors.insert(actor.name().to_owned(), actor); } - let old_actors = replace(&mut *self.old_actors.borrow_mut(), vec![]); + let old_actors = mem::take(&mut *self.old_actors.borrow_mut()); for name in old_actors { self.drop_actor(name); } diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 8a36d19137a..af67814d127 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -284,11 +284,11 @@ impl BrowsingContextActor { let tabdesc = TabDescriptorActor::new(actors, name.clone()); let target = BrowsingContextActor { - name: name, + name, script_chan: script_sender, - title: RefCell::new(String::from(title)), + title: RefCell::new(title), url: RefCell::new(url.into_string()), - console: console, + console, _emulation: emulation.name(), _inspector: inspector.name(), _timeline: timeline.name(), @@ -355,7 +355,7 @@ impl BrowsingContextActor { from: self.name(), type_: "tabNavigated".to_owned(), url: url.as_str().to_owned(), - title: title, + title, nativeConsoleAPI: true, state: state.to_owned(), isFrameSwitching: false, diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index f83fc3c05b2..b60e506ba43 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -133,7 +133,7 @@ impl ConsoleActor { } } - fn streams_mut<'a>(&self, registry: &'a ActorRegistry, cb: impl Fn(&mut TcpStream)) { + fn streams_mut(&self, registry: &ActorRegistry, cb: impl Fn(&mut TcpStream)) { match &self.root { Root::BrowsingContext(bc) => registry .find::(bc) @@ -221,7 +221,7 @@ impl ConsoleActor { ActorValue { class, uuid } => { //TODO: make initial ActorValue message include these properties? let mut m = Map::new(); - let actor = ObjectActor::new(registry, uuid); + let actor = ObjectActor::register(registry, uuid); m.insert("type".to_owned(), Value::String("object".to_owned())); m.insert("class".to_owned(), Value::String(class)); @@ -236,8 +236,8 @@ impl ConsoleActor { //TODO: catch and return exception values from JS evaluation let reply = EvaluateJSReply { from: self.name(), - input: input, - result: result, + input, + result, timestamp: 0, exception: Value::Null, exceptionMessage: Value::Null, @@ -255,7 +255,7 @@ impl ConsoleActor { self.cached_events .borrow_mut() .entry(id.clone()) - .or_insert(vec![]) + .or_default() .push(CachedConsoleMessage::PageError(page_error.clone())); if id == self.current_unique_id(registry) { let msg = PageErrorMsg { @@ -287,7 +287,7 @@ impl ConsoleActor { self.cached_events .borrow_mut() .entry(id.clone()) - .or_insert(vec![]) + .or_default() .push(CachedConsoleMessage::ConsoleAPI(ConsoleAPI { type_: "ConsoleAPI".to_owned(), level: level.clone(), @@ -306,7 +306,7 @@ impl ConsoleActor { from: self.name(), type_: "consoleAPICall".to_owned(), message: ConsoleMsg { - level: level, + level, timeStamp: SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() @@ -351,7 +351,7 @@ impl Actor for ConsoleActor { .unwrap() .as_array() .unwrap() - .into_iter() + .iter() .map(|json_type| json_type.as_str().unwrap()); let mut message_types = CachedConsoleMessageTypes::empty(); for str_type in str_types { @@ -393,7 +393,7 @@ impl Actor for ConsoleActor { let msg = GetCachedMessagesReply { from: self.name(), - messages: messages, + messages, }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -445,7 +445,7 @@ impl Actor for ConsoleActor { }, "evaluateJS" => { - let msg = self.evaluateJS(®istry, &msg); + let msg = self.evaluateJS(registry, msg); let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, @@ -468,14 +468,14 @@ impl Actor for ConsoleActor { return Ok(ActorMessageStatus::Processed); } - let reply = self.evaluateJS(®istry, &msg).unwrap(); + let reply = self.evaluateJS(registry, msg).unwrap(); let msg = EvaluateJSEvent { from: self.name(), r#type: "evaluationResult".to_owned(), input: reply.input, result: reply.result, timestamp: reply.timestamp, - resultID: resultID, + resultID, exception: reply.exception, exceptionMessage: reply.exceptionMessage, helperResult: reply.helperResult, diff --git a/components/devtools/actors/device.rs b/components/devtools/actors/device.rs index 9fb0c6a84b9..ed4c7993c9c 100644 --- a/components/devtools/actors/device.rs +++ b/components/devtools/actors/device.rs @@ -59,7 +59,7 @@ impl Actor for DeviceActor { impl DeviceActor { pub fn new(name: String) -> DeviceActor { - DeviceActor { name: name } + DeviceActor { name } } pub fn description() -> ActorDescription { diff --git a/components/devtools/actors/emulation.rs b/components/devtools/actors/emulation.rs index 4acfee1048e..6e91bea0037 100644 --- a/components/devtools/actors/emulation.rs +++ b/components/devtools/actors/emulation.rs @@ -21,19 +21,17 @@ impl Actor for EmulationActor { fn handle_message( &self, _registry: &ActorRegistry, - msg_type: &str, + _msg_type: &str, _msg: &Map, _stream: &mut TcpStream, _id: StreamId, ) -> Result { - Ok(match msg_type { - _ => ActorMessageStatus::Ignored, - }) + Ok(ActorMessageStatus::Ignored) } } impl EmulationActor { pub fn new(name: String) -> EmulationActor { - EmulationActor { name: name } + EmulationActor { name } } } diff --git a/components/devtools/actors/framerate.rs b/components/devtools/actors/framerate.rs index 607222489fa..5428a5cba67 100644 --- a/components/devtools/actors/framerate.rs +++ b/components/devtools/actors/framerate.rs @@ -51,7 +51,7 @@ impl FramerateActor { let mut actor = FramerateActor { name: actor_name.clone(), pipeline: pipeline_id, - script_sender: script_sender, + script_sender, is_recording: false, ticks: Vec::new(), }; @@ -71,7 +71,7 @@ impl FramerateActor { } pub fn take_pending_ticks(&mut self) -> Vec { - mem::replace(&mut self.ticks, Vec::new()) + mem::take(&mut self.ticks) } fn start_recording(&mut self) { diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index 0728f967411..eae82683748 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -207,8 +207,8 @@ impl NodeInfoToProtocol for NodeInfo { let name = actors.new_name("node"); let node_actor = NodeActor { name: name.clone(), - script_chan: script_chan, - pipeline: pipeline.clone(), + script_chan, + pipeline, }; actors.register_script_actor(self.uniqueId, name.clone()); actors.register_later(Box::new(node_actor)); @@ -315,7 +315,7 @@ impl Actor for WalkerActor { let msg = DocumentElementReply { from: self.name(), - node: node, + node, }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -538,16 +538,16 @@ impl Actor for PageStyleActor { let auto_margins = msg .get("autoMargins") - .and_then(&Value::as_bool) + .and_then(Value::as_bool) .unwrap_or(false); // http://mxr.mozilla.org/mozilla-central/source/toolkit/devtools/server/actors/styles.js let msg = GetLayoutReply { from: self.name(), - display: display, - position: position, - zIndex: zIndex, - boxSizing: boxSizing, + display, + position, + zIndex, + boxSizing, autoMargins: if auto_margins { let mut m = Map::new(); let auto = serde_json::value::Value::String("auto".to_owned()); @@ -567,20 +567,20 @@ impl Actor for PageStyleActor { } else { serde_json::value::Value::Null }, - marginTop: marginTop, - marginRight: marginRight, - marginBottom: marginBottom, - marginLeft: marginLeft, - borderTopWidth: borderTopWidth, - borderRightWidth: borderRightWidth, - borderBottomWidth: borderBottomWidth, - borderLeftWidth: borderLeftWidth, - paddingTop: paddingTop, - paddingRight: paddingRight, - paddingBottom: paddingBottom, - paddingLeft: paddingLeft, - width: width, - height: height, + marginTop, + marginRight, + marginBottom, + marginLeft, + borderTopWidth, + borderRightWidth, + borderBottomWidth, + borderLeftWidth, + paddingTop, + paddingRight, + paddingBottom, + paddingLeft, + width, + height, }; let msg = serde_json::to_string(&msg).unwrap(); let msg = serde_json::from_str::(&msg).unwrap(); @@ -614,7 +614,7 @@ impl Actor for InspectorActor { let walker = WalkerActor { name: registry.new_name("walker"), script_chan: self.script_chan.clone(), - pipeline: pipeline, + pipeline, }; let mut walker_name = self.walker.borrow_mut(); *walker_name = Some(walker.name()); @@ -643,7 +643,7 @@ impl Actor for InspectorActor { let style = PageStyleActor { name: registry.new_name("pageStyle"), script_chan: self.script_chan.clone(), - pipeline: pipeline, + pipeline, }; let mut pageStyle = self.pageStyle.borrow_mut(); *pageStyle = Some(style.name()); diff --git a/components/devtools/actors/network_event.rs b/components/devtools/actors/network_event.rs index 7eb664f9757..2a5a4f77e65 100644 --- a/components/devtools/actors/network_event.rs +++ b/components/devtools/actors/network_event.rs @@ -200,7 +200,7 @@ impl Actor for NetworkEventActor { } let msg = GetRequestHeadersReply { from: self.name(), - headers: headers, + headers, headerSize: headersSize, rawHeaders: rawHeadersString, }; @@ -218,7 +218,7 @@ impl Actor for NetworkEventActor { let msg = GetRequestCookiesReply { from: self.name(), - cookies: cookies, + cookies, }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -244,13 +244,13 @@ impl Actor for NetworkEventActor { }); headersSize += name.as_str().len() + value.len(); rawHeadersString.push_str(name.as_str()); - rawHeadersString.push_str(":"); + rawHeadersString.push(':'); rawHeadersString.push_str(value.to_str().unwrap()); rawHeadersString.push_str("\r\n"); } let msg = GetResponseHeadersReply { from: self.name(), - headers: headers, + headers, headerSize: headersSize, rawHeaders: rawHeadersString, }; @@ -269,7 +269,7 @@ impl Actor for NetworkEventActor { let msg = GetResponseCookiesReply { from: self.name(), - cookies: cookies, + cookies, }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -322,7 +322,7 @@ impl Actor for NetworkEventActor { impl NetworkEventActor { pub fn new(name: String) -> NetworkEventActor { NetworkEventActor { - name: name, + name, request: HttpRequest { url: String::new(), method: Method::GET, @@ -378,8 +378,8 @@ impl NetworkEventActor { .as_millis() as i64, ) { LocalResult::None => "".to_owned(), - LocalResult::Single(dateTime) => format!("{}", dateTime.to_rfc3339()), - LocalResult::Ambiguous(dateTime, _) => format!("{}", dateTime.to_rfc3339()), + LocalResult::Single(dateTime) => dateTime.to_rfc3339().to_string(), + LocalResult::Ambiguous(dateTime, _) => dateTime.to_rfc3339().to_string(), }; EventActor { diff --git a/components/devtools/actors/object.rs b/components/devtools/actors/object.rs index 4d08cb3c305..4091596f0b2 100644 --- a/components/devtools/actors/object.rs +++ b/components/devtools/actors/object.rs @@ -31,7 +31,7 @@ impl Actor for ObjectActor { } impl ObjectActor { - pub fn new(registry: &ActorRegistry, uuid: String) -> String { + pub fn register(registry: &ActorRegistry, uuid: String) -> String { if !registry.script_actor_registered(uuid.clone()) { let name = registry.new_name("object"); let actor = ObjectActor { diff --git a/components/devtools/actors/performance.rs b/components/devtools/actors/performance.rs index 5e8bc2b5738..2c60d79e30a 100644 --- a/components/devtools/actors/performance.rs +++ b/components/devtools/actors/performance.rs @@ -98,7 +98,7 @@ impl Actor for PerformanceActor { impl PerformanceActor { pub fn new(name: String) -> PerformanceActor { - PerformanceActor { name: name } + PerformanceActor { name } } pub fn description() -> ActorDescription { diff --git a/components/devtools/actors/profiler.rs b/components/devtools/actors/profiler.rs index 7db5f77a284..38c3d03f8cc 100644 --- a/components/devtools/actors/profiler.rs +++ b/components/devtools/actors/profiler.rs @@ -32,6 +32,6 @@ impl Actor for ProfilerActor { impl ProfilerActor { pub fn new(name: String) -> ProfilerActor { - ProfilerActor { name: name } + ProfilerActor { name } } } diff --git a/components/devtools/actors/root.rs b/components/devtools/actors/root.rs index 384a6022fd8..7cb96b512de 100644 --- a/components/devtools/actors/root.rs +++ b/components/devtools/actors/root.rs @@ -189,7 +189,7 @@ impl Actor for RootActor { .map(|target| { registry .find::(target) - .encodable(®istry) + .encodable(registry) }) .collect(), }; @@ -223,7 +223,7 @@ impl Actor for RootActor { let tab = registry.find::(&self.tabs[0]); let reply = GetTabReply { from: self.name(), - tab: tab.encodable(®istry), + tab: tab.encodable(registry), }; let _ = stream.write_json_packet(&reply); ActorMessageStatus::Processed diff --git a/components/devtools/actors/stylesheets.rs b/components/devtools/actors/stylesheets.rs index fadb16f1f3c..772d807e4ea 100644 --- a/components/devtools/actors/stylesheets.rs +++ b/components/devtools/actors/stylesheets.rs @@ -50,6 +50,6 @@ impl Actor for StyleSheetsActor { impl StyleSheetsActor { pub fn new(name: String) -> StyleSheetsActor { - StyleSheetsActor { name: name } + StyleSheetsActor { name } } } diff --git a/components/devtools/actors/tab.rs b/components/devtools/actors/tab.rs index 6f31b9b79c0..3e8ac2b4bb9 100644 --- a/components/devtools/actors/tab.rs +++ b/components/devtools/actors/tab.rs @@ -79,7 +79,7 @@ impl TabDescriptorActor { let root = actors.find_mut::("root"); root.tabs.push(name.clone()); TabDescriptorActor { - name: name, + name, browsing_context_actor, } } diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index 353f429ac54..00cd0839ab2 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -73,7 +73,7 @@ pub struct ThreadActor { impl ThreadActor { pub fn new(name: String) -> ThreadActor { - ThreadActor { name: name } + ThreadActor { name } } } diff --git a/components/devtools/actors/timeline.rs b/components/devtools/actors/timeline.rs index 9a28bdcbefa..7931f083426 100644 --- a/components/devtools/actors/timeline.rs +++ b/components/devtools/actors/timeline.rs @@ -107,7 +107,7 @@ pub struct HighResolutionStamp(f64); impl HighResolutionStamp { pub fn new(start_stamp: PreciseTime, time: PreciseTime) -> HighResolutionStamp { let duration = start_stamp.to(time).as_micros(); - HighResolutionStamp(duration as f64 / 1000 as f64) + HighResolutionStamp(duration as f64 / 1000_f64) } pub fn wrap(time: f64) -> HighResolutionStamp { @@ -132,10 +132,10 @@ impl TimelineActor { let marker_types = vec![TimelineMarkerType::Reflow, TimelineMarkerType::DOMEvent]; TimelineActor { - name: name, - pipeline: pipeline, - marker_types: marker_types, - script_sender: script_sender, + name, + pipeline, + marker_types, + script_sender, is_recording: Arc::new(Mutex::new(false)), stream: RefCell::new(None), @@ -217,7 +217,7 @@ impl Actor for TimelineActor { if let Some(true) = with_ticks.as_bool() { let framerate_actor = Some(FramerateActor::create( registry, - self.pipeline.clone(), + self.pipeline, self.script_sender.clone(), )); *self.framerate_actor.borrow_mut() = framerate_actor; @@ -274,7 +274,7 @@ impl Actor for TimelineActor { "isRecording" => { let msg = IsRecordingReply { from: self.name(), - value: self.is_recording.lock().unwrap().clone(), + value: *self.is_recording.lock().unwrap(), }; let _ = stream.write_json_packet(&msg); @@ -297,9 +297,9 @@ impl Emitter { ) -> Emitter { Emitter { from: name, - stream: stream, - registry: registry, - start_stamp: start_stamp, + stream, + registry, + start_stamp, framerate_actor: framerate_actor_name, memory_actor: memory_actor_name, @@ -320,7 +320,7 @@ impl Emitter { let end_time = PreciseTime::now(); let reply = MarkersEmitterReply { type_: "markers".to_owned(), - markers: markers, + markers, from: self.from.clone(), endTime: HighResolutionStamp::new(self.start_stamp, end_time), }; diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 5ed2111ee6e..467bccddd1e 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -10,6 +10,7 @@ #![crate_name = "devtools"] #![crate_type = "rlib"] #![allow(non_snake_case)] +#![allow(clippy::too_many_arguments)] #![deny(unsafe_code)] use std::borrow::ToOwned; @@ -135,7 +136,7 @@ fn run_server( port: u16, embedder: EmbedderProxy, ) { - let bound = TcpListener::bind(&("0.0.0.0", port)).ok().and_then(|l| { + let bound = TcpListener::bind(("0.0.0.0", port)).ok().and_then(|l| { l.local_addr() .map(|addr| addr.port()) .ok() @@ -143,7 +144,7 @@ fn run_server( }); // A token shared with the embedder to bypass permission prompt. - let token = format!("{:X}", servo_rand::ServoRng::new().next_u32()); + let token = format!("{:X}", servo_rand::ServoRng::default().next_u32()); let port = bound.as_ref().map(|(_, port)| *port).ok_or(()); embedder.send((None, EmbedderMsg::OnDevtoolsStarted(port, token.clone()))); @@ -259,7 +260,7 @@ fn run_server( Some(bc) => bc, None => return, }; - let name = match browsing_contexts.get(&bc) { + let name = match browsing_contexts.get(bc) { Some(name) => name, None => return, }; @@ -299,7 +300,7 @@ fn run_server( name: worker_name.clone(), console: console_name.clone(), thread: thread_name, - id: id, + id, url: page_info.url.clone(), type_: WorkerType::Dedicated, script_chan: script_sender, @@ -324,7 +325,7 @@ fn run_server( page_info, pipeline, script_sender, - &mut *actors, + &mut actors, ); let name = browsing_context_actor.name(); browsing_contexts.insert(browsing_context, name.clone()); @@ -366,7 +367,7 @@ fn run_server( let actors = actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker); - console_actor.handle_page_error(page_error, id, &*actors); + console_actor.handle_page_error(page_error, id, &actors); } fn handle_console_message( @@ -392,7 +393,7 @@ fn run_server( let actors = actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); let id = worker_id.map_or(UniqueId::Pipeline(id), UniqueId::Worker); - console_actor.handle_console_api(console_message, id, &*actors); + console_actor.handle_console_api(console_message, id, &actors); } fn find_console_actor( diff --git a/components/devtools/protocol.rs b/components/devtools/protocol.rs index 8edbce95a8c..5926e27701a 100644 --- a/components/devtools/protocol.rs +++ b/components/devtools/protocol.rs @@ -80,7 +80,7 @@ impl JsonPacketStream for TcpStream { Ok(packet_len) => packet_len, Err(_) => return Err("nonvalid UTF8 in packet length".to_owned()), }; - let packet_len = match u64::from_str_radix(&packet_len_str, 10) { + let packet_len = match packet_len_str.parse::() { Ok(packet_len) => packet_len, Err(_) => return Err("packet length missing / not parsable".to_owned()), }; diff --git a/components/geometry/Cargo.toml b/components/geometry/Cargo.toml index 87a870c0aa5..a816b6f5278 100644 --- a/components/geometry/Cargo.toml +++ b/components/geometry/Cargo.toml @@ -13,6 +13,6 @@ path = "lib.rs" [dependencies] app_units = { workspace = true } euclid = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } webrender_api = { workspace = true } diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index 9538ce11960..114e727d041 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -25,16 +25,16 @@ ipc-channel = { workspace = true } lazy_static = { workspace = true } libc = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } net_traits = { workspace = true } range = { path = "../range" } serde = { workspace = true } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms" } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_url = { path = "../url" } smallvec = { workspace = true, features = ["union"] } surfman = { workspace = true } -style = { path = "../style", features = ["servo"] } +style = { workspace = true } ucd = "0.1.1" unicode-bidi = { workspace = true, features = ["with_serde"] } unicode-script = { workspace = true } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 5bce57528f5..9fd25b839d0 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -169,19 +169,23 @@ pub struct Font { pub handle: FontHandle, pub metrics: FontMetrics, pub descriptor: FontDescriptor, - pub actual_pt_size: Au, shaper: Option, shape_cache: RefCell>>, glyph_advance_cache: RefCell>, pub font_key: FontInstanceKey, + + /// If this is a synthesized small caps font, then this font reference is for + /// the version of the font used to replace lowercase ASCII letters. It's up + /// to the consumer of this font to properly use this reference. + pub synthesized_small_caps: Option, } impl Font { pub fn new( handle: FontHandle, descriptor: FontDescriptor, - actual_pt_size: Au, font_key: FontInstanceKey, + synthesized_small_caps: Option, ) -> Font { let metrics = handle.metrics(); @@ -189,11 +193,11 @@ impl Font { handle: handle, shaper: None, descriptor, - actual_pt_size, metrics, shape_cache: RefCell::new(HashMap::new()), glyph_advance_cache: RefCell::new(HashMap::new()), font_key, + synthesized_small_caps, } } @@ -351,7 +355,7 @@ impl Font { #[inline] pub fn glyph_index(&self, codepoint: char) -> Option { let codepoint = match self.descriptor.variant { - font_variant_caps::T::SmallCaps => codepoint.to_uppercase().next().unwrap(), //FIXME: #5938 + font_variant_caps::T::SmallCaps => codepoint.to_ascii_uppercase(), font_variant_caps::T::Normal => codepoint, }; self.handle.glyph_index(codepoint) @@ -418,23 +422,33 @@ impl FontGroup { mut font_context: &mut FontContext, codepoint: char, ) -> Option { + let should_look_for_small_caps = self.descriptor.variant == font_variant_caps::T::SmallCaps && + codepoint.is_ascii_lowercase(); + let font_or_synthesized_small_caps = |font: FontRef| { + if should_look_for_small_caps { + let font = font.borrow(); + if font.synthesized_small_caps.is_some() { + return font.synthesized_small_caps.clone(); + } + } + Some(font) + }; + let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint); - let font = self.find(&mut font_context, |font| has_glyph(font)); - if font.is_some() { - return font; + if let Some(font) = self.find(&mut font_context, |font| has_glyph(font)) { + return font_or_synthesized_small_caps(font); } - if let Some(ref fallback) = self.last_matching_fallback { - if has_glyph(&fallback) { - return self.last_matching_fallback.clone(); + if let Some(ref last_matching_fallback) = self.last_matching_fallback { + if has_glyph(&last_matching_fallback) { + return font_or_synthesized_small_caps(last_matching_fallback.clone()); } } - let font = self.find_fallback(&mut font_context, Some(codepoint), has_glyph); - if font.is_some() { - self.last_matching_fallback = font.clone(); - return font; + if let Some(font) = self.find_fallback(&mut font_context, Some(codepoint), has_glyph) { + self.last_matching_fallback = Some(font.clone()); + return font_or_synthesized_small_caps(font); } self.first(&mut font_context) diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index cd07e658b3e..3a577a1fbd1 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -115,6 +115,36 @@ impl FontContext { font_descriptor: &FontDescriptor, family_descriptor: &FontFamilyDescriptor, ) -> Option { + self.get_font_maybe_synthesizing_small_caps( + font_descriptor, + family_descriptor, + true, /* synthesize_small_caps */ + ) + } + + fn get_font_maybe_synthesizing_small_caps( + &mut self, + font_descriptor: &FontDescriptor, + family_descriptor: &FontFamilyDescriptor, + synthesize_small_caps: bool, + ) -> Option { + // TODO: (Bug #3463): Currently we only support fake small-caps + // painting. We should also support true small-caps (where the + // font supports it) in the future. + let synthesized_small_caps_font = + if font_descriptor.variant == FontVariantCaps::SmallCaps && synthesize_small_caps { + let mut small_caps_descriptor = font_descriptor.clone(); + small_caps_descriptor.pt_size = + font_descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR); + self.get_font_maybe_synthesizing_small_caps( + &small_caps_descriptor, + family_descriptor, + false, /* synthesize_small_caps */ + ) + } else { + None + }; + let cache_key = FontCacheKey { font_descriptor: font_descriptor.clone(), family_descriptor: family_descriptor.clone(), @@ -132,8 +162,12 @@ impl FontContext { let font = self .font_template(&font_descriptor.template_descriptor, family_descriptor) .and_then(|template_info| { - self.create_font(template_info, font_descriptor.to_owned()) - .ok() + self.create_font( + template_info, + font_descriptor.to_owned(), + synthesized_small_caps_font, + ) + .ok() }) .map(|font| Rc::new(RefCell::new(font))); @@ -175,29 +209,22 @@ impl FontContext { &mut self, info: FontTemplateInfo, descriptor: FontDescriptor, + synthesized_small_caps: Option, ) -> Result { - // TODO: (Bug #3463): Currently we only support fake small-caps - // painting. We should also support true small-caps (where the - // font supports it) in the future. - let actual_pt_size = match descriptor.variant { - FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR), - FontVariantCaps::Normal => descriptor.pt_size, - }; - let handle = FontHandle::new_from_template( &self.platform_handle, info.font_template, - Some(actual_pt_size), + Some(descriptor.pt_size), )?; let font_instance_key = self .font_source - .get_font_instance(info.font_key, actual_pt_size); + .get_font_instance(info.font_key, descriptor.pt_size); Ok(Font::new( handle, descriptor, - actual_pt_size, font_instance_key, + synthesized_small_caps, )) } } diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 035a80ea7c3..6ea58172c27 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -245,8 +245,8 @@ fn test_font_template_is_cached() { let font2 = context.font(&font_descriptor, &family_descriptor).unwrap(); assert_ne!( - font1.borrow().actual_pt_size, - font2.borrow().actual_pt_size, + font1.borrow().descriptor.pt_size, + font2.borrow().descriptor.pt_size, "the same font should not have been returned" ); diff --git a/components/gfx/text/shaping/harfbuzz.rs b/components/gfx/text/shaping/harfbuzz.rs index 9128c7aab50..389a44b5457 100644 --- a/components/gfx/text/shaping/harfbuzz.rs +++ b/components/gfx/text/shaping/harfbuzz.rs @@ -11,9 +11,8 @@ use app_units::Au; use euclid::default::Point2D; // Eventually we would like the shaper to be pluggable, as many operating systems have their own // shapers. For now, however, HarfBuzz is a hard dependency. -use harfbuzz_sys::hb_blob_t; use harfbuzz_sys::{ - hb_blob_create, hb_bool_t, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy, + hb_blob_create, hb_blob_t, hb_bool_t, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy, hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, hb_buffer_get_length, hb_buffer_set_direction, hb_buffer_set_script, hb_buffer_t, hb_codepoint_t, hb_face_create_for_tables, hb_face_destroy, hb_face_t, hb_feature_t, hb_font_create, @@ -147,7 +146,7 @@ impl Shaper { let hb_font: *mut hb_font_t = hb_font_create(hb_face); // Set points-per-em. if zero, performs no hinting in that direction. - let pt_size = (*font).actual_pt_size.to_f64_px(); + let pt_size = (*font).descriptor.pt_size.to_f64_px(); hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint); // Set scaling. Note that this takes 16.16 fixed point. diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index 0b36cde4c36..011ba784107 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -31,7 +31,7 @@ pub struct TextRun { /// The UTF-8 string represented by this text run. pub text: Arc, pub font_template: Arc, - pub actual_pt_size: Au, + pub pt_size: Au, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, /// The glyph runs that make up this text run. @@ -195,7 +195,7 @@ impl<'a> TextRun { font_metrics: font.metrics.clone(), font_template: font.handle.template(), font_key: font.font_key, - actual_pt_size: font.actual_pt_size, + pt_size: font.descriptor.pt_size, glyphs: Arc::new(glyphs), bidi_level: bidi_level, extra_word_spacing: Au(0), diff --git a/components/hyper_serde/lib.rs b/components/hyper_serde/lib.rs index 4df1983ea7a..22f79c8ad65 100644 --- a/components/hyper_serde/lib.rs +++ b/components/hyper_serde/lib.rs @@ -138,7 +138,7 @@ impl De { /// Returns a new `De` wrapper #[inline(always)] pub fn new(v: T) -> Self { - De { v: v } + De { v } } } @@ -366,7 +366,7 @@ impl<'de> Deserialize<'de> for De { for v in values.0.iter() { headers.append( HeaderName::from_str(&k).map_err(V::Error::custom)?, - HeaderValue::from_bytes(&v).map_err(V::Error::custom)?, + HeaderValue::from_bytes(v).map_err(V::Error::custom)?, ); } } @@ -453,7 +453,7 @@ impl<'a> Serialize for Ser<'a, HeaderMap> { &Value( &values .iter() - .map(|v| v.as_bytes().iter().cloned().collect()) + .map(|v| v.as_bytes().to_vec()) .collect::>>(), self.pretty, ), @@ -531,7 +531,7 @@ impl<'a> Serialize for Ser<'a, Mime> { where S: Serializer, { - serializer.serialize_str(&self.v.to_string()) + serializer.serialize_str(&self.v.as_ref()) } } diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index 2f4040c9a48..6df0d11c36f 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -26,7 +26,7 @@ html5ever = { workspace = true } ipc-channel = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } msg = { workspace = true } net_traits = { workspace = true } parking_lot = { workspace = true } @@ -37,14 +37,14 @@ script_layout_interface = { workspace = true } script_traits = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms" } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } -size_of_test = { path = "../size_of_test" } +size_of_test = { workspace = true } smallvec = { workspace = true, features = ["union"] } -style = { path = "../style", features = ["servo"] } +style = { workspace = true } style_traits = { workspace = true } unicode-bidi = { workspace = true, features = ["with_serde"] } unicode-script = { workspace = true } diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 753b7747c9f..c813def2142 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -969,12 +969,19 @@ impl Fragment { let display_item = match gradient { Gradient::Linear { ref direction, + ref color_interpolation_method, ref items, ref repeating, compat_mode: _, } => { - let (gradient, stops) = - gradient::linear(style, placement.tile_size, items, *direction, *repeating); + let (gradient, stops) = gradient::linear( + style, + placement.tile_size, + items, + *direction, + color_interpolation_method, + *repeating, + ); let item = webrender_api::GradientDisplayItem { gradient, bounds: placement.bounds.to_f32_px(), @@ -987,6 +994,7 @@ impl Fragment { Gradient::Radial { ref shape, ref position, + ref color_interpolation_method, ref items, ref repeating, compat_mode: _, @@ -997,6 +1005,7 @@ impl Fragment { items, shape, position, + color_interpolation_method, *repeating, ); let item = webrender_api::RadialGradientDisplayItem { @@ -1226,18 +1235,26 @@ impl Fragment { Image::Gradient(ref gradient) => match **gradient { Gradient::Linear { ref direction, + ref color_interpolation_method, ref items, ref repeating, compat_mode: _, } => { - let (wr_gradient, linear_stops) = - gradient::linear(style, border_image_size, items, *direction, *repeating); + let (wr_gradient, linear_stops) = gradient::linear( + style, + border_image_size, + items, + *direction, + color_interpolation_method, + *repeating, + ); stops = linear_stops; NinePatchBorderSource::Gradient(wr_gradient) }, Gradient::Radial { ref shape, ref position, + ref color_interpolation_method, ref items, ref repeating, compat_mode: _, @@ -1248,6 +1265,7 @@ impl Fragment { items, shape, position, + color_interpolation_method, *repeating, ); stops = radial_stops; diff --git a/components/layout/display_list/gradient.rs b/components/layout/display_list/gradient.rs index a161a0a6c95..acc68811786 100644 --- a/components/layout/display_list/gradient.rs +++ b/components/layout/display_list/gradient.rs @@ -4,6 +4,7 @@ use app_units::Au; use euclid::default::{Point2D, Size2D, Vector2D}; +use style::color::mix::ColorInterpolationMethod; use style::properties::ComputedValues; use style::values::computed::image::{EndingShape, LineDirection}; use style::values::computed::{Angle, Color, LengthPercentage, Percentage, Position}; @@ -240,6 +241,7 @@ pub fn linear( size: Size2D, stops: &[GradientItem], direction: LineDirection, + _color_interpolation_method: &ColorInterpolationMethod, repeating: bool, ) -> (Gradient, Vec) { use style::values::specified::position::HorizontalPositionKeyword::*; @@ -307,6 +309,7 @@ pub fn radial( stops: &[GradientItem], shape: &EndingShape, center: &Position, + _color_interpolation_method: &ColorInterpolationMethod, repeating: bool, ) -> (RadialGradient, Vec) { let center = Point2D::new( diff --git a/components/layout/text.rs b/components/layout/text.rs index 4c38313cc64..0c6e8ecc424 100644 --- a/components/layout/text.rs +++ b/components/layout/text.rs @@ -664,11 +664,14 @@ impl RunInfo { } fn has_font(&self, font: &Option) -> bool { - fn identifier(font: &Option) -> Option { - font.as_ref().map(|f| f.borrow().identifier()) + fn identifier_and_pt_size(font: &Option) -> Option<(Atom, Au)> { + font.as_ref().map(|font| { + let font = font.borrow(); + (font.identifier(), font.descriptor.pt_size) + }) } - identifier(&self.font) == identifier(font) + identifier_and_pt_size(&self.font) == identifier_and_pt_size(font) } } diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml index e5b647955b8..9820827150a 100644 --- a/components/layout_2020/Cargo.toml +++ b/components/layout_2020/Cargo.toml @@ -36,10 +36,11 @@ script_layout_interface = { workspace = true } script_traits = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -servo_arc = { path = "../servo_arc" } +servo_arc = { workspace = true } servo_config = { path = "../config" } +servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } -style = { path = "../style", features = ["servo"] } +style = { workspace = true } style_traits = { workspace = true } unicode-script = { workspace = true } unicode-segmentation = { workspace = true } diff --git a/components/layout_2020/display_list/gradient.rs b/components/layout_2020/display_list/gradient.rs index b62f5715cd0..7340a955ecf 100644 --- a/components/layout_2020/display_list/gradient.rs +++ b/components/layout_2020/display_list/gradient.rs @@ -2,6 +2,7 @@ * 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 style::color::mix::ColorInterpolationMethod; use style::properties::ComputedValues; use style::values::computed::image::{EndingShape, Gradient, LineDirection}; use style::values::computed::{Color, Length, LengthPercentage, Position}; @@ -18,12 +19,14 @@ pub(super) fn build( Gradient::Linear { ref items, ref direction, + ref color_interpolation_method, ref repeating, compat_mode: _, } => build_linear( style, items, direction, + color_interpolation_method, if *repeating { wr::ExtendMode::Repeat } else { @@ -35,6 +38,7 @@ pub(super) fn build( Gradient::Radial { ref shape, ref position, + ref color_interpolation_method, ref items, ref repeating, compat_mode: _, @@ -43,6 +47,7 @@ pub(super) fn build( items, shape, position, + color_interpolation_method, if *repeating { wr::ExtendMode::Repeat } else { @@ -60,6 +65,7 @@ pub(super) fn build_linear( style: &ComputedValues, items: &[GradientItem], line_direction: &LineDirection, + _color_interpolation_method: &ColorInterpolationMethod, extend_mode: wr::ExtendMode, layer: &super::background::BackgroundLayer, builder: &mut super::DisplayListBuilder, @@ -161,6 +167,7 @@ pub(super) fn build_radial( items: &[GradientItem], shape: &EndingShape, center: &Position, + _color_interpolation_method: &ColorInterpolationMethod, extend_mode: wr::ExtendMode, layer: &super::background::BackgroundLayer, builder: &mut super::DisplayListBuilder, diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 30ae7ce1725..5902baf63c0 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -13,6 +13,7 @@ use gfx_traits::WebRenderEpochToU16; use msg::constellation_msg::BrowsingContextId; use net_traits::image_cache::UsePlaceholder; use script_traits::compositor::{CompositorDisplayListInfo, ScrollTreeNodeId}; +use servo_geometry::MaxRect; use style::color::{AbsoluteColor, ColorSpace}; use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle; use style::dom::OpaqueNode; @@ -23,6 +24,8 @@ use style::values::specified::text::TextDecorationLine; use style::values::specified::ui::CursorKind; use style_traits::CSSPixel; use webrender_api::{self as wr, units, ClipChainId, ClipId, CommonItemProperties}; +use wr::units::LayoutVector2D; +use wr::BoxShadowClipMode; use crate::context::LayoutContext; use crate::display_list::conversions::ToWebRender; @@ -552,6 +555,7 @@ impl<'a> BuilderForBoxFragment<'a> { } else { self.build_hit_test(builder); self.build_background(builder); + self.build_box_shadow(builder); self.build_border(builder); } } @@ -808,6 +812,42 @@ impl<'a> BuilderForBoxFragment<'a> { .wr() .push_border(&common, outline_rect, widths, details) } + + fn build_box_shadow(&self, builder: &mut DisplayListBuilder<'_>) { + let box_shadows = &self.fragment.style.get_effects().box_shadow.0; + if box_shadows.is_empty() { + return; + } + + // NB: According to CSS-BACKGROUNDS, box shadows render in *reverse* order (front to back). + let border_rect = self.border_rect; + let common = builder.common_properties(MaxRect::max_rect(), &self.fragment.style); + for box_shadow in box_shadows.iter().rev() { + let clip_mode = if box_shadow.inset { + BoxShadowClipMode::Inset + } else { + BoxShadowClipMode::Outset + }; + + builder.wr().push_box_shadow( + &common, + border_rect, + LayoutVector2D::new( + box_shadow.base.horizontal.px(), + box_shadow.base.vertical.px(), + ), + rgba( + self.fragment + .style + .resolve_color(box_shadow.base.color.clone()), + ), + box_shadow.base.blur.px(), + box_shadow.spread.px(), + self.border_radius, + clip_mode, + ); + } + } } fn rgba(color: AbsoluteColor) -> wr::ColorF { diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 1a86058bde7..c379dc451fc 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -410,6 +410,7 @@ impl FlexContainer { IndependentLayout { fragments, content_block_size: content_block_size.into(), + content_inline_size_for_table: None, baselines: Baselines::default(), } } @@ -1108,6 +1109,7 @@ impl<'a> FlexItem<'a> { flex_context.layout_context, &mut positioning_context, &item_as_containing_block, + &flex_context.containing_block, ); let hypothetical_cross_size = self diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 96bdf91ee62..fa40ae1bb22 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -251,6 +251,18 @@ impl<'a> PlacementAmongFloats<'a> { } } + /// After placing a table and then laying it out, it may turn out wider than what + /// we initially expected. This method takes care of updating the data so that + /// the next place() can find the right area for the new size. + /// Note that if the new size is smaller, placement won't backtrack to consider + /// areas that weren't big enough for the old size. + pub(crate) fn set_inline_size(&mut self, inline_size: Au, pbm: &PaddingBorderMargin) { + self.object_size.inline = inline_size; + self.max_inline_end = (self.float_context.containing_block_info.inline_end - + pbm.margin.inline_end.auto_is(Length::zero).into()) + .max(self.min_inline_start + inline_size); + } + /// After placing an object with `height: auto` (and using the minimum inline and /// block size as the object size) and then laying it out, try to fit the object into /// the current set of bands, given block size after layout and the available inline @@ -939,6 +951,7 @@ impl FloatBox { layout_context, positioning_context, &containing_block_for_children, + &containing_block, ); content_size = LogicalVec2 { inline: inline_size, diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 41926944a91..ff8f6c8a02d 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -2,6 +2,72 @@ * 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/. */ +//! # Inline Formatting Context Layout +//! +//! Inline layout is divided into three phases: +//! +//! 1. Box Tree Construction +//! 2. Box to Line Layout +//! 3. Line to Fragment Layout +//! +//! The first phase happens during normal box tree constrution, while the second two phases happen +//! during fragment tree construction (sometimes called just "layout"). +//! +//! ## Box Tree Construction +//! +//! During box tree construction, DOM elements are transformed into a box tree. This phase collects +//! all of the inline boxes, text, atomic inline elements (boxes with `display: inline-block` or +//! `display: inline-table` as well as things like images and canvas), absolutely positioned blocks, +//! and floated blocks. +//! +//! During the last part of this phase, whitespace is collapsed and text is segmented into +//! [`TextRun`]s based on script, chosen font, and line breaking opportunities. In addition, default +//! fonts are selected for every inline box. Each segment of text is shaped using HarfBuzz and +//! turned into a series of glyphs, which all have a size and a position relative to the origin of +//! the [`TextRun`] (calculated in later phases). +//! +//! The code for this phase is mainly in `construct.rs`, but text handling can also be found in +//! `text_runs.rs.` +//! +//! ## Box to Line Layout +//! +//! During the first phase of fragment tree construction, box tree items are laid out into +//! [`LineItem`]s and fragmented based on line boundaries. This is where line breaking happens. This +//! part of layout fragments boxes and their contents across multiple lines while positioning floats +//! and making sure non-floated contents flow around them. In addition, all atomic elements are laid +//! out, which may descend into their respective trees and create fragments. Finally, absolutely +//! positioned content is collected in order to later hoist it to the containing block for +//! absolutes. +//! +//! Note that during this phase, layout does not know the final block position of content. Only +//! during line to fragment layout, are the final block positions calculated based on the line's +//! final content and its vertical alignment. Instead, positions and line heights are calculated +//! relative to the line's final baseline which will be determined in the final phase. +//! +//! [`LineItem`]s represent a particular set of content on a line. Currently this is represented by +//! a linear series of items that describe the line's hierarchy of inline boxes and content. The +//! item types are: +//! +//! - [`LineItem::TextRun`] +//! - [`LineItem::StartInlineBox`] +//! - [`LineItem::EndInlineBox`] +//! - [`LineItem::Atomic`] +//! - [`LineItem::AbsolutelyPositioned`] +//! - [`LineItem::Float`] +//! +//! The code for this can be found by looking for methods of the form `layout_into_line_item()`. +//! +//! ## Line to Fragment Layout +//! +//! During the second phase of fragment tree construction, the final block position of [`LineItem`]s +//! is calculated and they are converted into [`Fragment`]s. After layout, the [`LineItem`]s are +//! discarded and the new fragments are incorporated into the fragment tree. The final static +//! position of absolutely positioned content is calculated and it is hoisted to its containing +//! block via [`PositioningContext`]. +//! +//! The code for this phase, can mainly be found in `line.rs`. +//! + use std::cell::OnceCell; use std::mem; @@ -70,7 +136,7 @@ pub(crate) struct InlineFormattingContext { #[derive(Debug, Serialize)] pub(crate) struct FontKeyAndMetrics { pub key: FontInstanceKey, - pub actual_pt_size: Au, + pub pt_size: Au, pub metrics: FontMetrics, } @@ -1940,24 +2006,32 @@ impl IndependentFormattingContext { layout_context, child_positioning_context.as_mut().unwrap(), &containing_block_for_children, + &ifc.containing_block, ); + let (inline_size, block_size) = + match independent_layout.content_inline_size_for_table { + Some(inline) => (inline, independent_layout.content_block_size), + None => { + // https://drafts.csswg.org/css2/visudet.html#block-root-margin + let tentative_block_size = box_size + .block + .auto_is(|| independent_layout.content_block_size.into()); - // https://drafts.csswg.org/css2/visudet.html#block-root-margin - let tentative_block_size = box_size - .block - .auto_is(|| independent_layout.content_block_size.into()); + // https://drafts.csswg.org/css2/visudet.html#min-max-heights + // In this case “applying the rules above again” with a non-auto block-size + // always results in that size. + let block_size = tentative_block_size + .clamp_between_extremums(min_box_size.block, max_box_size.block); - // https://drafts.csswg.org/css2/visudet.html#min-max-heights - // In this case “applying the rules above again” with a non-auto block-size - // always results in that size. - let block_size = tentative_block_size - .clamp_between_extremums(min_box_size.block, max_box_size.block); + (inline_size.into(), block_size.into()) + }, + }; let content_rect = LogicalRect { start_corner: pbm_sums.start_offset(), size: LogicalVec2 { - block: block_size.into(), - inline: inline_size.into(), + block: block_size, + inline: inline_size, }, }; diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 7fadfa8ce97..4f8afbe741b 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -1,6 +1,7 @@ /* 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/. */ +#![allow(rustdoc::private_intra_doc_links)] //! Flow layout, also known as block-and-inline layout. @@ -243,6 +244,7 @@ impl BlockFormattingContext { flow_layout.collapsible_margins_in_children.end.solve() + clearance.unwrap_or_else(Au::zero).into()) .into(), + content_inline_size_for_table: None, baselines: flow_layout.baselines, } } @@ -621,14 +623,20 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context( mut sequential_layout_state: Option<&mut SequentialLayoutState>, collapsible_with_parent_start_margin: Option, ) -> BoxFragment { - let ContainingBlockPaddingBorderAndMargin { + let ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, min_box_size, max_box_size, + } = solve_containing_block_padding_and_border_for_in_flow_box(containing_block, style); + let ResolvedMargins { margin, effective_margin_inline_start, - } = solve_containing_block_padding_border_and_margin_for_in_flow_box(containing_block, style); + } = solve_margins( + containing_block, + &pbm, + containing_block_for_children.inline_size.into(), + ); let computed_block_size = style.content_block_size(); let start_margin_can_collapse_with_children = @@ -834,14 +842,12 @@ impl NonReplacedFormattingContext { ); } - let ContainingBlockPaddingBorderAndMargin { + let ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, min_box_size, max_box_size, - margin, - effective_margin_inline_start, - } = solve_containing_block_padding_border_and_margin_for_in_flow_box( + } = solve_containing_block_padding_and_border_for_in_flow_box( containing_block, &self.style, ); @@ -850,14 +856,26 @@ impl NonReplacedFormattingContext { layout_context, positioning_context, &containing_block_for_children, + &containing_block, ); - let block_size = containing_block_for_children.block_size.auto_is(|| { - layout.content_block_size.clamp_between_extremums( - min_box_size.block.into(), - max_box_size.block.map(|t| t.into()), - ) - }); + let (block_size, inline_size) = match layout.content_inline_size_for_table { + Some(inline_size) => (layout.content_block_size, inline_size), + None => ( + containing_block_for_children.block_size.auto_is(|| { + layout.content_block_size.clamp_between_extremums( + min_box_size.block.into(), + max_box_size.block.map(|t| t.into()), + ) + }), + containing_block_for_children.inline_size, + ), + }; + + let ResolvedMargins { + margin, + effective_margin_inline_start, + } = solve_margins(containing_block, &pbm, inline_size.into()); let content_rect = LogicalRect { start_corner: LogicalVec2 { @@ -868,7 +886,7 @@ impl NonReplacedFormattingContext { }, size: LogicalVec2 { block: block_size, - inline: containing_block_for_children.inline_size, + inline: inline_size, }, }; @@ -940,20 +958,24 @@ impl NonReplacedFormattingContext { block_size: block_size.map(|t| t.into()), style: &self.style, }, + containing_block, ); - content_size = LogicalVec2 { - inline: inline_size, - block: block_size.auto_is(|| { - layout - .content_block_size - .clamp_between_extremums( - min_box_size.block.into(), - max_box_size.block.map(|t| t.into()), - ) - .into() - }), - }; + if let Some(inline_size) = layout.content_inline_size_for_table { + content_size = LogicalVec2 { + block: layout.content_block_size, + inline: inline_size, + } + .into(); + } else { + content_size = LogicalVec2 { + block: block_size.auto_is(|| { + Length::from(layout.content_block_size) + .clamp_between_extremums(min_box_size.block, max_box_size.block) + }), + inline: inline_size, + }; + } ( clearance, @@ -1013,19 +1035,37 @@ impl NonReplacedFormattingContext { block_size: block_size.map(|t| t.into()), style: &self.style, }, + containing_block, ); - content_size = LogicalVec2 { - inline: proposed_inline_size, - block: block_size.auto_is(|| { - layout - .content_block_size - .clamp_between_extremums( - min_box_size.block.into(), - max_box_size.block.map(|t| t.into()), - ) - .into() - }), - }; + + if let Some(inline_size) = layout.content_inline_size_for_table { + // If this is a table, it's impossible to know the inline size it will take + // up until after trying to place it. If the table doesn't fit into this + // positioning rectangle due to incompatibility in the inline axis, + // then retry at another location. + // Even if it would fit in the inline axis, we may end up having to retry + // at another location due to incompatibility in the block axis. Therefore, + // always update the size in the PlacementAmongFloats as an optimization. + let outer_inline_size = inline_size + pbm.padding_border_sums.inline; + placement.set_inline_size(outer_inline_size, &pbm); + if outer_inline_size > placement_rect.size.inline { + positioning_context.truncate(&positioning_context_length); + continue; + } + content_size = LogicalVec2 { + block: layout.content_block_size, + inline: inline_size, + } + .into(); + } else { + content_size = LogicalVec2 { + block: block_size.auto_is(|| { + Length::from(layout.content_block_size) + .clamp_between_extremums(min_box_size.block, max_box_size.block) + }), + inline: proposed_inline_size, + }; + } // Now we know the block size of this attempted layout of a box with block // size of auto. Try to fit it into our precalculated placement among the @@ -1224,11 +1264,15 @@ fn layout_in_flow_replaced_block_level<'a>( ) } -struct ContainingBlockPaddingBorderAndMargin<'a> { +struct ContainingBlockPaddingAndBorder<'a> { containing_block: ContainingBlock<'a>, pbm: PaddingBorderMargin, min_box_size: LogicalVec2, max_box_size: LogicalVec2>, +} + +struct ResolvedMargins { + /// Used value for the margin properties, as exposed in getComputedStyle(). margin: LogicalSides, /// Distance between the border box and the containing block on the inline-start side. @@ -1240,15 +1284,15 @@ struct ContainingBlockPaddingBorderAndMargin<'a> { effective_margin_inline_start: Length, } -/// Given the style for in in flow box and its containing block, determine the containing -/// block for its children and the margin it should use. +/// Given the style for an in-flow box and its containing block, determine the containing +/// block for its children. /// Note that in the presence of floats, this shouldn't be used for a block-level box -/// that establishes an independent formatting context (or is replaced), since the margins -/// and inline size could then be incorrect. -fn solve_containing_block_padding_border_and_margin_for_in_flow_box<'a>( +/// that establishes an independent formatting context (or is replaced), since the +/// inline size could then be incorrect. +fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( containing_block: &ContainingBlock<'_>, style: &'a Arc, -) -> ContainingBlockPaddingBorderAndMargin<'a> { +) -> ContainingBlockPaddingAndBorder<'a> { let pbm = style.padding_border_margin(containing_block); let box_size = style.content_box_size(containing_block, &pbm); let max_box_size = style.content_max_box_size(containing_block, &pbm); @@ -1269,16 +1313,6 @@ fn solve_containing_block_padding_border_and_margin_for_in_flow_box<'a>( }) .clamp_between_extremums(min_box_size.inline, max_box_size.inline); - let (inline_margins, effective_margin_inline_start) = - solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size); - let block_margins = solve_block_margins_for_in_flow_block_level(&pbm); - let margin = LogicalSides { - inline_start: inline_margins.0, - inline_end: inline_margins.1, - block_start: block_margins.0, - block_end: block_margins.1, - }; - // https://drafts.csswg.org/css2/#the-height-property // https://drafts.csswg.org/css2/visudet.html#min-max-heights let mut block_size = box_size.block; @@ -1296,12 +1330,33 @@ fn solve_containing_block_padding_border_and_margin_for_in_flow_box<'a>( containing_block.style.writing_mode, containing_block_for_children.style.writing_mode, "Mixed writing modes are not supported yet" ); - ContainingBlockPaddingBorderAndMargin { + ContainingBlockPaddingAndBorder { containing_block: containing_block_for_children, pbm, min_box_size, max_box_size, - margin, + } +} + +/// Given the containing block and size of an in-flow box, determine the margins. +/// Note that in the presence of floats, this shouldn't be used for a block-level box +/// that establishes an independent formatting context (or is replaced), since the +/// margins could then be incorrect. +fn solve_margins( + containing_block: &ContainingBlock<'_>, + pbm: &PaddingBorderMargin, + inline_size: Length, +) -> ResolvedMargins { + let (inline_margins, effective_margin_inline_start) = + solve_inline_margins_for_in_flow_block_level(containing_block, &pbm, inline_size); + let block_margins = solve_block_margins_for_in_flow_block_level(&pbm); + ResolvedMargins { + margin: LogicalSides { + inline_start: inline_margins.0, + inline_end: inline_margins.1, + block_start: block_margins.0, + block_end: block_margins.1, + }, effective_margin_inline_start, } } @@ -1347,7 +1402,7 @@ fn justify_self_alignment(containing_block: &ContainingBlock, free_space: Length /// that establishes an independent formatting context (or is replaced). /// /// In addition to the used margins, it also returns the effective margin-inline-start -/// (see ContainingBlockPaddingBorderAndMargin). +/// (see ContainingBlockPaddingAndBorder). fn solve_inline_margins_for_in_flow_block_level( containing_block: &ContainingBlock, pbm: &PaddingBorderMargin, @@ -1384,7 +1439,7 @@ fn solve_inline_margins_for_in_flow_block_level( /// they align within the provided rect (instead of the containing block), /// to avoid overlapping floats. /// In addition to the used margins, it also returns the effective -/// margin-inline-start (see ContainingBlockPaddingBorderAndMargin). +/// margin-inline-start (see ContainingBlockPaddingAndBorder). /// It may differ from the used inline-start margin if the computed value /// wasn't 'auto' and there are floats to avoid or the box is justified. /// See diff --git a/components/layout_2020/flow/text_run.rs b/components/layout_2020/flow/text_run.rs index 16f03b72b84..117cf37b915 100644 --- a/components/layout_2020/flow/text_run.rs +++ b/components/layout_2020/flow/text_run.rs @@ -122,7 +122,7 @@ impl TextRunSegment { let current_font_key_and_metrics = &fonts[self.font_index]; let new_font = font.borrow(); if new_font.font_key != current_font_key_and_metrics.key || - new_font.actual_pt_size != current_font_key_and_metrics.actual_pt_size + new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size { return false; } @@ -489,15 +489,14 @@ fn char_does_not_change_font(character: char) -> bool { pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec) -> usize { let font = font.borrow(); for (index, ifc_font_info) in ifc_fonts.iter().enumerate() { - if ifc_font_info.key == font.font_key && ifc_font_info.actual_pt_size == font.actual_pt_size - { + if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size { return index; } } ifc_fonts.push(FontKeyAndMetrics { metrics: font.metrics.clone(), key: font.font_key, - actual_pt_size: font.actual_pt_size, + pt_size: font.descriptor.pt_size, }); ifc_fonts.len() - 1 } diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index f3dc7644dc5..be481c5ece9 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -73,6 +73,11 @@ pub(crate) struct IndependentLayout { /// pub content_block_size: Au, + /// The contents of a table may force it to become wider than what we would expect + /// from 'width' and 'min-width'. This is the resulting inline content size, + /// or None for non-table layouts. + pub content_inline_size_for_table: Option, + /// The offset of the last inflow baseline of this layout in the content area, if /// there was one. This is used to propagate baselines to the ancestors of `display: /// inline-block`. @@ -187,18 +192,26 @@ impl NonReplacedFormattingContext { &self, layout_context: &LayoutContext, positioning_context: &mut PositioningContext, + containing_block_for_children: &ContainingBlock, containing_block: &ContainingBlock, ) -> IndependentLayout { match &self.contents { - NonReplacedFormattingContextContents::Flow(bfc) => { - bfc.layout(layout_context, positioning_context, containing_block) - }, - NonReplacedFormattingContextContents::Flex(fc) => { - fc.layout(layout_context, positioning_context, containing_block) - }, - NonReplacedFormattingContextContents::Table(table) => { - table.layout(layout_context, positioning_context, containing_block) - }, + NonReplacedFormattingContextContents::Flow(bfc) => bfc.layout( + layout_context, + positioning_context, + containing_block_for_children, + ), + NonReplacedFormattingContextContents::Flex(fc) => fc.layout( + layout_context, + positioning_context, + containing_block_for_children, + ), + NonReplacedFormattingContextContents::Table(table) => table.layout( + layout_context, + positioning_context, + containing_block_for_children, + containing_block, + ), } } diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index b74e802d31d..23964bea93b 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -625,6 +625,7 @@ impl HoistedAbsolutelyPositionedBox { layout_context, &mut positioning_context, &containing_block_for_children, + &containing_block.into(), ); let block_size = size.auto_is(|| independent_layout.content_block_size); Result { @@ -874,17 +875,22 @@ pub(crate) fn relative_adjustement( style: &ComputedValues, containing_block: &ContainingBlock, ) -> LogicalVec2 { - // "If the height of the containing block is not specified explicitly (i.e., - // it depends on content height), and this element is not absolutely - // positioned, the value computes to 'auto'."" - // https://www.w3.org/TR/CSS2/visudet.html#the-height-property + // It's not completely clear what to do with indefinite percentages + // (https://github.com/w3c/csswg-drafts/issues/9353), so we match + // other browsers and treat them as 'auto' offsets. let cbis = containing_block.inline_size; - let cbbs = containing_block.block_size.auto_is(Au::zero); + let cbbs = containing_block.block_size; let box_offsets = style .box_offsets(containing_block) .map_inline_and_block_axes( |v| v.percentage_relative_to(cbis.into()), - |v| v.percentage_relative_to(cbbs.into()), + |v| match cbbs.non_auto() { + Some(cbbs) => v.percentage_relative_to(cbbs.into()), + None => match v.non_auto().and_then(|v| v.to_length()) { + Some(v) => LengthOrAuto::LengthPercentage(v), + None => LengthOrAuto::Auto, + }, + }, ); fn adjust(start: LengthOrAuto, end: LengthOrAuto) -> Length { match (start, end) { diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 9c39abda413..e7e8cdf8667 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -20,18 +20,22 @@ use script_layout_interface::wrapper_traits::{ }; use script_traits::UntrustedNodeAddress; use servo_arc::Arc as ServoArc; +use servo_url::ServoUrl; use style::computed_values::position::T as Position; -use style::context::{StyleContext, ThreadLocalStyleContext}; +use style::context::{QuirksMode, SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use style::dom::{OpaqueNode, TElement}; use style::properties::style_structs::Font; use style::properties::{ - Importance, LonghandId, PropertyDeclarationBlock, PropertyDeclarationId, PropertyId, + parse_one_declaration_into, ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, + PropertyDeclarationId, PropertyId, SourcePropertyDeclaration, }; use style::selector_parser::PseudoElement; +use style::shared_lock::SharedRwLock; +use style::stylesheets::{CssRuleType, Origin, UrlExtraData}; use style::stylist::RuleInclusion; use style::traversal::resolve_style; use style::values::generics::text::LineHeight; -use style_traits::{CSSPixel, ToCss}; +use style_traits::{CSSPixel, ParsingMode, ToCss}; use webrender_api::units::LayoutPixel; use webrender_api::{DisplayListBuilder, ExternalScrollId}; @@ -645,10 +649,107 @@ pub fn process_text_index_request(_node: OpaqueNode, _point: Point2D) -> Tex TextIndexResponse(None) } -pub fn process_resolved_font_style_query<'dom>( - _node: impl LayoutNode<'dom>, - _property: &PropertyId, - _value: &str, -) -> Option> { - None +pub fn process_resolved_font_style_query<'dom, E>( + context: &LayoutContext, + node: E, + property: &PropertyId, + value: &str, + url_data: ServoUrl, + shared_lock: &SharedRwLock, +) -> Option> +where + E: LayoutNode<'dom>, +{ + fn create_font_declaration( + value: &str, + property: &PropertyId, + url_data: &ServoUrl, + quirks_mode: QuirksMode, + ) -> Option { + let mut declarations = SourcePropertyDeclaration::default(); + let result = parse_one_declaration_into( + &mut declarations, + property.clone(), + value, + Origin::Author, + &UrlExtraData(url_data.get_arc()), + None, + ParsingMode::DEFAULT, + quirks_mode, + CssRuleType::Style, + ); + let declarations = match result { + Ok(()) => { + let mut block = PropertyDeclarationBlock::new(); + block.extend(declarations.drain(), Importance::Normal); + block + }, + Err(_) => return None, + }; + // TODO: Force to set line-height property to 'normal' font property. + Some(declarations) + } + fn resolve_for_declarations<'dom, E>( + context: &SharedStyleContext, + parent_style: Option<&ComputedValues>, + declarations: PropertyDeclarationBlock, + shared_lock: &SharedRwLock, + ) -> ServoArc + where + E: LayoutNode<'dom>, + { + let parent_style = match parent_style { + Some(parent) => parent, + None => context.stylist.device().default_computed_values(), + }; + context + .stylist + .compute_for_declarations::( + &context.guards, + parent_style, + ServoArc::new(shared_lock.wrap(declarations)), + ) + } + + // https://html.spec.whatwg.org/multipage/#dom-context-2d-font + // 1. Parse the given font property value + let quirks_mode = context.style_context.quirks_mode(); + let declarations = create_font_declaration(value, property, &url_data, quirks_mode)?; + + // TODO: Reject 'inherit' and 'initial' values for the font property. + + // 2. Get resolved styles for the parent element + let element = node.as_element().unwrap(); + let parent_style = if node.is_connected() { + if element.has_data() { + node.to_threadsafe().as_element().unwrap().resolved_style() + } else { + let mut tlc = ThreadLocalStyleContext::new(); + let mut context = StyleContext { + shared: &context.style_context, + thread_local: &mut tlc, + }; + let styles = resolve_style(&mut context, element, RuleInclusion::All, None, None); + styles.primary().clone() + } + } else { + let default_declarations = + create_font_declaration("10px sans-serif", property, &url_data, quirks_mode).unwrap(); + resolve_for_declarations::( + &context.style_context, + None, + default_declarations, + shared_lock, + ) + }; + + // 3. Resolve the parsed value with resolved styles of the parent element + let computed_values = resolve_for_declarations::( + &context.style_context, + Some(&*parent_style), + declarations, + shared_lock, + ); + + Some(computed_values.clone_font()) } diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs index a5748eb9eb8..20b9192cd3a 100644 --- a/components/layout_2020/sizing.rs +++ b/components/layout_2020/sizing.rs @@ -24,13 +24,6 @@ pub(crate) struct ContentSizes { /// impl ContentSizes { - pub fn zero() -> Self { - Self { - min_content: Au::zero(), - max_content: Au::zero(), - } - } - pub fn map(&self, f: impl Fn(Au) -> Au) -> Self { Self { min_content: f(self.min_content), @@ -57,6 +50,19 @@ impl ContentSizes { } } +impl Zero for ContentSizes { + fn zero() -> Self { + Self { + min_content: Au::zero(), + max_content: Au::zero(), + } + } + + fn is_zero(&self) -> bool { + self.min_content.is_zero() && self.max_content.is_zero() + } +} + impl Add for ContentSizes { type Output = Self; diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs index b4d0428a5a8..b4a5325eaca 100644 --- a/components/layout_2020/style_ext.rs +++ b/components/layout_2020/style_ext.rs @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use app_units::Au; -use servo_config::pref; use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::position::T as ComputedPosition; use style::computed_values::transform_style::T as ComputedTransformStyle; @@ -580,13 +579,12 @@ impl From for Display { let outside = match outside { stylo::DisplayOutside::Block => DisplayOutside::Block, stylo::DisplayOutside::Inline => DisplayOutside::Inline, - stylo::DisplayOutside::TableCaption if pref!(layout.tables.enabled) => { + stylo::DisplayOutside::TableCaption => { return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal( DisplayLayoutInternal::TableCaption, )); }, - stylo::DisplayOutside::TableCaption => DisplayOutside::Block, - stylo::DisplayOutside::InternalTable if pref!(layout.tables.enabled) => { + stylo::DisplayOutside::InternalTable => { let internal = match inside { stylo::DisplayInside::TableRowGroup => DisplayLayoutInternal::TableRowGroup, stylo::DisplayInside::TableColumn => DisplayLayoutInternal::TableColumn, @@ -605,7 +603,6 @@ impl From for Display { }; return Display::GeneratingBox(DisplayGeneratingBox::LayoutInternal(internal)); }, - stylo::DisplayOutside::InternalTable => DisplayOutside::Block, // This should not be a value of DisplayInside, but oh well // special-case display: contents because we still want it to work despite the early return stylo::DisplayOutside::None if inside == stylo::DisplayInside::Contents => { @@ -627,17 +624,14 @@ impl From for Display { stylo::DisplayInside::None => return Display::None, stylo::DisplayInside::Contents => return Display::Contents, - stylo::DisplayInside::Table if pref!(layout.tables.enabled) => DisplayInside::Table, - stylo::DisplayInside::Table | + stylo::DisplayInside::Table => DisplayInside::Table, stylo::DisplayInside::TableRowGroup | stylo::DisplayInside::TableColumn | stylo::DisplayInside::TableColumnGroup | stylo::DisplayInside::TableHeaderGroup | stylo::DisplayInside::TableFooterGroup | stylo::DisplayInside::TableRow | - stylo::DisplayInside::TableCell => DisplayInside::Flow { - is_list_item: packed.is_list_item(), - }, + stylo::DisplayInside::TableCell => unreachable!("Internal DisplayInside found"), }; Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { outside, inside }) } diff --git a/components/layout_2020/table/construct.rs b/components/layout_2020/table/construct.rs index 5bde80ee67e..fc9d3c3406b 100644 --- a/components/layout_2020/table/construct.rs +++ b/components/layout_2020/table/construct.rs @@ -63,8 +63,9 @@ impl Table { contents: NonReplacedContents, propagated_text_decoration_line: TextDecorationLine, ) -> Self { - let mut traversal = - TableBuilderTraversal::new(context, info, propagated_text_decoration_line); + let text_decoration_line = + propagated_text_decoration_line | info.style.clone_text_decoration_line(); + let mut traversal = TableBuilderTraversal::new(context, info, text_decoration_line); contents.traverse(context, info, &mut traversal); traversal.finish() } @@ -589,9 +590,9 @@ pub(crate) struct TableBuilderTraversal<'style, 'dom, Node> { context: &'style LayoutContext<'style>, info: &'style NodeAndStyleInfo, - /// Propagated value for text-decoration-line, used to construct the block - /// contents of table cells. - propagated_text_decoration_line: TextDecorationLine, + /// The value of the [`TextDecorationLine`] to use, either for the row group + /// if processing one or for the table itself if outside a row group. + current_text_decoration_line: TextDecorationLine, /// The [`TableBuilder`] for this [`TableBuilderTraversal`]. This is separated /// into another struct so that we can write unit tests against the builder. @@ -610,12 +611,12 @@ where pub(crate) fn new( context: &'style LayoutContext<'style>, info: &'style NodeAndStyleInfo, - propagated_text_decoration_line: TextDecorationLine, + text_decoration_line: TextDecorationLine, ) -> Self { TableBuilderTraversal { context, info, - propagated_text_decoration_line, + current_text_decoration_line: text_decoration_line, builder: TableBuilder::new(info.style.clone()), current_anonymous_row_content: Vec::new(), current_row_group_index: None, @@ -644,7 +645,8 @@ where &self.info.style, ); let anonymous_info = self.info.new_replacing_style(anonymous_style); - let mut row_builder = TableRowBuilder::new(self, &anonymous_info); + let mut row_builder = + TableRowBuilder::new(self, &anonymous_info, self.current_text_decoration_line); for cell_content in row_content { match cell_content { @@ -703,8 +705,13 @@ where track_range: next_row_index..next_row_index, }); + let previous_text_decoration_line = self.current_text_decoration_line; + self.current_text_decoration_line = + self.current_text_decoration_line | info.style.clone_text_decoration_line(); + let new_row_group_index = self.builder.table.row_groups.len() - 1; self.current_row_group_index = Some(new_row_group_index); + NonReplacedContents::try_from(contents).unwrap().traverse( self.context, info, @@ -712,6 +719,7 @@ where ); self.current_row_group_index = None; + self.current_text_decoration_line = previous_text_decoration_line; self.builder.incoming_rowspans.clear(); // We are doing this until we have actually set a Box for this `BoxSlot`. @@ -722,7 +730,8 @@ where let context = self.context; - let mut row_builder = TableRowBuilder::new(self, info); + let mut row_builder = + TableRowBuilder::new(self, info, self.current_text_decoration_line); NonReplacedContents::try_from(contents).unwrap().traverse( context, info, @@ -842,6 +851,9 @@ struct TableRowBuilder<'style, 'builder, 'dom, 'a, Node> { info: &'a NodeAndStyleInfo, current_anonymous_cell_content: Vec>, + + /// The [`TextDecorationLine`] to use for all children of this row. + text_decoration_line: TextDecorationLine, } impl<'style, 'builder, 'dom, 'a, Node: 'dom> TableRowBuilder<'style, 'builder, 'dom, 'a, Node> @@ -851,13 +863,17 @@ where fn new( table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom, Node>, info: &'a NodeAndStyleInfo, + propagated_text_decoration_line: TextDecorationLine, ) -> Self { table_traversal.builder.start_row(); + let text_decoration_line = + propagated_text_decoration_line | info.style.clone_text_decoration_line(); TableRowBuilder { table_traversal, info, current_anonymous_cell_content: Vec::new(), + text_decoration_line, } } @@ -881,11 +897,8 @@ where &self.info.style, ); let anonymous_info = self.info.new_replacing_style(anonymous_style); - let mut builder = BlockContainerBuilder::new( - context, - &anonymous_info, - self.table_traversal.propagated_text_decoration_line, - ); + let mut builder = + BlockContainerBuilder::new(context, &anonymous_info, self.text_decoration_line); for cell_content in self.current_anonymous_cell_content.drain(..) { match cell_content { @@ -964,7 +977,7 @@ where self.table_traversal.context, info, non_replaced_contents, - self.table_traversal.propagated_text_decoration_line, + self.text_decoration_line, false, /* is_list_item */ ) }, diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 61be299c695..05501724c5e 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -2,6 +2,8 @@ * 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::ops::Range; + use app_units::{Au, MAX_AU}; use log::warn; use servo_arc::Arc; @@ -52,36 +54,57 @@ impl CellLayout { } } +/// Information stored during the layout of rows. +#[derive(Clone, Debug, Default)] +struct RowLayout { + constrained: bool, + has_cell_with_span_greater_than_one: bool, + percent: Percentage, +} + +/// Information stored during the layout of columns. +#[derive(Clone, Debug, Default)] +struct ColumnLayout { + constrained: bool, + has_originating_cells: bool, +} + /// A helper struct that performs the layout of the box tree version /// of a table into the fragment tree version. This implements /// struct TableLayout<'a> { table: &'a Table, pbm: PaddingBorderMargin, - column_constrainedness: Vec, - column_has_originating_cell: Vec, - cell_measures: Vec>, + rows: Vec, + columns: Vec, + cell_measures: Vec>>, assignable_width: Au, - column_measures: Vec, + final_table_height: Au, + column_measures: Vec, distributed_column_widths: Vec, row_sizes: Vec, row_baselines: Vec, cells_laid_out: Vec>>, + basis_for_cell_padding_percentage: Au, } #[derive(Clone, Debug)] -struct CellOrColumnMeasure { +struct CellOrTrackMeasure { content_sizes: ContentSizes, - percentage_width: Percentage, + percentage: Percentage, } -impl CellOrColumnMeasure { +impl Zero for CellOrTrackMeasure { fn zero() -> Self { Self { content_sizes: ContentSizes::zero(), - percentage_width: Percentage(0.), + percentage: Percentage(0.), } } + + fn is_zero(&self) -> bool { + self.content_sizes.is_zero() && self.percentage.is_zero() + } } impl<'a> TableLayout<'a> { @@ -89,47 +112,31 @@ impl<'a> TableLayout<'a> { Self { table, pbm: PaddingBorderMargin::zero(), - column_constrainedness: Vec::new(), - column_has_originating_cell: Vec::new(), + rows: Vec::new(), + columns: Vec::new(), cell_measures: Vec::new(), assignable_width: Au::zero(), + final_table_height: Au::zero(), column_measures: Vec::new(), distributed_column_widths: Vec::new(), row_sizes: Vec::new(), row_baselines: Vec::new(), cells_laid_out: Vec::new(), + basis_for_cell_padding_percentage: Au::zero(), } } - /// Do the preparatory steps to table layout, measuring cells and distributing sizes - /// to all columns and rows. - fn compute_measures( - &mut self, - layout_context: &LayoutContext, - positioning_context: &mut PositioningContext, - containing_block: &ContainingBlock, - ) { - let writing_mode = containing_block.style.writing_mode; - self.compute_column_constrainedness_and_has_originating_cells(writing_mode); - self.compute_cell_measures(layout_context, containing_block); - self.compute_column_measures(containing_block.style.writing_mode); - self.compute_table_width(containing_block); - self.distributed_column_widths = self.distribute_width_to_columns(); - self.do_row_layout_first_pass(layout_context, containing_block, positioning_context); - self.distribute_height_to_rows(); - } - /// This is an implementation of *Computing Cell Measures* from /// . pub(crate) fn compute_cell_measures( &mut self, layout_context: &LayoutContext, - containing_block: &ContainingBlock, + writing_mode: WritingMode, ) { - let writing_mode = containing_block.style.writing_mode; - for row_index in 0..self.table.size.height { - let mut row_measures = vec![CellOrColumnMeasure::zero(); self.table.size.width]; + let row_measures = vec![LogicalVec2::zero(); self.table.size.width]; + self.cell_measures = vec![row_measures; self.table.size.height]; + for row_index in 0..self.table.size.height { for column_index in 0..self.table.size.width { let cell = match self.table.slots[row_index][column_index] { TableSlot::Cell(ref cell) => cell, @@ -137,50 +144,92 @@ impl<'a> TableLayout<'a> { }; let (size, min_size, max_size) = get_sizes_from_style(&cell.style, writing_mode); - let content_sizes = cell + let mut inline_content_sizes = cell .contents .contents .inline_content_sizes(layout_context, writing_mode); + + // TODO: the max-content size should never be smaller than the min-content size! + inline_content_sizes.max_content = inline_content_sizes + .max_content + .max(inline_content_sizes.min_content); + let percentage_contribution = get_size_percentage_contribution_from_style(&cell.style, writing_mode); - // > The outer min-content width of a table-cell is max(min-width, min-content width) - // > adjusted by the cell intrinsic offsets. - let mut outer_min_content_width = content_sizes.min_content.max(min_size.inline); - let mut outer_max_content_width = if !self.column_constrainedness[column_index] { - // > The outer max-content width of a table-cell in a non-constrained column is - // > max(min-width, width, min-content width, min(max-width, max-content width)) - // > adjusted by the cell intrinsic offsets. - min_size - .inline + // These formulas differ from the spec, but seem to match Gecko and Blink. + let mut outer_min_content_width = inline_content_sizes + .min_content + .min(max_size.inline) + .max(min_size.inline); + let mut outer_max_content_width = if self.columns[column_index].constrained { + inline_content_sizes + .min_content .max(size.inline) - .max(content_sizes.min_content) - .max(max_size.inline.min(content_sizes.max_content)) + .min(max_size.inline) + .max(min_size.inline) } else { - // > The outer max-content width of a table-cell in a constrained column is - // > max(min-width, width, min-content width, min(max-width, width)) adjusted by the - // > cell intrinsic offsets. - min_size - .inline + inline_content_sizes + .max_content .max(size.inline) - .max(content_sizes.min_content) - .max(max_size.inline.min(size.inline)) + .min(max_size.inline) + .max(min_size.inline) }; + assert!(outer_min_content_width <= outer_max_content_width); - let pbm = cell.style.padding_border_margin(containing_block); - outer_min_content_width += pbm.padding_border_sums.inline; - outer_max_content_width += pbm.padding_border_sums.inline; + let padding = cell + .style + .padding(writing_mode) + .percentages_relative_to(Length::zero()); + let border = cell.style.border_width(writing_mode); - row_measures[column_index] = CellOrColumnMeasure { + let inline_padding_border_sum = + Au::from(padding.inline_sum() + border.inline_sum()); + outer_min_content_width += inline_padding_border_sum; + outer_max_content_width += inline_padding_border_sum; + + let inline_measure = CellOrTrackMeasure { content_sizes: ContentSizes { min_content: outer_min_content_width, max_content: outer_max_content_width, }, - percentage_width: percentage_contribution.inline, + percentage: percentage_contribution.inline, + }; + + // These calculations do not take into account the `min-content` and `max-content` + // sizes. These sizes are incorporated after the first row layout pass, when the + // block size of the layout is known. + // + // TODO: Is it correct to use the block size as the minimum of the `outer min + // content height` here? The specification doesn't mention this, but it does cause + // a test to pass. + let mut outer_min_content_height = min_size.block.max(size.block); + let mut outer_max_content_height = if !self.rows[row_index].constrained { + min_size.block.max(size.block) + } else { + min_size + .block + .max(size.block) + .max(max_size.block.min(size.block)) + }; + + let block_padding_border_sum = Au::from(padding.block_sum() + border.block_sum()); + outer_min_content_height += block_padding_border_sum; + outer_max_content_height += block_padding_border_sum; + + let block_measure = CellOrTrackMeasure { + content_sizes: ContentSizes { + min_content: outer_min_content_height, + max_content: outer_max_content_height, + }, + percentage: percentage_contribution.block, + }; + + self.cell_measures[row_index][column_index] = LogicalVec2 { + inline: inline_measure, + block: block_measure, }; } - - self.cell_measures.push(row_measures); } } @@ -189,14 +238,48 @@ impl<'a> TableLayout<'a> { /// > A column is constrained if its corresponding table-column-group (if any), its /// > corresponding table-column (if any), or any of the cells spanning only that /// > column has a computed width that is not "auto", and is not a percentage. - fn compute_column_constrainedness_and_has_originating_cells( + fn compute_track_constrainedness_and_has_originating_cells( &mut self, writing_mode: WritingMode, ) { - for column_index in 0..self.table.size.width { - let mut column_constrained = false; - let mut column_has_originating_cell = false; + self.rows = vec![RowLayout::default(); self.table.size.height]; + self.columns = vec![ColumnLayout::default(); self.table.size.width]; + for column_index in 0..self.table.size.width { + if let Some(column) = self.table.columns.get(column_index) { + if !column.style.box_size(writing_mode).inline.is_auto() { + self.columns[column_index].constrained = true; + continue; + } + if let Some(column_group_index) = column.group_index { + let column_group = &self.table.column_groups[column_group_index]; + if !column_group.style.box_size(writing_mode).inline.is_auto() { + self.columns[column_index].constrained = true; + continue; + } + } + self.columns[column_index].constrained = false; + } + } + + for row_index in 0..self.table.size.height { + if let Some(row) = self.table.rows.get(row_index) { + if !row.style.box_size(writing_mode).block.is_auto() { + self.rows[row_index].constrained = true; + continue; + } + if let Some(row_group_index) = row.group_index { + let row_group = &self.table.row_groups[row_group_index]; + if !row_group.style.box_size(writing_mode).block.is_auto() { + self.rows[row_index].constrained = true; + continue; + } + } + } + self.rows[row_index].constrained = false; + } + + for column_index in 0..self.table.size.width { for row_index in 0..self.table.size.height { let coords = TableSlotCoordinates::new(column_index, row_index); let cell_constrained = match self.table.resolve_first_cell(coords) { @@ -205,17 +288,24 @@ impl<'a> TableLayout<'a> { .box_size(writing_mode) .inline .non_auto() - .map(|length_percentage| length_percentage.to_length().is_some()) - .unwrap_or(false), + .and_then(|length_percentage| length_percentage.to_length()) + .is_some(), _ => false, }; - column_has_originating_cell = column_has_originating_cell || + + let rowspan_greater_than_1 = match self.table.slots[row_index][column_index] { + TableSlot::Cell(ref cell) => cell.rowspan > 1, + _ => false, + }; + + self.rows[row_index].has_cell_with_span_greater_than_one |= rowspan_greater_than_1; + self.rows[row_index].constrained |= cell_constrained; + + let has_originating_cell = matches!(self.table.get_slot(coords), Some(TableSlot::Cell(_))); - column_constrained = column_constrained || cell_constrained; + self.columns[column_index].has_originating_cells |= has_originating_cell; + self.columns[column_index].constrained |= cell_constrained; } - self.column_constrainedness.push(column_constrained); - self.column_has_originating_cell - .push(column_has_originating_cell); } } @@ -273,16 +363,12 @@ impl<'a> TableLayout<'a> { // This takes the max of `min_content`, `max_content`, and // intrinsic percentage width as described above. - let cell_measure = &self.cell_measures[row_index][column_index]; + let cell_measure = &self.cell_measures[row_index][column_index].inline; column_measure .content_sizes .max_assign(cell_measure.content_sizes); - column_measure.percentage_width = Percentage( - column_measure - .percentage_width - .0 - .max(cell_measure.percentage_width.0), - ); + column_measure.percentage = + Percentage(column_measure.percentage.0.max(cell_measure.percentage.0)); } column_measures.push(column_measure); @@ -306,11 +392,11 @@ impl<'a> TableLayout<'a> { for column_index in 0..self.table.size.width { let column_measure = &mut column_measures[column_index]; let final_intrinsic_percentage_width = column_measure - .percentage_width + .percentage .0 - .min(100. - total_intrinsic_percentage_width); + .min(1. - total_intrinsic_percentage_width); total_intrinsic_percentage_width += final_intrinsic_percentage_width; - column_measure.percentage_width = Percentage(final_intrinsic_percentage_width); + column_measure.percentage = Percentage(final_intrinsic_percentage_width); } self.column_measures = column_measures; @@ -319,8 +405,8 @@ impl<'a> TableLayout<'a> { fn compute_content_sizes_for_columns_with_span_up_to_n( &self, n: usize, - old_column_measures: &[CellOrColumnMeasure], - ) -> (usize, Vec) { + old_column_measures: &[CellOrTrackMeasure], + ) -> (usize, Vec) { let mut next_span_n = usize::MAX; let mut new_content_sizes_for_columns = Vec::new(); let border_spacing = self.table.border_spacing(); @@ -346,7 +432,8 @@ impl<'a> TableLayout<'a> { _ => continue, }; - let cell_measures = &self.cell_measures[resolved_coords.y][resolved_coords.x]; + let cell_measures = + &self.cell_measures[resolved_coords.y][resolved_coords.x].inline; let cell_inline_content_sizes = cell_measures.content_sizes; let columns_spanned = resolved_coords.x..resolved_coords.x + cell.colspan; @@ -477,9 +564,7 @@ impl<'a> TableLayout<'a> { // > Otherwise, it is the largest of the contributions of the cells in the column // > whose colSpan is N, where the contribution of a cell is the result of taking // > the following steps: - if old_column_measure.percentage_width.0 <= 0. && - cell_measures.percentage_width.0 != 0. - { + if old_column_measure.percentage.0 <= 0. && cell_measures.percentage.0 != 0. { // > 1. Start with the percentage contribution of the cell. // > 2. Subtract the intrinsic percentage width of the column based on cells // > of span up to N-1 of all columns that the cell spans. If this gives a @@ -488,13 +573,13 @@ impl<'a> TableLayout<'a> { let other_column_percentages_sum = (columns_spanned).fold(0., |sum, spanned_column_index| { let spanned_column_percentage = - old_column_measures[spanned_column_index].percentage_width; + old_column_measures[spanned_column_index].percentage; if spanned_column_percentage.0 == 0. { spanned_columns_with_zero += 1; } sum + spanned_column_percentage.0 }); - let step_2 = (cell_measures.percentage_width - + let step_2 = (cell_measures.percentage - Percentage(other_column_percentages_sum)) .clamp_to_non_negative(); @@ -513,71 +598,96 @@ impl<'a> TableLayout<'a> { Percentage(new_column_intrinsic_percentage_width.0.max(step_3)); } } - new_content_sizes_for_columns.push(CellOrColumnMeasure { + new_content_sizes_for_columns.push(CellOrTrackMeasure { content_sizes: new_column_content_sizes, - percentage_width: new_column_intrinsic_percentage_width, + percentage: new_column_intrinsic_percentage_width, }); } (next_span_n, new_content_sizes_for_columns) } - fn compute_table_width(&mut self, containing_block: &ContainingBlock) { + /// Compute the GRIDMIN and GRIDMAX. + fn compute_grid_min_max( + &mut self, + layout_context: &LayoutContext, + writing_mode: WritingMode, + ) -> ContentSizes { + self.compute_track_constrainedness_and_has_originating_cells(writing_mode); + self.compute_cell_measures(layout_context, writing_mode); + self.compute_column_measures(writing_mode); + // https://drafts.csswg.org/css-tables/#gridmin: // > The row/column-grid width minimum (GRIDMIN) width is the sum of the min-content width of // > all the columns plus cell spacing or borders. // https://drafts.csswg.org/css-tables/#gridmax: // > The row/column-grid width maximum (GRIDMAX) width is the sum of the max-content width of // > all the columns plus cell spacing or borders. - - let mut grid_min_and_max = self + let mut grid_min_max = self .column_measures .iter() .fold(ContentSizes::zero(), |result, measure| { result + measure.content_sizes }); - let border_spacing = self.table.border_spacing(); - let inline_border_spacing = border_spacing.inline * (self.table.size.width as i32 + 1); - grid_min_and_max.min_content += inline_border_spacing; - grid_min_and_max.max_content += inline_border_spacing; - self.pbm = self.table.style.padding_border_margin(containing_block); - let content_box_size = self - .table - .style - .content_box_size(containing_block, &self.pbm); - let min_content_sizes = self - .table - .style - .content_min_box_size(containing_block, &self.pbm) - .auto_is(Length::zero); + // TODO: GRIDMAX should never be smaller than GRIDMIN! + grid_min_max.max_content = grid_min_max.max_content.max(grid_min_max.min_content); - // https://drafts.csswg.org/css-tables/#used-min-width-of-table - // > The used min-width of a table is the greater of the resolved min-width, CAPMIN, and GRIDMIN. - let used_min_width_of_table = grid_min_and_max - .min_content - .max(min_content_sizes.inline.into()); + let inline_border_spacing = self.table.total_border_spacing().inline; + grid_min_max.min_content += inline_border_spacing; + grid_min_max.max_content += inline_border_spacing; + grid_min_max + } + + fn compute_table_width( + &mut self, + containing_block_for_children: &ContainingBlock, + containing_block_for_table: &ContainingBlock, + grid_min_max: ContentSizes, + ) { + let style = &self.table.style; + self.pbm = style.padding_border_margin(containing_block_for_table); + + // https://drafts.csswg.org/css-tables/#resolved-table-width + // * If inline-size computes to 'auto', this is the stretch-fit size + // (https://drafts.csswg.org/css-sizing-3/#stretch-fit-size). + // * Otherwise, it's the resulting length (with percentages resolved). + // In both cases, it's clamped between min-inline-size and max-inline-size. + // This diverges a little from the specification. + let resolved_table_width = containing_block_for_children.inline_size; // https://drafts.csswg.org/css-tables/#used-width-of-table - // > The used width of a table depends on the columns and captions widths as follows: - // > * If the table-root’s width property has a computed value (resolving to - // > resolved-table-width) other than auto, the used width is the greater of - // > resolved-table-width, and the used min-width of the table. - // > * If the table-root has 'width: auto', the used width is the greater of min(GRIDMAX, - // > the table’s containing block width), the used min-width of the table. - let used_width_of_table = match content_box_size.inline { - LengthPercentage(length_percentage) => { - Au::from(length_percentage).max(used_min_width_of_table) + // * If table-root has a computed value for inline-size different than auto: + // use the maximum of the resolved table width and GRIDMIN. + // * If auto: use the resolved_table_width, clamped between GRIDMIN and GRIDMAX, + // but at least as big as min-inline-size. + // This diverges a little from the specification, but should be equivalent + // (other than using the stretch-fit size instead of the containing block width). + let used_width_of_table = match style + .content_box_size(containing_block_for_table, &self.pbm) + .inline + { + LengthPercentage(_) => resolved_table_width.max(grid_min_max.min_content), + Auto => { + let min_width: Au = style + .content_min_box_size(containing_block_for_table, &self.pbm) + .inline + .auto_is(Length::zero) + .into(); + resolved_table_width + .clamp(grid_min_max.min_content, grid_min_max.max_content) + .max(min_width) }, - Auto => grid_min_and_max - .max_content - .min(containing_block.inline_size) - .max(used_min_width_of_table), }; // > The assignable table width is the used width of the table minus the total horizontal // > border spacing (if any). This is the width that we will be able to allocate to the // > columns. - self.assignable_width = used_width_of_table - inline_border_spacing; + self.assignable_width = used_width_of_table - self.table.total_border_spacing().inline; + + // This is the amount that we will use to resolve percentages in the padding of cells. + // It matches what Gecko and Blink do, though they disagree when there is a big caption. + self.basis_for_cell_padding_percentage = + used_width_of_table - self.table.border_spacing().inline * 2; } /// Distribute width to columns, performing step 2.4 of table layout from @@ -624,16 +734,14 @@ impl<'a> TableLayout<'a> { let column_measure = &self.column_measures[column_idx]; let min_content_width = column_measure.content_sizes.min_content; let max_content_width = column_measure.content_sizes.max_content; - let constrained = self.column_constrainedness[column_idx]; + let constrained = self.columns[column_idx].constrained; let ( min_content_percentage_sizing_guess, min_content_specified_sizing_guess, max_content_sizing_guess, - ) = if !column_measure.percentage_width.is_zero() { - let resolved = self - .assignable_width - .scale_by(column_measure.percentage_width.0); + ) = if !column_measure.percentage.is_zero() { + let resolved = self.assignable_width.scale_by(column_measure.percentage.0); let percent_guess = min_content_width.max(resolved); (percent_guess, percent_guess, percent_guess) } else if constrained { @@ -734,11 +842,11 @@ impl<'a> TableLayout<'a> { let extra_inline_size = self.assignable_width - column_sizes_sum; let has_originating_cells = - |column_index: &usize| self.column_has_originating_cell[*column_index]; - let is_constrained = |column_index: &usize| self.column_constrainedness[*column_index]; + |column_index: &usize| self.columns[*column_index].has_originating_cells; + let is_constrained = |column_index: &usize| self.columns[*column_index].constrained; let is_unconstrained = |column_index: &usize| !is_constrained(column_index); let has_percent_greater_than_zero = - |column_index: &usize| self.column_measures[*column_index].percentage_width.0 > 0.; + |column_index: &usize| self.column_measures[*column_index].percentage.0 > 0.; let has_percent_zero = |column_index: &usize| !has_percent_greater_than_zero(column_index); let has_max_content = |column_index: &usize| { self.column_measures[*column_index] @@ -834,13 +942,12 @@ impl<'a> TableLayout<'a> { let columns_with_percentage = all_columns.clone().filter(has_percent_greater_than_zero); let total_percent = columns_with_percentage .clone() - .map(|column_index| self.column_measures[column_index].percentage_width.0) + .map(|column_index| self.column_measures[column_index].percentage.0) .sum::(); if total_percent > 0. { for column_index in columns_with_percentage { - column_sizes[column_index] += extra_inline_size.scale_by( - self.column_measures[column_index].percentage_width.0 / total_percent, - ); + column_sizes[column_index] += extra_inline_size + .scale_by(self.column_measures[column_index].percentage.0 / total_percent); } return; } @@ -870,10 +977,10 @@ impl<'a> TableLayout<'a> { /// This is an implementation of *Row layout (first pass)* from /// . - fn do_row_layout_first_pass( + fn layout_cells_in_row( &mut self, layout_context: &LayoutContext, - containing_block: &ContainingBlock, + containing_block_for_table: &ContainingBlock, parent_positioning_context: &mut PositioningContext, ) { for row_index in 0..self.table.slots.len() { @@ -893,11 +1000,13 @@ impl<'a> TableLayout<'a> { total_width += self.distributed_column_widths[width_index]; } - let border = cell.style.border_width(containing_block.style.writing_mode); + let border = cell + .style + .border_width(containing_block_for_table.style.writing_mode); let padding = cell .style - .padding(containing_block.style.writing_mode) - .percentages_relative_to(Length::zero()); + .padding(containing_block_for_table.style.writing_mode) + .percentages_relative_to(self.basis_for_cell_padding_percentage.into()); let inline_border_padding_sum = border.inline_sum() + padding.inline_sum(); let mut total_width: CSSPixelLength = Length::from(total_width) - inline_border_padding_sum; @@ -918,91 +1027,387 @@ impl<'a> TableLayout<'a> { &mut positioning_context, &containing_block_for_children, ); + + let content_size_from_layout = ContentSizes { + min_content: layout.content_block_size, + max_content: layout.content_block_size, + }; + self.cell_measures[row_index][column_index] + .block + .content_sizes + .max_assign(content_size_from_layout); + cells_laid_out_row.push(Some(CellLayout { layout, padding, border, positioning_context, - })) + })); } self.cells_laid_out.push(cells_laid_out_row); } } - fn distribute_height_to_rows(&mut self) { + /// Do the first layout of a table row, after laying out the cells themselves. This is + /// more or less and implementation of . + fn do_first_row_layout(&mut self, writing_mode: WritingMode) -> Vec { + let mut row_sizes = (0..self.table.size.height) + .map(|row_index| { + let (mut max_ascent, mut max_descent, mut max_row_height) = + (Au::zero(), Au::zero(), Au::zero()); + + for column_index in 0..self.table.size.width { + let cell = match self.table.slots[row_index][column_index] { + TableSlot::Cell(ref cell) => cell, + _ => continue, + }; + + let layout = match self.cells_laid_out[row_index][column_index] { + Some(ref layout) => layout, + None => { + warn!( + "Did not find a layout at a slot index with an originating cell." + ); + continue; + }, + }; + + let outer_block_size = layout.outer_block_size(); + if cell.rowspan == 1 { + max_row_height = max_row_height.max(outer_block_size); + } + + if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline { + let ascent = layout.ascent(); + let border_padding_start = + layout.border.block_start + layout.padding.block_start; + let border_padding_end = layout.border.block_end + layout.padding.block_end; + max_ascent = max_ascent.max(ascent + border_padding_start.into()); + + // Only take into account the descent of this cell if doesn't span + // rows. The descent portion of the cell in cells that do span rows + // may extend into other rows. + if cell.rowspan == 1 { + max_descent = max_descent.max( + layout.layout.content_block_size - ascent + + border_padding_end.into(), + ); + } + } + } + self.row_baselines.push(max_ascent); + max_row_height.max(max_ascent + max_descent) + }) + .collect(); + self.calculate_row_sizes_after_first_layout(&mut row_sizes, writing_mode); + row_sizes + } + + /// After doing layout of table rows, calculate final row size and distribute space across + /// rowspanned cells. This follows the implementation of LayoutNG and the priority + /// agorithm described at . + fn calculate_row_sizes_after_first_layout( + &mut self, + row_sizes: &mut Vec, + writing_mode: WritingMode, + ) { + let mut cells_to_distribute = Vec::new(); + let mut total_percentage = 0.; for row_index in 0..self.table.size.height { - let (mut max_ascent, mut max_descent, mut max_row_height) = - (Au::zero(), Au::zero(), Au::zero()); + let row_measure = self + .table + .get_row_measure_for_row_at_index(writing_mode, row_index); + row_sizes[row_index] = row_sizes[row_index].max(row_measure.content_sizes.min_content); + let mut percentage = match self.table.rows.get(row_index) { + Some(row) => { + get_size_percentage_contribution_from_style(&row.style, writing_mode) + .block + .0 + }, + None => 0., + }; for column_index in 0..self.table.size.width { - let coords = TableSlotCoordinates::new(column_index, row_index); + let cell_percentage = self.cell_measures[row_index][column_index] + .block + .percentage + .0; + percentage = percentage.max(cell_percentage); + let cell_measure = &self.cell_measures[row_index][column_index].block; let cell = match self.table.slots[row_index][column_index] { - TableSlot::Cell(ref cell) => cell, - TableSlot::Spanned(ref spanned_cells) if spanned_cells[0].y != 0 => { - let offset = spanned_cells[0]; - let origin = coords - offset; - - // We only allocate the remaining space for the last row of the rowspanned cell. - if let Some(TableSlot::Cell(origin_cell)) = self.table.get_slot(origin) { - if origin_cell.rowspan != offset.y + 1 { - continue; - } - } - - // This is all of the rows that are spanned except this one. - let used_block_size = (origin.y..coords.y) - .map(|row_index| self.row_sizes[row_index]) - .fold(Au::zero(), |sum, size| sum + size); - if let Some(layout) = &self.cells_laid_out[origin.y][origin.x] { - max_row_height = - max_row_height.max(layout.outer_block_size() - used_block_size); - } + TableSlot::Cell(ref cell) if cell.rowspan > 1 => cell, + TableSlot::Cell(_) => { + // If this is an originating cell, that isn't spanning, then we make sure the row is + // at least big enough to hold the cell. + row_sizes[row_index] = + row_sizes[row_index].max(cell_measure.content_sizes.max_content); continue; }, _ => continue, }; - let layout = match self.cells_laid_out[row_index][column_index] { - Some(ref layout) => layout, - None => { - warn!("Did not find a layout at a slot index with an originating cell."); - continue; - }, - }; + cells_to_distribute.push(RowspanToDistribute { + coordinates: TableSlotCoordinates::new(column_index, row_index), + cell, + measure: cell_measure, + }); + } - let outer_block_size = layout.outer_block_size(); - if cell.rowspan == 1 { - max_row_height = max_row_height.max(outer_block_size); - } + self.rows[row_index].percent = Percentage(percentage.min(1. - total_percentage)); + total_percentage += self.rows[row_index].percent.0; + } - if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline { - let ascent = layout.ascent(); - let border_padding_start = - layout.border.block_start + layout.padding.block_start; - let border_padding_end = layout.border.block_end + layout.padding.block_end; - max_ascent = max_ascent.max(ascent + border_padding_start.into()); + cells_to_distribute.sort_by(|a, b| { + if a.range() == b.range() { + return a + .measure + .content_sizes + .min_content + .cmp(&b.measure.content_sizes.min_content); + } + if a.fully_encloses(b) { + return std::cmp::Ordering::Greater; + } + if b.fully_encloses(a) { + return std::cmp::Ordering::Less; + } + a.coordinates.y.cmp(&b.coordinates.y) + }); - // Only take into account the descent of this cell if doesn't span - // rows. The descent portion of the cell in cells that do span rows - // will be allocated to the other rows that it spans. - if cell.rowspan == 1 { - max_descent = max_descent.max( - layout.layout.content_block_size - ascent + border_padding_end.into(), - ); + for rowspan_to_distribute in cells_to_distribute { + let rows_spanned = rowspan_to_distribute.range(); + let current_rows_size: Au = rows_spanned.clone().map(|index| row_sizes[index]).sum(); + let border_spacing_spanned = + self.table.border_spacing().block * (rows_spanned.len() - 1) as i32; + let excess_size = (rowspan_to_distribute.measure.content_sizes.min_content - + current_rows_size - + border_spacing_spanned) + .max(Au::zero()); + + self.distribute_extra_size_to_rows( + excess_size, + rows_spanned, + row_sizes, + None, + true, /* rowspan_distribution */ + ); + } + } + + /// An implementation of the same extra block size distribution algorithm used in + /// LayoutNG and described at . + fn distribute_extra_size_to_rows( + &self, + mut excess_size: Au, + track_range: Range, + track_sizes: &mut Vec, + percentage_resolution_size: Option, + rowspan_distribution: bool, + ) { + if excess_size.is_zero() { + return; + } + + let is_constrained = |track_index: &usize| self.rows[*track_index].constrained; + let is_unconstrained = |track_index: &usize| !is_constrained(track_index); + let is_empty: Vec = track_sizes.iter().map(|size| size.is_zero()).collect(); + let is_not_empty = |track_index: &usize| !is_empty[*track_index]; + let other_row_that_starts_a_rowspan = |track_index: &usize| { + *track_index != track_range.start && + self.rows[*track_index].has_cell_with_span_greater_than_one + }; + + // If we have a table height (not during rowspan distribution), first distribute to rows + // that have percentage sizes proportionally to the size missing to reach the percentage + // of table height required. + if let Some(percentage_resolution_size) = percentage_resolution_size { + let get_percent_block_size_deficit = |row_index: usize, track_size: Au| { + let size_needed_for_percent = + percentage_resolution_size.scale_by(self.rows[row_index].percent.0); + (size_needed_for_percent - track_size).max(Au::zero()) + }; + let percent_block_size_deficit: Au = track_range + .clone() + .map(|index| get_percent_block_size_deficit(index, track_sizes[index])) + .sum(); + let percent_distributable_block_size = percent_block_size_deficit.min(excess_size); + if percent_distributable_block_size > Au::zero() { + for track_index in track_range.clone() { + let row_deficit = + get_percent_block_size_deficit(track_index, track_sizes[track_index]); + if row_deficit > Au::zero() { + let ratio = + row_deficit.to_f32_px() / percent_block_size_deficit.to_f32_px(); + let size = percent_distributable_block_size.scale_by(ratio); + track_sizes[track_index] += size; + excess_size -= size; } } } - - self.row_baselines.push(max_ascent); - self.row_sizes - .push(max_row_height.max(max_ascent + max_descent)); } + + // If this is rowspan distribution and there are rows other than the first row that have a + // cell with rowspan > 1, distribute the extra space equally to those rows. + if rowspan_distribution { + let rows_that_start_rowspan: Vec = track_range + .clone() + .filter(other_row_that_starts_a_rowspan) + .collect(); + if !rows_that_start_rowspan.is_empty() { + let scale = 1.0 / rows_that_start_rowspan.len() as f32; + for track_index in rows_that_start_rowspan.iter() { + track_sizes[*track_index] += excess_size.scale_by(scale); + } + return; + } + } + + // If there are unconstrained non-empty rows, grow them all proportionally to their current size. + let unconstrained_non_empty_rows: Vec = track_range + .clone() + .filter(is_unconstrained) + .filter(is_not_empty) + .collect(); + if !unconstrained_non_empty_rows.is_empty() { + let total_size: Au = unconstrained_non_empty_rows + .iter() + .map(|index| track_sizes[*index]) + .sum(); + for track_index in unconstrained_non_empty_rows.iter() { + let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px(); + track_sizes[*track_index] += excess_size.scale_by(scale); + } + return; + } + + let (non_empty_rows, empty_rows): (Vec, Vec) = + track_range.clone().partition(is_not_empty); + let only_have_empty_rows = empty_rows.len() == track_range.len(); + if !empty_rows.is_empty() { + // If this is rowspan distribution and there are only empty rows, just grow the + // last one. + if rowspan_distribution && only_have_empty_rows { + track_sizes[*empty_rows.last().unwrap()] += excess_size; + return; + } + + // Otherwise, if we only have empty rows or if all the non-empty rows are constrained, + // then grow the empty rows. + let non_empty_rows_all_constrained = !non_empty_rows.iter().any(is_unconstrained); + if only_have_empty_rows || non_empty_rows_all_constrained { + // If there are both unconstrained and constrained empty rows, only increase the + // size of the unconstrained ones, otherwise increase the size of all empty rows. + let mut rows_to_grow = &empty_rows; + let unconstrained_empty_rows: Vec = rows_to_grow + .iter() + .copied() + .filter(is_unconstrained) + .collect(); + if !unconstrained_empty_rows.is_empty() { + rows_to_grow = &unconstrained_empty_rows; + } + + // All empty rows that will grow equally. + let scale = 1.0 / rows_to_grow.len() as f32; + for track_index in rows_to_grow.iter() { + track_sizes[*track_index] += excess_size.scale_by(scale); + } + return; + } + } + + // If there are non-empty rows, they all grow in proportion to their current size, + // whether or not they are constrained. + if !non_empty_rows.is_empty() { + let total_size: Au = non_empty_rows.iter().map(|index| track_sizes[*index]).sum(); + for track_index in non_empty_rows.iter() { + let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px(); + track_sizes[*track_index] += excess_size.scale_by(scale); + } + } + } + + /// Given computed row sizes, compute the final block size of the table and distribute extra + /// block size to table rows. + fn compute_table_height_and_final_row_heights( + &mut self, + mut row_sizes: Vec, + containing_block_for_children: &ContainingBlock, + containing_block_for_table: &ContainingBlock, + ) { + // The table content height is the maximum of the computed table height from style and the + // sum of computed row heights from row layout plus size from borders and spacing. + // When block-size doesn't compute to auto, `containing_block_for children` will have + // the resulting length, properly clamped between min-block-size and max-block-size. + let style = &self.table.style; + let table_height_from_style = match style + .content_box_size(containing_block_for_table, &self.pbm) + .block + { + LengthPercentage(_) => containing_block_for_children.block_size, + Auto => style + .content_min_box_size(containing_block_for_table, &self.pbm) + .block + .map(Au::from), + } + .auto_is(Au::zero); + + let block_border_spacing = self.table.total_border_spacing().block; + let table_height_from_rows = row_sizes.iter().sum::() + block_border_spacing; + self.final_table_height = table_height_from_rows.max(table_height_from_style); + + // If the table height is defined by the rows sizes, there is no extra space to distribute + // to rows. + if self.final_table_height == table_height_from_rows { + self.row_sizes = row_sizes; + return; + } + + // There was extra block size added to the table from the table style, so distribute this + // extra space to rows using the same distribution algorithm used for distributing rowspan + // space. + // TODO: This should first distribute space to row groups and then to rows. + self.distribute_extra_size_to_rows( + self.final_table_height - table_height_from_rows, + 0..self.table.size.height, + &mut row_sizes, + Some(self.final_table_height), + false, /* rowspan_distribution */ + ); + self.row_sizes = row_sizes; } /// Lay out the table of this [`TableLayout`] into fragments. This should only be be called /// after calling [`TableLayout.compute_measures`]. - fn layout(mut self, positioning_context: &mut PositioningContext) -> IndependentLayout { + fn layout( + mut self, + layout_context: &LayoutContext, + positioning_context: &mut PositioningContext, + containing_block_for_children: &ContainingBlock, + containing_block_for_table: &ContainingBlock, + ) -> IndependentLayout { + let writing_mode = containing_block_for_children.style.writing_mode; + let grid_min_max = self.compute_grid_min_max(layout_context, writing_mode); + self.compute_table_width( + containing_block_for_children, + containing_block_for_table, + grid_min_max, + ); + self.distributed_column_widths = self.distribute_width_to_columns(); + + self.layout_cells_in_row( + layout_context, + containing_block_for_children, + positioning_context, + ); + let first_layout_row_heights = self.do_first_row_layout(writing_mode); + self.compute_table_height_and_final_row_heights( + first_layout_row_heights, + containing_block_for_children, + containing_block_for_table, + ); + assert_eq!(self.table.size.height, self.row_sizes.len()); assert_eq!(self.table.size.width, self.distributed_column_widths.len()); @@ -1012,7 +1417,8 @@ impl<'a> TableLayout<'a> { if self.table.size.width == 0 || self.table.size.height == 0 { return IndependentLayout { fragments, - content_block_size: Au::zero(), + content_block_size: self.final_table_height, + content_inline_size_for_table: Some(self.assignable_width), baselines, }; } @@ -1121,6 +1527,7 @@ impl<'a> TableLayout<'a> { IndependentLayout { fragments, content_block_size: dimensions.table_rect.max_block_position(), + content_inline_size_for_table: Some(dimensions.table_rect.max_inline_position()), baselines, } } @@ -1302,49 +1709,20 @@ impl Table { } } - fn inline_content_sizes_for_cell_at( - &self, - coords: TableSlotCoordinates, - layout_context: &LayoutContext, - writing_mode: WritingMode, - ) -> ContentSizes { - let cell = match self.resolve_first_cell(coords) { - Some(cell) => cell, - None => return ContentSizes::zero(), - }; - - let sizes = cell.inline_content_sizes(layout_context, writing_mode); - sizes.map(|size| size.scale_by(1.0 / cell.colspan as f32)) - } - - pub(crate) fn compute_inline_content_sizes( - &self, - layout_context: &LayoutContext, - writing_mode: WritingMode, - ) -> (ContentSizes, Vec>) { - let mut total_size = ContentSizes::zero(); - let mut inline_content_sizes = Vec::new(); - for column_index in 0..self.size.width { - let mut row_inline_content_sizes = Vec::new(); - let mut max_content_sizes_in_column = ContentSizes::zero(); - - for row_index in 0..self.size.width { - // TODO: Take into account padding and border here. - let coords = TableSlotCoordinates::new(column_index, row_index); - - let content_sizes = - self.inline_content_sizes_for_cell_at(coords, layout_context, writing_mode); - max_content_sizes_in_column.max_assign(content_sizes); - row_inline_content_sizes.push(content_sizes); - } - - inline_content_sizes.push(row_inline_content_sizes); - total_size += max_content_sizes_in_column; + fn total_border_spacing(&self) -> LogicalVec2 { + let border_spacing = self.border_spacing(); + LogicalVec2 { + inline: if self.size.width > 0 { + border_spacing.inline * (self.size.width as i32 + 1) + } else { + Au::zero() + }, + block: if self.size.height > 0 { + border_spacing.block * (self.size.height as i32 + 1) + } else { + Au::zero() + }, } - let gutters = self.border_spacing().inline * (self.size.width as i32 + 1); - total_size.min_content += gutters; - total_size.max_content += gutters; - (total_size, inline_content_sizes) } pub(crate) fn inline_content_sizes( @@ -1352,34 +1730,68 @@ impl Table { layout_context: &LayoutContext, writing_mode: WritingMode, ) -> ContentSizes { - self.compute_inline_content_sizes(layout_context, writing_mode) - .0 + TableLayout::new(self).compute_grid_min_max(layout_context, writing_mode) } fn get_column_measure_for_column_at_index( &self, writing_mode: WritingMode, column_index: usize, - ) -> CellOrColumnMeasure { + ) -> CellOrTrackMeasure { let column = match self.columns.get(column_index) { Some(column) => column, - None => return CellOrColumnMeasure::zero(), + None => return CellOrTrackMeasure::zero(), }; let (size, min_size, max_size) = get_sizes_from_style(&column.style, writing_mode); let percentage_contribution = get_size_percentage_contribution_from_style(&column.style, writing_mode); - CellOrColumnMeasure { + CellOrTrackMeasure { content_sizes: ContentSizes { // > The outer min-content width of a table-column or table-column-group is // > max(min-width, width). - min_content: min_size.inline.max(size.inline), + // But that's clearly wrong, since it would be equal to or greater than + // the outer max-content width. So we match other browsers instead. + min_content: min_size.inline, // > The outer max-content width of a table-column or table-column-group is // > max(min-width, min(max-width, width)). + // This matches Gecko, but Blink and WebKit ignore max_size. max_content: min_size.inline.max(max_size.inline.min(size.inline)), }, - percentage_width: percentage_contribution.inline, + percentage: percentage_contribution.inline, + } + } + + fn get_row_measure_for_row_at_index( + &self, + writing_mode: WritingMode, + row_index: usize, + ) -> CellOrTrackMeasure { + let row = match self.rows.get(row_index) { + Some(row) => row, + None => return CellOrTrackMeasure::zero(), + }; + + // In the block axis, the min-content and max-content sizes are the same + // (except for new layout boxes like grid and flex containers). Note that + // other browsers don't seem to use the min and max sizing properties here. + let size = row + .style + .box_size(writing_mode) + .block + .non_auto() + .and_then(|size| size.to_length()) + .map_or_else(Au::zero, Au::from); + let percentage_contribution = + get_size_percentage_contribution_from_style(&row.style, writing_mode); + + CellOrTrackMeasure { + content_sizes: ContentSizes { + min_content: size, + max_content: size, + }, + percentage: percentage_contribution.block, } } @@ -1387,40 +1799,19 @@ impl Table { &self, layout_context: &LayoutContext, positioning_context: &mut PositioningContext, - containing_block: &ContainingBlock, + containing_block_for_children: &ContainingBlock, + containing_block_for_table: &ContainingBlock, ) -> IndependentLayout { - let mut table_layout = TableLayout::new(self); - table_layout.compute_measures(layout_context, positioning_context, containing_block); - table_layout.layout(positioning_context) + TableLayout::new(self).layout( + layout_context, + positioning_context, + containing_block_for_children, + containing_block_for_table, + ) } } impl TableSlotCell { - pub(crate) fn inline_content_sizes( - &self, - layout_context: &LayoutContext, - writing_mode: WritingMode, - ) -> ContentSizes { - let border = self.style.border_width(writing_mode); - let padding = self.style.padding(writing_mode); - - // For padding, a cyclic percentage is resolved against zero for determining intrinsic size - // contributions. - // https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution - let zero = Length::zero(); - let border_padding_sum = border.inline_sum() + - padding.inline_start.resolve(zero) + - padding.inline_end.resolve(zero); - - let mut sizes = self - .contents - .contents - .inline_content_sizes(layout_context, writing_mode); - sizes.min_content += border_padding_sum.into(); - sizes.max_content += border_padding_sum.into(); - sizes - } - fn effective_vertical_align(&self) -> VerticalAlignKeyword { match self.style.clone_vertical_align() { VerticalAlign::Keyword(VerticalAlignKeyword::Top) => VerticalAlignKeyword::Top, @@ -1535,10 +1926,8 @@ fn get_sizes_from_style( writing_mode: WritingMode, ) -> (LogicalVec2, LogicalVec2, LogicalVec2) { let get_max_size_for_axis = |size: Option<&ComputedLengthPercentage>| { - size.map_or_else( - || MAX_AU, - |length_percentage| length_percentage.resolve(Length::zero()).into(), - ) + size.and_then(|length_percentage| length_percentage.to_length()) + .map_or(MAX_AU, Au::from) }; let max_size = style.max_box_size(writing_mode); @@ -1548,9 +1937,9 @@ fn get_sizes_from_style( }; let get_size_for_axis = |size: LengthPercentageOrAuto<'_>| { - size.percentage_relative_to(Length::zero()) - .map(|value| value.into()) - .auto_is(Au::zero) + size.non_auto() + .and_then(|size| size.to_length()) + .map_or_else(Au::zero, Au::from) }; let min_size = style.min_box_size(writing_mode); @@ -1567,3 +1956,19 @@ fn get_sizes_from_style( (size, min_size, max_size) } + +struct RowspanToDistribute<'a> { + coordinates: TableSlotCoordinates, + cell: &'a TableSlotCell, + measure: &'a CellOrTrackMeasure, +} + +impl<'a> RowspanToDistribute<'a> { + fn range(&self) -> Range { + self.coordinates.y..self.coordinates.y + self.cell.rowspan + } + + fn fully_encloses(&self, other: &RowspanToDistribute) -> bool { + other.coordinates.y > self.coordinates.y && other.range().end < self.range().end + } +} diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index 4da4f2d785a..97fc689dd2d 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -24,7 +24,7 @@ ipc-channel = { workspace = true } layout = { path = "../layout", package = "layout_2013" } lazy_static = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } metrics = { path = "../metrics" } msg = { workspace = true } net_traits = { workspace = true } @@ -36,11 +36,11 @@ script_layout_interface = { workspace = true } script_traits = { workspace = true } serde_json = { workspace = true } servo_allocator = { path = "../allocator" } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms" } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_config = { path = "../config" } servo_url = { path = "../url" } -style = { path = "../style" } +style = { workspace = true } style_traits = { workspace = true } time = { workspace = true } url = { workspace = true } diff --git a/components/layout_thread_2020/Cargo.toml b/components/layout_thread_2020/Cargo.toml index 2a1e510630c..c5010281ae8 100644 --- a/components/layout_thread_2020/Cargo.toml +++ b/components/layout_thread_2020/Cargo.toml @@ -23,7 +23,7 @@ ipc-channel = { workspace = true } layout = { path = "../layout_2020", package = "layout_2020" } lazy_static = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } metrics = { path = "../metrics" } msg = { workspace = true } net_traits = { workspace = true } @@ -33,11 +33,11 @@ script = { path = "../script" } script_layout_interface = { workspace = true } script_traits = { workspace = true } servo_allocator = { path = "../allocator" } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms" } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_config = { path = "../config" } servo_url = { path = "../url" } -style = { path = "../style" } +style = { workspace = true } style_traits = { workspace = true } url = { workspace = true } webrender_api = { workspace = true } diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 9e7bb76f31d..f3dd538adda 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -870,6 +870,7 @@ impl LayoutThread { &mut *rw_data, &mut layout_context, data.result.borrow_mut().as_mut().unwrap(), + document_shared_lock, ); } @@ -879,6 +880,7 @@ impl LayoutThread { rw_data: &mut LayoutThreadData, context: &mut LayoutContext, reflow_result: &mut ReflowComplete, + shared_lock: &SharedRwLock, ) { reflow_result.pending_images = std::mem::replace(&mut *context.pending_images.lock().unwrap(), vec![]); @@ -925,8 +927,14 @@ impl LayoutThread { }, &QueryMsg::ResolvedFontStyleQuery(node, ref property, ref value) => { let node = unsafe { ServoLayoutNode::::new(&node) }; - rw_data.resolved_font_style_response = - process_resolved_font_style_query(node, property, value); + rw_data.resolved_font_style_response = process_resolved_font_style_query( + context, + node, + property, + value, + self.url.clone(), + shared_lock, + ); }, &QueryMsg::OffsetParentQuery(node) => { rw_data.offset_parent_response = diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml deleted file mode 100644 index 01204a98e41..00000000000 --- a/components/malloc_size_of/Cargo.toml +++ /dev/null @@ -1,52 +0,0 @@ -[package] -name = "malloc_size_of" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MIT OR Apache-2.0" -publish = false - -[lib] -path = "lib.rs" - -[features] -servo = [ - "accountable-refcell", - "content-security-policy", - "crossbeam-channel", - "http", - "keyboard-types", - "serde", - "serde_bytes", - "string_cache", - "time", - "url", - "uuid", - "webrender_api", - "xml5ever", -] - -[dependencies] -accountable-refcell = { workspace = true, optional = true } -app_units = { workspace = true } -content-security-policy = { workspace = true, optional = true } -crossbeam-channel = { workspace = true, optional = true } -cssparser = { workspace = true } -euclid = { workspace = true } -http = { workspace = true, optional = true } -indexmap = { workspace = true } -keyboard-types = { workspace = true, optional = true } -selectors = { path = "../selectors" } -serde = { workspace = true, optional = true } -serde_bytes = { workspace = true, optional = true } -servo_arc = { path = "../servo_arc" } -smallbitvec = { workspace = true } -smallvec = { workspace = true } -string_cache = { workspace = true, optional = true } -thin-vec = { workspace = true } -time = { workspace = true, optional = true } -tokio = { workspace = true, features = ["sync"] } -url = { workspace = true, optional = true } -uuid = { workspace = true, optional = true } -void = "1.0.2" -webrender_api = { workspace = true, optional = true } -xml5ever = { workspace = true, optional = true } diff --git a/components/malloc_size_of/LICENSE-APACHE b/components/malloc_size_of/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e8..00000000000 --- a/components/malloc_size_of/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/components/malloc_size_of/LICENSE-MIT b/components/malloc_size_of/LICENSE-MIT deleted file mode 100644 index 31aa79387f2..00000000000 --- a/components/malloc_size_of/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs deleted file mode 100644 index dd0ca38e17c..00000000000 --- a/components/malloc_size_of/lib.rs +++ /dev/null @@ -1,978 +0,0 @@ -// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A crate for measuring the heap usage of data structures in a way that -//! integrates with Firefox's memory reporting, particularly the use of -//! mozjemalloc and DMD. In particular, it has the following features. -//! - It isn't bound to a particular heap allocator. -//! - It provides traits for both "shallow" and "deep" measurement, which gives -//! flexibility in the cases where the traits can't be used. -//! - It allows for measuring blocks even when only an interior pointer can be -//! obtained for heap allocations, e.g. `HashSet` and `HashMap`. (This relies -//! on the heap allocator having suitable support, which mozjemalloc has.) -//! - It allows handling of types like `Rc` and `Arc` by providing traits that -//! are different to the ones for non-graph structures. -//! -//! Suggested uses are as follows. -//! - When possible, use the `MallocSizeOf` trait. (Deriving support is -//! provided by the `malloc_size_of_derive` crate.) -//! - If you need an additional synchronization argument, provide a function -//! that is like the standard trait method, but with the extra argument. -//! - If you need multiple measurements for a type, provide a function named -//! `add_size_of` that takes a mutable reference to a struct that contains -//! the multiple measurement fields. -//! - When deep measurement (via `MallocSizeOf`) cannot be implemented for a -//! type, shallow measurement (via `MallocShallowSizeOf`) in combination with -//! iteration can be a useful substitute. -//! - `Rc` and `Arc` are always tricky, which is why `MallocSizeOf` is not (and -//! should not be) implemented for them. -//! - If an `Rc` or `Arc` is known to be a "primary" reference and can always -//! be measured, it should be measured via the `MallocUnconditionalSizeOf` -//! trait. -//! - If an `Rc` or `Arc` should be measured only if it hasn't been seen -//! before, it should be measured via the `MallocConditionalSizeOf` trait. -//! - Using universal function call syntax is a good idea when measuring boxed -//! fields in structs, because it makes it clear that the Box is being -//! measured as well as the thing it points to. E.g. -//! ` as MallocSizeOf>::size_of(field, ops)`. -//! -//! Note: WebRender has a reduced fork of this crate, so that we can avoid -//! publishing this crate on crates.io. - -#[cfg(feature = "servo")] -extern crate accountable_refcell; -extern crate app_units; -#[cfg(feature = "servo")] -extern crate content_security_policy; -#[cfg(feature = "servo")] -extern crate crossbeam_channel; -extern crate cssparser; -extern crate euclid; -#[cfg(feature = "servo")] -extern crate http; -#[cfg(feature = "servo")] -extern crate keyboard_types; -extern crate selectors; -#[cfg(feature = "servo")] -extern crate serde; -#[cfg(feature = "servo")] -extern crate serde_bytes; -extern crate servo_arc; -extern crate smallbitvec; -extern crate smallvec; -#[cfg(feature = "servo")] -extern crate string_cache; -#[cfg(feature = "servo")] -extern crate time; -#[cfg(feature = "url")] -extern crate url; -#[cfg(feature = "servo")] -extern crate uuid; -extern crate void; -#[cfg(feature = "webrender_api")] -extern crate webrender_api; -#[cfg(feature = "servo")] -extern crate xml5ever; - -#[cfg(feature = "servo")] -use content_security_policy as csp; -#[cfg(feature = "servo")] -use serde_bytes::ByteBuf; -use std::hash::{BuildHasher, Hash}; -use std::mem::size_of; -use std::ops::Range; -use std::ops::{Deref, DerefMut}; -use std::os::raw::c_void; -#[cfg(feature = "servo")] -use uuid::Uuid; -use void::Void; - -/// A C function that takes a pointer to a heap allocation and returns its size. -type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; - -/// A closure implementing a stateful predicate on pointers. -type VoidPtrToBoolFnMut = dyn FnMut(*const c_void) -> bool; - -/// Operations used when measuring heap usage of data structures. -pub struct MallocSizeOfOps { - /// A function that returns the size of a heap allocation. - size_of_op: VoidPtrToSizeFn, - - /// Like `size_of_op`, but can take an interior pointer. Optional because - /// not all allocators support this operation. If it's not provided, some - /// memory measurements will actually be computed estimates rather than - /// real and accurate measurements. - enclosing_size_of_op: Option, - - /// Check if a pointer has been seen before, and remember it for next time. - /// Useful when measuring `Rc`s and `Arc`s. Optional, because many places - /// don't need it. - have_seen_ptr_op: Option>, -} - -impl MallocSizeOfOps { - pub fn new( - size_of: VoidPtrToSizeFn, - malloc_enclosing_size_of: Option, - have_seen_ptr: Option>, - ) -> Self { - MallocSizeOfOps { - size_of_op: size_of, - enclosing_size_of_op: malloc_enclosing_size_of, - have_seen_ptr_op: have_seen_ptr, - } - } - - /// Check if an allocation is empty. This relies on knowledge of how Rust - /// handles empty allocations, which may change in the future. - fn is_empty(ptr: *const T) -> bool { - // The correct condition is this: - // `ptr as usize <= ::std::mem::align_of::()` - // But we can't call align_of() on a ?Sized T. So we approximate it - // with the following. 256 is large enough that it should always be - // larger than the required alignment, but small enough that it is - // always in the first page of memory and therefore not a legitimate - // address. - return ptr as *const usize as usize <= 256; - } - - /// Call `size_of_op` on `ptr`, first checking that the allocation isn't - /// empty, because some types (such as `Vec`) utilize empty allocations. - pub unsafe fn malloc_size_of(&self, ptr: *const T) -> usize { - if MallocSizeOfOps::is_empty(ptr) { - 0 - } else { - (self.size_of_op)(ptr as *const c_void) - } - } - - /// Is an `enclosing_size_of_op` available? - pub fn has_malloc_enclosing_size_of(&self) -> bool { - self.enclosing_size_of_op.is_some() - } - - /// Call `enclosing_size_of_op`, which must be available, on `ptr`, which - /// must not be empty. - pub unsafe fn malloc_enclosing_size_of(&self, ptr: *const T) -> usize { - assert!(!MallocSizeOfOps::is_empty(ptr)); - (self.enclosing_size_of_op.unwrap())(ptr as *const c_void) - } - - /// Call `have_seen_ptr_op` on `ptr`. - pub fn have_seen_ptr(&mut self, ptr: *const T) -> bool { - let have_seen_ptr_op = self - .have_seen_ptr_op - .as_mut() - .expect("missing have_seen_ptr_op"); - have_seen_ptr_op(ptr as *const c_void) - } -} - -/// Trait for measuring the "deep" heap usage of a data structure. This is the -/// most commonly-used of the traits. -pub trait MallocSizeOf { - /// Measure the heap usage of all descendant heap-allocated structures, but - /// not the space taken up by the value itself. - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// Trait for measuring the "shallow" heap usage of a container. -pub trait MallocShallowSizeOf { - /// Measure the heap usage of immediate heap-allocated descendant - /// structures, but not the space taken up by the value itself. Anything - /// beyond the immediate descendants must be measured separately, using - /// iteration. - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// Like `MallocSizeOf`, but with a different name so it cannot be used -/// accidentally with derive(MallocSizeOf). For use with types like `Rc` and -/// `Arc` when appropriate (e.g. when measuring a "primary" reference). -pub trait MallocUnconditionalSizeOf { - /// Measure the heap usage of all heap-allocated descendant structures, but - /// not the space taken up by the value itself. - fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// `MallocUnconditionalSizeOf` combined with `MallocShallowSizeOf`. -pub trait MallocUnconditionalShallowSizeOf { - /// `unconditional_size_of` combined with `shallow_size_of`. - fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// Like `MallocSizeOf`, but only measures if the value hasn't already been -/// measured. For use with types like `Rc` and `Arc` when appropriate (e.g. -/// when there is no "primary" reference). -pub trait MallocConditionalSizeOf { - /// Measure the heap usage of all heap-allocated descendant structures, but - /// not the space taken up by the value itself, and only if that heap usage - /// hasn't already been measured. - fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -/// `MallocConditionalSizeOf` combined with `MallocShallowSizeOf`. -pub trait MallocConditionalShallowSizeOf { - /// `conditional_size_of` combined with `shallow_size_of`. - fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize; -} - -impl MallocSizeOf for String { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - unsafe { ops.malloc_size_of(self.as_ptr()) } - } -} - -impl<'a, T: ?Sized> MallocSizeOf for &'a T { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - // Zero makes sense for a non-owning reference. - 0 - } -} - -impl MallocShallowSizeOf for Box { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - unsafe { ops.malloc_size_of(&**self) } - } -} - -impl MallocSizeOf for Box { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.shallow_size_of(ops) + (**self).size_of(ops) - } -} - -impl MallocSizeOf for () { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -impl MallocSizeOf for (T1, T2) -where - T1: MallocSizeOf, - T2: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.0.size_of(ops) + self.1.size_of(ops) - } -} - -impl MallocSizeOf for (T1, T2, T3) -where - T1: MallocSizeOf, - T2: MallocSizeOf, - T3: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) - } -} - -impl MallocSizeOf for (T1, T2, T3, T4) -where - T1: MallocSizeOf, - T2: MallocSizeOf, - T3: MallocSizeOf, - T4: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) + self.3.size_of(ops) - } -} - -impl MallocSizeOf for Option { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if let Some(val) = self.as_ref() { - val.size_of(ops) - } else { - 0 - } - } -} - -impl MallocSizeOf for Result { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - match *self { - Ok(ref x) => x.size_of(ops), - Err(ref e) => e.size_of(ops), - } - } -} - -impl MallocSizeOf for std::cell::Cell { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.get().size_of(ops) - } -} - -impl MallocSizeOf for std::cell::RefCell { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.borrow().size_of(ops) - } -} - -impl<'a, B: ?Sized + ToOwned> MallocSizeOf for std::borrow::Cow<'a, B> -where - B::Owned: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - match *self { - std::borrow::Cow::Borrowed(_) => 0, - std::borrow::Cow::Owned(ref b) => b.size_of(ops), - } - } -} - -impl MallocSizeOf for [T] { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = 0; - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -#[cfg(feature = "servo")] -impl MallocShallowSizeOf for ByteBuf { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - unsafe { ops.malloc_size_of(self.as_ptr()) } - } -} - -#[cfg(feature = "servo")] -impl MallocSizeOf for ByteBuf { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -impl MallocShallowSizeOf for Vec { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - unsafe { ops.malloc_size_of(self.as_ptr()) } - } -} - -impl MallocSizeOf for Vec { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -impl MallocShallowSizeOf for std::collections::VecDeque { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.has_malloc_enclosing_size_of() { - if let Some(front) = self.front() { - // The front element is an interior pointer. - unsafe { ops.malloc_enclosing_size_of(&*front) } - } else { - // This assumes that no memory is allocated when the VecDeque is empty. - 0 - } - } else { - // An estimate. - self.capacity() * size_of::() - } - } -} - -impl MallocSizeOf for std::collections::VecDeque { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -impl MallocShallowSizeOf for smallvec::SmallVec { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if self.spilled() { - unsafe { ops.malloc_size_of(self.as_ptr()) } - } else { - 0 - } - } -} - -impl MallocSizeOf for smallvec::SmallVec -where - A: smallvec::Array, - A::Item: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -impl MallocShallowSizeOf for thin_vec::ThinVec { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if self.capacity() == 0 { - // If it's the singleton we might not be a heap pointer. - return 0; - } - - assert_eq!( - std::mem::size_of::(), - std::mem::size_of::<*const ()>() - ); - unsafe { ops.malloc_size_of(*(self as *const Self as *const *const ())) } - } -} - -impl MallocSizeOf for thin_vec::ThinVec { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for elem in self.iter() { - n += elem.size_of(ops); - } - n - } -} - -macro_rules! malloc_size_of_hash_set { - ($ty:ty) => { - impl MallocShallowSizeOf for $ty - where - T: Eq + Hash, - S: BuildHasher, - { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.has_malloc_enclosing_size_of() { - // The first value from the iterator gives us an interior pointer. - // `ops.malloc_enclosing_size_of()` then gives us the storage size. - // This assumes that the `HashSet`'s contents (values and hashes) - // are all stored in a single contiguous heap allocation. - self.iter() - .next() - .map_or(0, |t| unsafe { ops.malloc_enclosing_size_of(t) }) - } else { - // An estimate. - self.capacity() * (size_of::() + size_of::()) - } - } - } - - impl MallocSizeOf for $ty - where - T: Eq + Hash + MallocSizeOf, - S: BuildHasher, - { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for t in self.iter() { - n += t.size_of(ops); - } - n - } - } - }; -} - -malloc_size_of_hash_set!(std::collections::HashSet); -malloc_size_of_hash_set!(indexmap::IndexSet); - -macro_rules! malloc_size_of_hash_map { - ($ty:ty) => { - impl MallocShallowSizeOf for $ty - where - K: Eq + Hash, - S: BuildHasher, - { - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - // See the implementation for std::collections::HashSet for details. - if ops.has_malloc_enclosing_size_of() { - self.values() - .next() - .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) }) - } else { - self.capacity() * (size_of::() + size_of::() + size_of::()) - } - } - } - - impl MallocSizeOf for $ty - where - K: Eq + Hash + MallocSizeOf, - V: MallocSizeOf, - S: BuildHasher, - { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for (k, v) in self.iter() { - n += k.size_of(ops); - n += v.size_of(ops); - } - n - } - } - }; -} - -malloc_size_of_hash_map!(std::collections::HashMap); -malloc_size_of_hash_map!(indexmap::IndexMap); - -impl MallocShallowSizeOf for std::collections::BTreeMap -where - K: Eq + Hash, -{ - fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.has_malloc_enclosing_size_of() { - self.values() - .next() - .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) }) - } else { - self.len() * (size_of::() + size_of::() + size_of::()) - } - } -} - -impl MallocSizeOf for std::collections::BTreeMap -where - K: Eq + Hash + MallocSizeOf, - V: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.shallow_size_of(ops); - for (k, v) in self.iter() { - n += k.size_of(ops); - n += v.size_of(ops); - } - n - } -} - -// PhantomData is always 0. -impl MallocSizeOf for std::marker::PhantomData { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -// XXX: we don't want MallocSizeOf to be defined for Rc and Arc. If negative -// trait bounds are ever allowed, this code should be uncommented. -// (We do have a compile-fail test for this: -// rc_arc_must_not_derive_malloc_size_of.rs) -//impl !MallocSizeOf for Arc { } -//impl !MallocShallowSizeOf for Arc { } - -impl MallocUnconditionalShallowSizeOf for servo_arc::Arc { - fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - unsafe { ops.malloc_size_of(self.heap_ptr()) } - } -} - -impl MallocUnconditionalSizeOf for servo_arc::Arc { - fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.unconditional_shallow_size_of(ops) + (**self).size_of(ops) - } -} - -impl MallocConditionalShallowSizeOf for servo_arc::Arc { - fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.have_seen_ptr(self.heap_ptr()) { - 0 - } else { - self.unconditional_shallow_size_of(ops) - } - } -} - -impl MallocConditionalSizeOf for servo_arc::Arc { - fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if ops.have_seen_ptr(self.heap_ptr()) { - 0 - } else { - self.unconditional_size_of(ops) - } - } -} - -/// If a mutex is stored directly as a member of a data type that is being measured, -/// it is the unique owner of its contents and deserves to be measured. -/// -/// If a mutex is stored inside of an Arc value as a member of a data type that is being measured, -/// the Arc will not be automatically measured so there is no risk of overcounting the mutex's -/// contents. -impl MallocSizeOf for std::sync::Mutex { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - (*self.lock().unwrap()).size_of(ops) - } -} - -impl MallocSizeOf for smallbitvec::SmallBitVec { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - if let Some(ptr) = self.heap_ptr() { - unsafe { ops.malloc_size_of(ptr) } - } else { - 0 - } - } -} - -impl MallocSizeOf for euclid::Length { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.0.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Scale { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.0.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Point2D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.x.size_of(ops) + self.y.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Rect { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.origin.size_of(ops) + self.size.size_of(ops) - } -} - -impl MallocSizeOf for euclid::SideOffsets2D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.top.size_of(ops) + - self.right.size_of(ops) + - self.bottom.size_of(ops) + - self.left.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Size2D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.width.size_of(ops) + self.height.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Transform2D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.m11.size_of(ops) + - self.m12.size_of(ops) + - self.m21.size_of(ops) + - self.m22.size_of(ops) + - self.m31.size_of(ops) + - self.m32.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Transform3D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.m11.size_of(ops) + - self.m12.size_of(ops) + - self.m13.size_of(ops) + - self.m14.size_of(ops) + - self.m21.size_of(ops) + - self.m22.size_of(ops) + - self.m23.size_of(ops) + - self.m24.size_of(ops) + - self.m31.size_of(ops) + - self.m32.size_of(ops) + - self.m33.size_of(ops) + - self.m34.size_of(ops) + - self.m41.size_of(ops) + - self.m42.size_of(ops) + - self.m43.size_of(ops) + - self.m44.size_of(ops) - } -} - -impl MallocSizeOf for euclid::Vector2D { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.x.size_of(ops) + self.y.size_of(ops) - } -} - -impl MallocSizeOf for selectors::parser::AncestorHashes { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let selectors::parser::AncestorHashes { ref packed_hashes } = *self; - packed_hashes.size_of(ops) - } -} - -impl MallocSizeOf for selectors::parser::Selector -where - Impl::NonTSPseudoClass: MallocSizeOf, - Impl::PseudoElement: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - let mut n = 0; - - // It's OK to measure this ThinArc directly because it's the - // "primary" reference. (The secondary references are on the - // Stylist.) - n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) }; - for component in self.iter_raw_match_order() { - n += component.size_of(ops); - } - - n - } -} - -impl MallocSizeOf for selectors::parser::Component -where - Impl::NonTSPseudoClass: MallocSizeOf, - Impl::PseudoElement: MallocSizeOf, -{ - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - use selectors::parser::Component; - - match self { - Component::AttributeOther(ref attr_selector) => attr_selector.size_of(ops), - Component::Negation(ref components) => components.size_of(ops), - Component::NonTSPseudoClass(ref pseudo) => (*pseudo).size_of(ops), - Component::Slotted(ref selector) | Component::Host(Some(ref selector)) => { - selector.size_of(ops) - }, - Component::Is(ref list) | Component::Where(ref list) => list.size_of(ops), - Component::Has(ref relative_selectors) => relative_selectors.size_of(ops), - Component::NthOf(ref nth_of_data) => nth_of_data.size_of(ops), - Component::PseudoElement(ref pseudo) => (*pseudo).size_of(ops), - Component::Combinator(..) | - Component::ExplicitAnyNamespace | - Component::ExplicitNoNamespace | - Component::DefaultNamespace(..) | - Component::Namespace(..) | - Component::ExplicitUniversalType | - Component::LocalName(..) | - Component::ID(..) | - Component::Part(..) | - Component::Class(..) | - Component::AttributeInNoNamespaceExists { .. } | - Component::AttributeInNoNamespace { .. } | - Component::Root | - Component::Empty | - Component::Scope | - Component::ParentSelector | - Component::Nth(..) | - Component::Host(None) | - Component::RelativeSelectorAnchor => 0, - } - } -} - -impl MallocSizeOf - for selectors::attr::AttrSelectorWithOptionalNamespace -{ - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -impl MallocSizeOf for Void { - #[inline] - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - void::unreachable(*self) - } -} - -#[cfg(feature = "servo")] -impl MallocSizeOf for string_cache::Atom { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -/// For use on types where size_of() returns 0. -#[macro_export] -macro_rules! malloc_size_of_is_0( - ($($ty:ty),+) => ( - $( - impl $crate::MallocSizeOf for $ty { - #[inline(always)] - fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize { - 0 - } - } - )+ - ); - ($($ty:ident<$($gen:ident),+>),+) => ( - $( - impl<$($gen: $crate::MallocSizeOf),+> $crate::MallocSizeOf for $ty<$($gen),+> { - #[inline(always)] - fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize { - 0 - } - } - )+ - ); -); - -malloc_size_of_is_0!(bool, char, str); -malloc_size_of_is_0!(u8, u16, u32, u64, u128, usize); -malloc_size_of_is_0!(i8, i16, i32, i64, i128, isize); -malloc_size_of_is_0!(f32, f64); -malloc_size_of_is_0!(std::num::NonZeroU64); - -malloc_size_of_is_0!(std::sync::atomic::AtomicBool); -malloc_size_of_is_0!(std::sync::atomic::AtomicIsize); -malloc_size_of_is_0!(std::sync::atomic::AtomicUsize); - -malloc_size_of_is_0!(Range, Range, Range, Range, Range); -malloc_size_of_is_0!(Range, Range, Range, Range, Range); -malloc_size_of_is_0!(Range, Range); - -malloc_size_of_is_0!(app_units::Au); - -malloc_size_of_is_0!(cssparser::RGBA, cssparser::TokenSerializationType); - -#[cfg(feature = "servo")] -malloc_size_of_is_0!(csp::Destination); - -#[cfg(feature = "servo")] -malloc_size_of_is_0!(Uuid); - -#[cfg(feature = "url")] -impl MallocSizeOf for url::Host { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - match *self { - url::Host::Domain(ref s) => s.size_of(ops), - _ => 0, - } - } -} -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::BorderRadius); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::BorderStyle); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::BoxShadowClipMode); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ColorF); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ComplexClipRegion); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ExtendMode); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::FilterOp); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ExternalScrollId); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::FontInstanceKey); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::GradientStop); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::GlyphInstance); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::NinePatchBorder); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ImageKey); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::ImageRendering); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::LineStyle); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::MixBlendMode); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::NormalBorder); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::RepeatMode); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::StickyOffsetBounds); -#[cfg(feature = "webrender_api")] -malloc_size_of_is_0!(webrender_api::TransformStyle); - -#[cfg(feature = "servo")] -impl MallocSizeOf for keyboard_types::Key { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - match self { - keyboard_types::Key::Character(ref s) => s.size_of(ops), - _ => 0, - } - } -} - -#[cfg(feature = "servo")] -malloc_size_of_is_0!(keyboard_types::Modifiers); - -#[cfg(feature = "servo")] -impl MallocSizeOf for xml5ever::QualName { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.prefix.size_of(ops) + self.ns.size_of(ops) + self.local.size_of(ops) - } -} - -#[cfg(feature = "servo")] -malloc_size_of_is_0!(time::Duration); -#[cfg(feature = "servo")] -malloc_size_of_is_0!(time::Tm); -#[cfg(feature = "servo")] -malloc_size_of_is_0!(std::time::Duration); -#[cfg(feature = "servo")] -malloc_size_of_is_0!(std::time::SystemTime); -#[cfg(feature = "servo")] -malloc_size_of_is_0!(std::time::Instant); - -// Placeholder for unique case where internals of Sender cannot be measured. -// malloc size of is 0 macro complains about type supplied! -#[cfg(feature = "servo")] -impl MallocSizeOf for crossbeam_channel::Sender { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -#[cfg(feature = "servo")] -impl MallocSizeOf for tokio::sync::mpsc::UnboundedSender { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -#[cfg(feature = "servo")] -impl MallocSizeOf for http::StatusCode { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - 0 - } -} - -/// Measurable that defers to inner value and used to verify MallocSizeOf implementation in a -/// struct. -#[derive(Clone)] -pub struct Measurable(pub T); - -impl Deref for Measurable { - type Target = T; - - fn deref(&self) -> &T { - &self.0 - } -} - -impl DerefMut for Measurable { - fn deref_mut(&mut self) -> &mut T { - &mut self.0 - } -} - -#[cfg(feature = "servo")] -impl MallocSizeOf for accountable_refcell::RefCell { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - self.borrow().size_of(ops) - } -} diff --git a/components/malloc_size_of/rustfmt.toml b/components/malloc_size_of/rustfmt.toml deleted file mode 100644 index c7ad93bafe3..00000000000 --- a/components/malloc_size_of/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -disable_all_formatting = true diff --git a/components/media/Cargo.toml b/components/media/Cargo.toml index a63c108fcac..9691d989312 100644 --- a/components/media/Cargo.toml +++ b/components/media/Cargo.toml @@ -17,7 +17,7 @@ ipc-channel = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } serde = { workspace = true } -servo-media = { git = "https://github.com/servo/media" } +servo-media = { workspace = true } servo_config = { path = "../config" } webrender_api = { workspace = true } webrender_traits = { workspace = true } diff --git a/components/metrics/Cargo.toml b/components/metrics/Cargo.toml index 29579d9aacb..08701edf372 100644 --- a/components/metrics/Cargo.toml +++ b/components/metrics/Cargo.toml @@ -14,7 +14,7 @@ path = "lib.rs" gfx_traits = { workspace = true } ipc-channel = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } msg = { workspace = true } profile_traits = { workspace = true } diff --git a/components/net/Cargo.toml b/components/net/Cargo.toml index 0d188ba4d13..01da269cd0b 100644 --- a/components/net/Cargo.toml +++ b/components/net/Cargo.toml @@ -38,7 +38,7 @@ ipc-channel = { workspace = true } lazy_static = { workspace = true } libflate = "0.1" log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } mime = { workspace = true } mime_guess = { workspace = true } @@ -53,7 +53,7 @@ rustls-pemfile = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } servo_allocator = { path = "../allocator" } -servo_arc = { path = "../servo_arc" } +servo_arc = { workspace = true } servo_config = { path = "../config" } servo_url = { path = "../url" } sha2 = "0.10" diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs index 6a25aad9818..5f30884ecf1 100644 --- a/components/net/image_cache.rs +++ b/components/net/image_cache.rs @@ -4,8 +4,8 @@ use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::HashMap; +use std::mem; use std::sync::{Arc, Mutex}; -use std::{mem, thread}; use embedder_traits::resources::{self, Resource}; use imsz::imsz_from_reader; @@ -25,6 +25,8 @@ use servo_url::{ImmutableOrigin, ServoUrl}; use webrender_api::units::DeviceIntSize; use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat}; +use crate::resource_thread::CoreResourceThreadPool; + /// /// TODO(gw): Remaining work on image cache: /// * Make use of the prefetch support in various parts of the code. @@ -415,6 +417,9 @@ impl ImageCacheStore { pub struct ImageCacheImpl { store: Arc>, + + /// Thread pool for image decoding + thread_pool: CoreResourceThreadPool, } impl ImageCache for ImageCacheImpl { @@ -431,6 +436,7 @@ impl ImageCache for ImageCacheImpl { placeholder_url: ServoUrl::parse("chrome://resources/rippy.png").unwrap(), webrender_api: webrender_api, })), + thread_pool: CoreResourceThreadPool::new(16), } } @@ -634,7 +640,7 @@ impl ImageCache for ImageCacheImpl { }; let local_store = self.store.clone(); - thread::spawn(move || { + self.thread_pool.spawn(move || { let msg = decode_bytes_sync(key, &*bytes, cors_status); debug!("Image decoded"); local_store.lock().unwrap().handle_decoder(msg); diff --git a/components/pixels/Cargo.toml b/components/pixels/Cargo.toml index 708b16163ba..f201ff7c0e4 100644 --- a/components/pixels/Cargo.toml +++ b/components/pixels/Cargo.toml @@ -12,6 +12,6 @@ path = "lib.rs" [dependencies] euclid = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } serde = { workspace = true, features = ["derive"] } diff --git a/components/rand/lib.rs b/components/rand/lib.rs index 260ff2c259a..042736ed5af 100644 --- a/components/rand/lib.rs +++ b/components/rand/lib.rs @@ -95,12 +95,12 @@ impl ServoRng { } } -impl ServoRng { +impl Default for ServoRng { /// Create an auto-reseeding instance of `ServoRng`. /// /// This uses the shared `OsRng`, so avoids consuming /// a file descriptor. - pub fn new() -> ServoRng { + fn default() -> Self { trace!("Creating new ServoRng."); let mut os_rng = OS_RNG.lock().expect("Poisoned lock."); let isaac_rng = IsaacCore::from_rng(&mut *os_rng).unwrap(); @@ -156,7 +156,7 @@ pub fn thread_rng() -> ServoThreadRng { } thread_local! { - static SERVO_THREAD_RNG: ServoThreadRng = ServoThreadRng { rng: Rc::new(RefCell::new(ServoRng::new())) }; + static SERVO_THREAD_RNG: ServoThreadRng = ServoThreadRng { rng: Rc::new(RefCell::new(ServoRng::default())) }; } impl RngCore for ServoThreadRng { diff --git a/components/range/Cargo.toml b/components/range/Cargo.toml index bcaf874786f..c674d5a799c 100644 --- a/components/range/Cargo.toml +++ b/components/range/Cargo.toml @@ -11,7 +11,7 @@ name = "range" path = "lib.rs" [dependencies] -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } num-traits = { workspace = true } serde = { workspace = true } diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index cc29865bd69..a9d2d52b94e 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -68,7 +68,7 @@ keyboard-types = { workspace = true } lazy_static = { workspace = true } libc = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } media = { path = "../media" } metrics = { path = "../metrics" } @@ -88,20 +88,20 @@ ref_filter_map = "1.0.1" regex = { workspace = true } script_layout_interface = { workspace = true } script_traits = { workspace = true } -selectors = { path = "../selectors" } +selectors = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_bytes = { workspace = true } -servo-media = { git = "https://github.com/servo/media" } +servo-media = { workspace = true } servo_allocator = { path = "../allocator" } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms" } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } servo_rand = { path = "../rand" } servo_url = { path = "../url" } smallvec = { workspace = true, features = ["union"] } sparkle = { workspace = true } -style = { path = "../style", features = ["servo"] } +style = { workspace = true } style_traits = { workspace = true } swapper = "0.1" tempfile = "3" diff --git a/components/script/dom/audiobuffersourcenode.rs b/components/script/dom/audiobuffersourcenode.rs index c7df916db5a..5525078a852 100644 --- a/components/script/dom/audiobuffersourcenode.rs +++ b/components/script/dom/audiobuffersourcenode.rs @@ -10,7 +10,7 @@ use js::rust::HandleObject; use servo_media::audio::buffer_source_node::{ AudioBufferSourceNodeMessage, AudioBufferSourceNodeOptions, }; -use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType}; use servo_media::audio::param::ParamType; use crate::dom::audiobuffer::AudioBuffer; @@ -61,6 +61,7 @@ impl AudioBufferSourceNode { &window, context, node_id, + AudioNodeType::AudioBufferSourceNode, ParamType::PlaybackRate, AutomationRate::K_rate, *options.playbackRate, @@ -71,6 +72,7 @@ impl AudioBufferSourceNode { &window, context, node_id, + AudioNodeType::AudioBufferSourceNode, ParamType::Detune, AutomationRate::K_rate, *options.detune, diff --git a/components/script/dom/audiolistener.rs b/components/script/dom/audiolistener.rs index e26389b0c8c..ad8bdb071ba 100644 --- a/components/script/dom/audiolistener.rs +++ b/components/script/dom/audiolistener.rs @@ -5,6 +5,7 @@ use std::f32; use dom_struct::dom_struct; +use servo_media::audio::node::AudioNodeType; use servo_media::audio::param::{ParamDir, ParamType}; use crate::dom::audioparam::AudioParam; @@ -41,6 +42,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Position(ParamDir::X), AutomationRate::A_rate, 0., // default value @@ -51,6 +53,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Position(ParamDir::Y), AutomationRate::A_rate, 0., // default value @@ -61,6 +64,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Position(ParamDir::Z), AutomationRate::A_rate, 0., // default value @@ -71,6 +75,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Forward(ParamDir::X), AutomationRate::A_rate, 0., // default value @@ -81,6 +86,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Forward(ParamDir::Y), AutomationRate::A_rate, 0., // default value @@ -91,6 +97,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Forward(ParamDir::Z), AutomationRate::A_rate, -1., // default value @@ -101,6 +108,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Up(ParamDir::X), AutomationRate::A_rate, 0., // default value @@ -111,6 +119,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Up(ParamDir::Y), AutomationRate::A_rate, 1., // default value @@ -121,6 +130,7 @@ impl AudioListener { window, context, node, + AudioNodeType::AudioListenerNode, ParamType::Up(ParamDir::Z), AutomationRate::A_rate, 0., // default value diff --git a/components/script/dom/audioparam.rs b/components/script/dom/audioparam.rs index 509252c1485..b8e434bd528 100644 --- a/components/script/dom/audioparam.rs +++ b/components/script/dom/audioparam.rs @@ -7,7 +7,7 @@ use std::sync::mpsc; use dom_struct::dom_struct; use servo_media::audio::graph::NodeId; -use servo_media::audio::node::AudioNodeMessage; +use servo_media::audio::node::{AudioNodeMessage, AudioNodeType}; use servo_media::audio::param::{ParamRate, ParamType, RampKind, UserAutomationEvent}; use crate::dom::baseaudiocontext::BaseAudioContext; @@ -29,6 +29,9 @@ pub struct AudioParam { node: NodeId, #[ignore_malloc_size_of = "servo_media"] #[no_trace] + node_type: AudioNodeType, + #[ignore_malloc_size_of = "servo_media"] + #[no_trace] param: ParamType, automation_rate: Cell, default_value: f32, @@ -40,6 +43,7 @@ impl AudioParam { pub fn new_inherited( context: &BaseAudioContext, node: NodeId, + node_type: AudioNodeType, param: ParamType, automation_rate: AutomationRate, default_value: f32, @@ -50,6 +54,7 @@ impl AudioParam { reflector_: Reflector::new(), context: Dom::from_ref(context), node, + node_type, param, automation_rate: Cell::new(automation_rate), default_value, @@ -63,6 +68,7 @@ impl AudioParam { window: &Window, context: &BaseAudioContext, node: NodeId, + node_type: AudioNodeType, param: ParamType, automation_rate: AutomationRate, default_value: f32, @@ -72,6 +78,7 @@ impl AudioParam { let audio_param = AudioParam::new_inherited( context, node, + node_type, param, automation_rate, default_value, @@ -109,12 +116,24 @@ impl AudioParamMethods for AudioParam { } // https://webaudio.github.io/web-audio-api/#dom-audioparam-automationrate - fn SetAutomationRate(&self, automation_rate: AutomationRate) { + fn SetAutomationRate(&self, automation_rate: AutomationRate) -> Fallible<()> { + // > AudioBufferSourceNode + // > The AudioParams playbackRate and detune MUST be "k-rate". An InvalidStateError must be + // > thrown if the rate is changed to "a-rate". + if automation_rate == AutomationRate::A_rate && + self.node_type == AudioNodeType::AudioBufferSourceNode && + (self.param == ParamType::Detune || self.param == ParamType::PlaybackRate) + { + return Err(Error::InvalidState); + } + self.automation_rate.set(automation_rate); self.message_node(AudioNodeMessage::SetParamRate( self.param, automation_rate.into(), )); + + Ok(()) } // https://webaudio.github.io/web-audio-api/#dom-audioparam-value diff --git a/components/script/dom/bindings/buffer_source.rs b/components/script/dom/bindings/buffer_source.rs index 74195e8f3ca..80fa00e408b 100644 --- a/components/script/dom/bindings/buffer_source.rs +++ b/components/script/dom/bindings/buffer_source.rs @@ -269,8 +269,9 @@ where buffer.get() }, }); - let Ok(array) = array as Result>, &mut ()> else - { + let Ok(array) = + array as Result>, &mut ()> + else { return Err(()); }; unsafe { @@ -306,8 +307,9 @@ where buffer.get() }, }); - let Ok(mut array) = array as Result>, &mut ()> else - { + let Ok(mut array) = + array as Result>, &mut ()> + else { return Err(()); }; unsafe { diff --git a/components/script/dom/biquadfilternode.rs b/components/script/dom/biquadfilternode.rs index 53daa65c2df..385b002277a 100644 --- a/components/script/dom/biquadfilternode.rs +++ b/components/script/dom/biquadfilternode.rs @@ -10,7 +10,7 @@ use js::rust::HandleObject; use servo_media::audio::biquad_filter_node::{ BiquadFilterNodeMessage, BiquadFilterNodeOptions, FilterType, }; -use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage}; +use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage, AudioNodeType}; use servo_media::audio::param::ParamType; use crate::dom::audionode::AudioNode; @@ -62,6 +62,7 @@ impl BiquadFilterNode { window, context, node.node_id(), + AudioNodeType::BiquadFilterNode, ParamType::Gain, AutomationRate::A_rate, options.gain, // default value @@ -72,6 +73,7 @@ impl BiquadFilterNode { window, context, node.node_id(), + AudioNodeType::BiquadFilterNode, ParamType::Q, AutomationRate::A_rate, options.q, // default value @@ -82,6 +84,7 @@ impl BiquadFilterNode { window, context, node.node_id(), + AudioNodeType::BiquadFilterNode, ParamType::Frequency, AutomationRate::A_rate, options.frequency, // default value @@ -92,6 +95,7 @@ impl BiquadFilterNode { window, context, node.node_id(), + AudioNodeType::BiquadFilterNode, ParamType::Detune, AutomationRate::A_rate, options.detune, // default value diff --git a/components/script/dom/constantsourcenode.rs b/components/script/dom/constantsourcenode.rs index 88a4d5eaabc..1285ead095e 100644 --- a/components/script/dom/constantsourcenode.rs +++ b/components/script/dom/constantsourcenode.rs @@ -7,7 +7,7 @@ use std::f32; use dom_struct::dom_struct; use js::rust::HandleObject; use servo_media::audio::constant_source_node::ConstantSourceNodeOptions as ServoMediaConstantSourceOptions; -use servo_media::audio::node::AudioNodeInit; +use servo_media::audio::node::{AudioNodeInit, AudioNodeType}; use servo_media::audio::param::ParamType; use crate::dom::audioparam::AudioParam; @@ -48,6 +48,7 @@ impl ConstantSourceNode { window, context, node_id, + AudioNodeType::ConstantSourceNode, ParamType::Offset, AutomationRate::A_rate, *options.offset, diff --git a/components/script/dom/crypto.rs b/components/script/dom/crypto.rs index e337c6ebb01..6abc8b36fa9 100644 --- a/components/script/dom/crypto.rs +++ b/components/script/dom/crypto.rs @@ -29,7 +29,7 @@ impl Crypto { fn new_inherited() -> Crypto { Crypto { reflector_: Reflector::new(), - rng: DomRefCell::new(ServoRng::new()), + rng: DomRefCell::new(ServoRng::default()), } } diff --git a/components/script/dom/cssgroupingrule.rs b/components/script/dom/cssgroupingrule.rs index c3fa70de189..3e30104bf61 100644 --- a/components/script/dom/cssgroupingrule.rs +++ b/components/script/dom/cssgroupingrule.rs @@ -5,9 +5,10 @@ use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::{Locked, SharedRwLock}; -use style::stylesheets::CssRules as StyleCssRules; +use style::stylesheets::{CssRuleTypes, CssRules as StyleCssRules}; use crate::dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods; +use crate::dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRule_Binding::CSSRuleMethods; use crate::dom::bindings::error::{ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomObject; @@ -67,7 +68,10 @@ impl CSSGroupingRuleMethods for CSSGroupingRule { // https://drafts.csswg.org/cssom/#dom-cssgroupingrule-insertrule fn InsertRule(&self, rule: DOMString, index: u32) -> Fallible { - self.rulelist().insert_rule(&rule, index, /* nested */ true) + // TODO: this should accumulate the rule types of all ancestors. + let containing_rule_types = CssRuleTypes::from_bits(self.cssrule.Type().into()); + self.rulelist() + .insert_rule(&rule, index, containing_rule_types) } // https://drafts.csswg.org/cssom/#dom-cssgroupingrule-deleterule diff --git a/components/script/dom/cssimportrule.rs b/components/script/dom/cssimportrule.rs index 3b4c3afcd41..b88e1e7dd5c 100644 --- a/components/script/dom/cssimportrule.rs +++ b/components/script/dom/cssimportrule.rs @@ -5,8 +5,11 @@ use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::{Locked, ToCssWithGuard}; +use style::stylesheets::import_rule::ImportLayer; use style::stylesheets::ImportRule; +use style_traits::ToCss; +use crate::dom::bindings::codegen::Bindings::CSSImportRuleBinding::CSSImportRuleMethods; use crate::dom::bindings::reflector::reflect_dom_object; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; @@ -60,3 +63,15 @@ impl SpecificCSSRule for CSSImportRule { .into() } } + +impl CSSImportRuleMethods for CSSImportRule { + /// + fn GetLayerName(&self) -> Option { + let guard = self.cssrule.shared_lock().read(); + match &self.import_rule.read_with(&guard).layer { + ImportLayer::None => None, + ImportLayer::Anonymous => Some(DOMString::new()), + ImportLayer::Named(name) => Some(DOMString::from_string(name.to_css_string())), + } + } +} diff --git a/components/script/dom/csslayerblockrule.rs b/components/script/dom/csslayerblockrule.rs new file mode 100644 index 00000000000..08779e62f98 --- /dev/null +++ b/components/script/dom/csslayerblockrule.rs @@ -0,0 +1,78 @@ +/* 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 servo_arc::Arc; +use style::shared_lock::ToCssWithGuard; +use style::stylesheets::LayerBlockRule; +use style_traits::ToCss; + +use crate::dom::bindings::codegen::Bindings::CSSLayerBlockRuleBinding::CSSLayerBlockRuleMethods; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::cssgroupingrule::CSSGroupingRule; +use crate::dom::cssrule::SpecificCSSRule; +use crate::dom::cssstylesheet::CSSStyleSheet; +use crate::dom::window::Window; + +#[dom_struct] +pub struct CSSLayerBlockRule { + cssgroupingrule: CSSGroupingRule, + #[ignore_malloc_size_of = "Arc"] + #[no_trace] + layerblockrule: Arc, +} + +impl CSSLayerBlockRule { + pub fn new_inherited( + parent_stylesheet: &CSSStyleSheet, + layerblockrule: Arc, + ) -> CSSLayerBlockRule { + CSSLayerBlockRule { + cssgroupingrule: CSSGroupingRule::new_inherited( + parent_stylesheet, + layerblockrule.rules.clone(), + ), + layerblockrule, + } + } + + #[allow(crown::unrooted_must_root)] + pub fn new( + window: &Window, + parent_stylesheet: &CSSStyleSheet, + layerblockrule: Arc, + ) -> DomRoot { + reflect_dom_object( + Box::new(CSSLayerBlockRule::new_inherited( + parent_stylesheet, + layerblockrule, + )), + window, + ) + } +} + +impl SpecificCSSRule for CSSLayerBlockRule { + fn ty(&self) -> u16 { + 0 + } + + fn get_css(&self) -> DOMString { + let guard = self.cssgroupingrule.shared_lock().read(); + self.layerblockrule.to_css_string(&guard).into() + } +} + +impl CSSLayerBlockRuleMethods for CSSLayerBlockRule { + /// + fn Name(&self) -> DOMString { + if let Some(name) = &self.layerblockrule.name { + DOMString::from_string(name.to_css_string()) + } else { + DOMString::new() + } + } +} diff --git a/components/script/dom/csslayerstatementrule.rs b/components/script/dom/csslayerstatementrule.rs new file mode 100644 index 00000000000..f2dbfeca0fd --- /dev/null +++ b/components/script/dom/csslayerstatementrule.rs @@ -0,0 +1,79 @@ +/* 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 js::jsval::JSVal; +use servo_arc::Arc; +use style::shared_lock::ToCssWithGuard; +use style::stylesheets::LayerStatementRule; +use style_traits::ToCss; + +use crate::dom::bindings::codegen::Bindings::CSSLayerStatementRuleBinding::CSSLayerStatementRuleMethods; +use crate::dom::bindings::reflector::reflect_dom_object; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::utils::to_frozen_array; +use crate::dom::cssrule::{CSSRule, SpecificCSSRule}; +use crate::dom::cssstylesheet::CSSStyleSheet; +use crate::dom::window::Window; +use crate::script_runtime::JSContext as SafeJSContext; + +#[dom_struct] +pub struct CSSLayerStatementRule { + cssrule: CSSRule, + #[ignore_malloc_size_of = "Arc"] + #[no_trace] + layerstatementrule: Arc, +} + +impl CSSLayerStatementRule { + pub fn new_inherited( + parent_stylesheet: &CSSStyleSheet, + layerstatementrule: Arc, + ) -> CSSLayerStatementRule { + CSSLayerStatementRule { + cssrule: CSSRule::new_inherited(parent_stylesheet), + layerstatementrule, + } + } + + #[allow(crown::unrooted_must_root)] + pub fn new( + window: &Window, + parent_stylesheet: &CSSStyleSheet, + layerstatementrule: Arc, + ) -> DomRoot { + reflect_dom_object( + Box::new(CSSLayerStatementRule::new_inherited( + parent_stylesheet, + layerstatementrule, + )), + window, + ) + } +} + +impl SpecificCSSRule for CSSLayerStatementRule { + fn ty(&self) -> u16 { + 0 + } + + fn get_css(&self) -> DOMString { + let guard = self.cssrule.shared_lock().read(); + self.layerstatementrule.to_css_string(&guard).into() + } +} + +impl CSSLayerStatementRuleMethods for CSSLayerStatementRule { + /// + fn NameList(&self, cx: SafeJSContext) -> JSVal { + let names: Vec = self + .layerstatementrule + .names + .iter() + .map(|name| DOMString::from_string(name.to_css_string())) + .collect(); + to_frozen_array(names.as_slice(), cx) + } +} diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index d3fd12f9948..bafc803f721 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -17,6 +17,8 @@ use crate::dom::cssfontfacerule::CSSFontFaceRule; use crate::dom::cssimportrule::CSSImportRule; use crate::dom::csskeyframerule::CSSKeyframeRule; use crate::dom::csskeyframesrule::CSSKeyframesRule; +use crate::dom::csslayerblockrule::CSSLayerBlockRule; +use crate::dom::csslayerstatementrule::CSSLayerStatementRule; use crate::dom::cssmediarule::CSSMediaRule; use crate::dom::cssnamespacerule::CSSNamespaceRule; use crate::dom::cssstylerule::CSSStyleRule; @@ -62,6 +64,10 @@ impl CSSRule { rule as &dyn SpecificCSSRule } else if let Some(rule) = self.downcast::() { rule as &dyn SpecificCSSRule + } else if let Some(rule) = self.downcast::() { + rule as &dyn SpecificCSSRule + } else if let Some(rule) = self.downcast::() { + rule as &dyn SpecificCSSRule } else { unreachable!() } @@ -102,10 +108,14 @@ impl CSSRule { StyleCssRule::Page(_) => unreachable!(), StyleCssRule::Container(_) => unimplemented!(), // TODO StyleCssRule::Document(_) => unimplemented!(), // TODO - StyleCssRule::LayerBlock(_) => unimplemented!(), // TODO - StyleCssRule::LayerStatement(_) => unimplemented!(), // TODO + StyleCssRule::LayerBlock(s) => { + DomRoot::upcast(CSSLayerBlockRule::new(window, parent_stylesheet, s)) + }, + StyleCssRule::LayerStatement(s) => { + DomRoot::upcast(CSSLayerStatementRule::new(window, parent_stylesheet, s)) + }, StyleCssRule::FontPaletteValues(_) => unimplemented!(), // TODO - StyleCssRule::Property(_) => unimplemented!(), // TODO + StyleCssRule::Property(_) => unimplemented!(), // TODO } } diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index ac345f4f22a..7d660424c98 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -8,7 +8,7 @@ use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::Locked; use style::stylesheets::{ - AllowImportRules, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError, + AllowImportRules, CssRuleTypes, CssRules, CssRulesHelpers, KeyframesRule, RulesMutateError, StylesheetLoader as StyleStylesheetLoader, }; @@ -92,7 +92,12 @@ impl CSSRuleList { /// Should only be called for CssRules-backed rules. Use append_lazy_rule /// for keyframes-backed rules. - pub fn insert_rule(&self, rule: &str, idx: u32, nested: bool) -> Fallible { + pub fn insert_rule( + &self, + rule: &str, + idx: u32, + containing_rule_types: CssRuleTypes, + ) -> Fallible { let css_rules = if let RulesSource::Rules(ref rules) = self.rules { rules } else { @@ -117,7 +122,7 @@ impl CSSRuleList { rule, &parent_stylesheet.contents, index, - nested, + containing_rule_types, loader.as_ref().map(|l| l as &dyn StyleStylesheetLoader), AllowImportRules::Yes, )?; diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index d6c6892c431..60b9ae6fb0f 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -2,8 +2,11 @@ * 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::cmp::Ordering; + use dom_struct::dom_struct; use html5ever::local_name; +use lazy_static::lazy_static; use servo_arc::Arc; use servo_url::ServoUrl; use style::attr::AttrValue; @@ -342,9 +345,44 @@ impl CSSStyleDeclaration { } } +lazy_static! { + static ref ENABLED_LONGHAND_PROPERTIES: Vec = { + // The 'all' shorthand contains all the enabled longhands with 2 exceptions: + // 'direction' and 'unicode-bidi', so these must be added afterward. + let mut enabled_longhands: Vec = ShorthandId::All.longhands().collect(); + if PropertyId::Longhand(LonghandId::Direction).enabled_for_all_content() { + enabled_longhands.push(LonghandId::Direction); + } + if PropertyId::Longhand(LonghandId::UnicodeBidi).enabled_for_all_content() { + enabled_longhands.push(LonghandId::UnicodeBidi); + } + + // Sort lexicographically, but with vendor-prefixed properties after standard ones. + enabled_longhands.sort_unstable_by(|a, b| { + let a = a.name(); + let b = b.name(); + let is_a_vendor_prefixed = a.starts_with("-"); + let is_b_vendor_prefixed = b.starts_with("-"); + if is_a_vendor_prefixed == is_b_vendor_prefixed { + a.partial_cmp(b).unwrap() + } else if is_b_vendor_prefixed { + Ordering::Less + } else { + Ordering::Greater + } + }); + enabled_longhands + }; +} + impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-length fn Length(&self) -> u32 { + if self.readonly { + // Readonly style declarations are used for getComputedStyle. + // TODO: include custom properties whose computed value is not the guaranteed-invalid value. + return ENABLED_LONGHAND_PROPERTIES.len() as u32; + } self.owner.with_block(|pdb| pdb.declarations().len() as u32) } @@ -364,6 +402,10 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority fn GetPropertyPriority(&self, property: DOMString) -> DOMString { + if self.readonly { + // Readonly style declarations are used for getComputedStyle. + return DOMString::new(); + } let id = match PropertyId::parse_enabled_for_all_content(&property) { Ok(id) => id, Err(..) => return DOMString::new(), @@ -432,6 +474,12 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface fn IndexedGetter(&self, index: u32) -> Option { + if self.readonly { + // Readonly style declarations are used for getComputedStyle. + // TODO: include custom properties whose computed value is not the guaranteed-invalid value. + let longhand = ENABLED_LONGHAND_PROPERTIES.get(index as usize)?; + return Some(DOMString::from(longhand.name())); + } self.owner.with_block(|pdb| { let declaration = pdb.declarations().get(index as usize)?; Some(DOMString::from(declaration.id().name())) @@ -440,6 +488,10 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext fn CssText(&self) -> DOMString { + if self.readonly { + // Readonly style declarations are used for getComputedStyle. + return DOMString::new(); + } self.owner.with_block(|pdb| { let mut serialization = String::new(); pdb.to_css(&mut serialization).unwrap(); diff --git a/components/script/dom/cssstylerule.rs b/components/script/dom/cssstylerule.rs index a5d41d5d0d6..0d1e6480713 100644 --- a/components/script/dom/cssstylerule.rs +++ b/components/script/dom/cssstylerule.rs @@ -6,7 +6,7 @@ use std::mem; use cssparser::{Parser as CssParser, ParserInput as CssParserInput, ToCss}; use dom_struct::dom_struct; -use selectors::parser::SelectorList; +use selectors::parser::{ParseRelative, SelectorList}; use servo_arc::Arc; use style::selector_parser::SelectorParser; use style::shared_lock::{Locked, ToCssWithGuard}; @@ -111,7 +111,9 @@ impl CSSStyleRuleMethods for CSSStyleRule { }; let mut css_parser = CssParserInput::new(&*value); let mut css_parser = CssParser::new(&mut css_parser); - if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser) { + // TODO: Maybe allow setting relative selectors from the OM, if we're in a nested style + // rule? + if let Ok(mut s) = SelectorList::parse(&parser, &mut css_parser, ParseRelative::No) { // This mirrors what we do in CSSStyleOwner::mutate_associated_block. let mut guard = self.cssrule.shared_lock().write(); let stylerule = self.stylerule.write_with(&mut guard); diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs index 64c57461871..9fee49f3945 100644 --- a/components/script/dom/cssstylesheet.rs +++ b/components/script/dom/cssstylesheet.rs @@ -7,7 +7,7 @@ use std::cell::Cell; use dom_struct::dom_struct; use servo_arc::Arc; use style::shared_lock::SharedRwLock; -use style::stylesheets::Stylesheet as StyleStyleSheet; +use style::stylesheets::{CssRuleTypes, Stylesheet as StyleStyleSheet}; use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; @@ -129,7 +129,7 @@ impl CSSStyleSheetMethods for CSSStyleSheet { return Err(Error::Security); } self.rulelist() - .insert_rule(&rule, index, /* nested */ false) + .insert_rule(&rule, index, CssRuleTypes::default()) } // https://drafts.csswg.org/cssom/#dom-cssstylesheet-deleterule diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 512c3870b1f..019cc07529f 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -140,6 +140,7 @@ use crate::dom::htmlhtmlelement::HTMLHtmlElement; use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::htmlimageelement::HTMLImageElement; use crate::dom::htmlinputelement::HTMLInputElement; +use crate::dom::htmlmetaelement::RefreshRedirectDue; use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult}; use crate::dom::htmltextareaelement::HTMLTextAreaElement; use crate::dom::htmltitleelement::HTMLTitleElement; @@ -233,6 +234,17 @@ enum FocusTransaction { InTransaction(Option>), } +/// Information about a declarative refresh +#[derive(JSTraceable, MallocSizeOf)] +pub enum DeclarativeRefresh { + PendingLoad { + #[no_trace] + url: ServoUrl, + time: u64, + }, + CreatedAfterLoad, +} + /// #[dom_struct] pub struct Document { @@ -435,6 +447,8 @@ pub struct Document { animations: DomRefCell, /// The nearest inclusive ancestors to all the nodes that require a restyle. dirty_root: MutNullableDom, + /// + declarative_refresh: DomRefCell>, } #[derive(JSTraceable, MallocSizeOf)] @@ -2399,6 +2413,19 @@ impl Document { task!(completely_loaded: move || { let document = document.root(); document.completely_loaded.set(true); + if let Some(DeclarativeRefresh::PendingLoad { + url, + time + }) = &*document.declarative_refresh.borrow() { + // https://html.spec.whatwg.org/multipage/#shared-declarative-refresh-steps + document.window.upcast::().schedule_callback( + OneshotTimerCallback::RefreshRedirectDue(RefreshRedirectDue { + window: window_from_node(&*document), + url: url.clone(), + }), + MsDuration::new(time.saturating_mul(1000)), + ); + } // Note: this will, among others, result in the "iframe-load-event-steps" being run. // https://html.spec.whatwg.org/multipage/#iframe-load-event-steps document.notify_constellation_load(); @@ -2409,6 +2436,10 @@ impl Document { } } + pub fn completely_loaded(&self) -> bool { + self.completely_loaded.get() + } + // https://html.spec.whatwg.org/multipage/#pending-parsing-blocking-script pub fn set_pending_parsing_blocking_script( &self, @@ -3195,6 +3226,7 @@ impl Document { }, animations: DomRefCell::new(Animations::new()), dirty_root: Default::default(), + declarative_refresh: Default::default(), } } @@ -3940,6 +3972,13 @@ impl Document { pub(crate) fn cancel_animations_for_node(&self, node: &Node) { self.animations.borrow().cancel_animations_for_node(node); } + + pub(crate) fn will_declaratively_refresh(&self) -> bool { + self.declarative_refresh.borrow().is_some() + } + pub(crate) fn set_declarative_refresh(&self, refresh: DeclarativeRefresh) { + *self.declarative_refresh.borrow_mut() = Some(refresh); + } } impl Element { diff --git a/components/script/dom/gainnode.rs b/components/script/dom/gainnode.rs index e05a5d0f24c..29f2587164f 100644 --- a/components/script/dom/gainnode.rs +++ b/components/script/dom/gainnode.rs @@ -7,7 +7,7 @@ use std::f32; use dom_struct::dom_struct; use js::rust::HandleObject; use servo_media::audio::gain_node::GainNodeOptions; -use servo_media::audio::node::AudioNodeInit; +use servo_media::audio::node::{AudioNodeInit, AudioNodeType}; use servo_media::audio::param::ParamType; use crate::dom::audionode::AudioNode; @@ -51,6 +51,7 @@ impl GainNode { window, context, node.node_id(), + AudioNodeType::GainNode, ParamType::Gain, AutomationRate::A_rate, *options.gain, // default value diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 869a4e17346..4fbd375a579 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -10,16 +10,12 @@ use dom_struct::dom_struct; use webgpu::wgpu::command as wgpu_com; use webgpu::{self, wgt, WebGPU, WebGPURequest}; -use super::bindings::codegen::Bindings::WebGPUBinding::{ - GPUCommandBufferDescriptor, GPUImageCopyBuffer, GPUImageCopyTexture, GPUImageDataLayout, - GPULoadOp, GPUTextureAspect, -}; -use super::bindings::codegen::UnionTypes::DoubleSequenceOrGPUColorDict; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ - GPUCommandEncoderMethods, GPUComputePassDescriptor, GPUExtent3D, GPUOrigin3D, - GPURenderPassDescriptor, GPUSize64, GPUStoreOp, + GPUCommandBufferDescriptor, GPUCommandEncoderMethods, GPUComputePassDescriptor, GPUExtent3D, + GPUImageCopyBuffer, GPUImageCopyTexture, GPURenderPassDescriptor, GPUSize64, }; +use crate::dom::bindings::codegen::UnionTypes::DoubleSequenceOrGPUColorDict; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; @@ -28,7 +24,11 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::gpubuffer::GPUBuffer; use crate::dom::gpucommandbuffer::GPUCommandBuffer; use crate::dom::gpucomputepassencoder::GPUComputePassEncoder; -use crate::dom::gpudevice::{convert_texture_size_to_dict, convert_texture_size_to_wgt, GPUDevice}; +use crate::dom::gpuconvert::{ + convert_ic_buffer, convert_ic_texture, convert_load_op, convert_store_op, + convert_texture_size_to_dict, convert_texture_size_to_wgt, +}; +use crate::dom::gpudevice::GPUDevice; use crate::dom::gpurenderpassencoder::GPURenderPassEncoder; // TODO(sagudev): this is different now @@ -403,63 +403,3 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { ) } } - -fn convert_load_op(op: Option) -> wgpu_com::LoadOp { - match op { - Some(GPULoadOp::Load) => wgpu_com::LoadOp::Load, - Some(GPULoadOp::Clear) => wgpu_com::LoadOp::Clear, - None => wgpu_com::LoadOp::Clear, - } -} - -fn convert_store_op(op: Option) -> wgpu_com::StoreOp { - match op { - Some(GPUStoreOp::Store) => wgpu_com::StoreOp::Store, - Some(GPUStoreOp::Discard) => wgpu_com::StoreOp::Discard, - None => wgpu_com::StoreOp::Discard, - } -} - -fn convert_ic_buffer(ic_buffer: &GPUImageCopyBuffer) -> wgpu_com::ImageCopyBuffer { - wgpu_com::ImageCopyBuffer { - buffer: ic_buffer.buffer.id().0, - layout: convert_image_data_layout(&ic_buffer.parent), - } -} - -pub fn convert_ic_texture(ic_texture: &GPUImageCopyTexture) -> wgpu_com::ImageCopyTexture { - wgpu_com::ImageCopyTexture { - texture: ic_texture.texture.id().0, - mip_level: ic_texture.mipLevel, - origin: match ic_texture.origin { - Some(GPUOrigin3D::RangeEnforcedUnsignedLongSequence(ref v)) => { - let mut w = v.clone(); - w.resize(3, 0); - wgt::Origin3d { - x: w[0], - y: w[1], - z: w[2], - } - }, - Some(GPUOrigin3D::GPUOrigin3DDict(ref d)) => wgt::Origin3d { - x: d.x, - y: d.y, - z: d.z, - }, - None => wgt::Origin3d::default(), - }, - aspect: match ic_texture.aspect { - GPUTextureAspect::All => wgt::TextureAspect::All, - GPUTextureAspect::Stencil_only => wgt::TextureAspect::StencilOnly, - GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly, - }, - } -} - -pub fn convert_image_data_layout(data_layout: &GPUImageDataLayout) -> wgt::ImageDataLayout { - wgt::ImageDataLayout { - offset: data_layout.offset as wgt::BufferAddress, - bytes_per_row: data_layout.bytesPerRow, - rows_per_image: data_layout.rowsPerImage, - } -} diff --git a/components/script/dom/gpuconvert.rs b/components/script/dom/gpuconvert.rs new file mode 100644 index 00000000000..82b6b1dc2f0 --- /dev/null +++ b/components/script/dom/gpuconvert.rs @@ -0,0 +1,333 @@ +/* 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::borrow::Cow; + +use webgpu::wgpu::command as wgpu_com; +use webgpu::wgt; + +use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ + GPUAddressMode, GPUBlendComponent, GPUBlendFactor, GPUBlendOperation, GPUCompareFunction, + GPUCullMode, GPUExtent3D, GPUExtent3DDict, GPUFilterMode, GPUFrontFace, GPUImageCopyBuffer, + GPUImageCopyTexture, GPUImageDataLayout, GPUIndexFormat, GPULoadOp, GPUObjectDescriptorBase, + GPUOrigin3D, GPUPrimitiveState, GPUPrimitiveTopology, GPUStencilOperation, GPUStoreOp, + GPUTextureAspect, GPUTextureFormat, GPUTextureViewDimension, GPUVertexFormat, +}; + +pub fn convert_texture_format(format: GPUTextureFormat) -> wgt::TextureFormat { + match format { + GPUTextureFormat::R8unorm => wgt::TextureFormat::R8Unorm, + GPUTextureFormat::R8snorm => wgt::TextureFormat::R8Snorm, + GPUTextureFormat::R8uint => wgt::TextureFormat::R8Uint, + GPUTextureFormat::R8sint => wgt::TextureFormat::R8Sint, + GPUTextureFormat::R16uint => wgt::TextureFormat::R16Uint, + GPUTextureFormat::R16sint => wgt::TextureFormat::R16Sint, + GPUTextureFormat::R16float => wgt::TextureFormat::R16Float, + GPUTextureFormat::Rg8unorm => wgt::TextureFormat::Rg8Unorm, + GPUTextureFormat::Rg8snorm => wgt::TextureFormat::Rg8Snorm, + GPUTextureFormat::Rg8uint => wgt::TextureFormat::Rg8Uint, + GPUTextureFormat::Rg8sint => wgt::TextureFormat::Rg8Sint, + GPUTextureFormat::R32uint => wgt::TextureFormat::R32Uint, + GPUTextureFormat::R32sint => wgt::TextureFormat::R32Sint, + GPUTextureFormat::R32float => wgt::TextureFormat::R32Float, + GPUTextureFormat::Rg16uint => wgt::TextureFormat::Rg16Uint, + GPUTextureFormat::Rg16sint => wgt::TextureFormat::Rg16Sint, + GPUTextureFormat::Rg16float => wgt::TextureFormat::Rg16Float, + GPUTextureFormat::Rgba8unorm => wgt::TextureFormat::Rgba8Unorm, + GPUTextureFormat::Rgba8unorm_srgb => wgt::TextureFormat::Rgba8UnormSrgb, + GPUTextureFormat::Rgba8snorm => wgt::TextureFormat::Rgba8Snorm, + GPUTextureFormat::Rgba8uint => wgt::TextureFormat::Rgba8Uint, + GPUTextureFormat::Rgba8sint => wgt::TextureFormat::Rgba8Sint, + GPUTextureFormat::Bgra8unorm => wgt::TextureFormat::Bgra8Unorm, + GPUTextureFormat::Bgra8unorm_srgb => wgt::TextureFormat::Bgra8UnormSrgb, + GPUTextureFormat::Rgb10a2unorm => wgt::TextureFormat::Rgb10a2Unorm, + GPUTextureFormat::Rg32uint => wgt::TextureFormat::Rg32Uint, + GPUTextureFormat::Rg32sint => wgt::TextureFormat::Rg32Sint, + GPUTextureFormat::Rg32float => wgt::TextureFormat::Rg32Float, + GPUTextureFormat::Rgba16uint => wgt::TextureFormat::Rgba16Uint, + GPUTextureFormat::Rgba16sint => wgt::TextureFormat::Rgba16Sint, + GPUTextureFormat::Rgba16float => wgt::TextureFormat::Rgba16Float, + GPUTextureFormat::Rgba32uint => wgt::TextureFormat::Rgba32Uint, + GPUTextureFormat::Rgba32sint => wgt::TextureFormat::Rgba32Sint, + GPUTextureFormat::Rgba32float => wgt::TextureFormat::Rgba32Float, + GPUTextureFormat::Depth32float => wgt::TextureFormat::Depth32Float, + GPUTextureFormat::Depth24plus => wgt::TextureFormat::Depth24Plus, + GPUTextureFormat::Depth24plus_stencil8 => wgt::TextureFormat::Depth24PlusStencil8, + GPUTextureFormat::Bc1_rgba_unorm => wgt::TextureFormat::Bc1RgbaUnorm, + GPUTextureFormat::Bc1_rgba_unorm_srgb => wgt::TextureFormat::Bc1RgbaUnormSrgb, + GPUTextureFormat::Bc2_rgba_unorm => wgt::TextureFormat::Bc2RgbaUnorm, + GPUTextureFormat::Bc2_rgba_unorm_srgb => wgt::TextureFormat::Bc2RgbaUnormSrgb, + GPUTextureFormat::Bc3_rgba_unorm => wgt::TextureFormat::Bc3RgbaUnorm, + GPUTextureFormat::Bc3_rgba_unorm_srgb => wgt::TextureFormat::Bc3RgbaUnormSrgb, + GPUTextureFormat::Bc4_r_unorm => wgt::TextureFormat::Bc4RUnorm, + GPUTextureFormat::Bc4_r_snorm => wgt::TextureFormat::Bc4RSnorm, + GPUTextureFormat::Bc5_rg_unorm => wgt::TextureFormat::Bc5RgUnorm, + GPUTextureFormat::Bc5_rg_snorm => wgt::TextureFormat::Bc5RgSnorm, + GPUTextureFormat::Bc6h_rgb_ufloat => wgt::TextureFormat::Bc6hRgbUfloat, + GPUTextureFormat::Bc7_rgba_unorm => wgt::TextureFormat::Bc7RgbaUnorm, + GPUTextureFormat::Bc7_rgba_unorm_srgb => wgt::TextureFormat::Bc7RgbaUnormSrgb, + GPUTextureFormat::Rg11b10float => wgt::TextureFormat::Rg11b10Float, + GPUTextureFormat::Bc6h_rgb_float => wgt::TextureFormat::Bc6hRgbFloat, + } +} + +pub fn convert_texture_view_dimension( + dimension: GPUTextureViewDimension, +) -> wgt::TextureViewDimension { + match dimension { + GPUTextureViewDimension::_1d => wgt::TextureViewDimension::D1, + GPUTextureViewDimension::_2d => wgt::TextureViewDimension::D2, + GPUTextureViewDimension::_2d_array => wgt::TextureViewDimension::D2Array, + GPUTextureViewDimension::Cube => wgt::TextureViewDimension::Cube, + GPUTextureViewDimension::Cube_array => wgt::TextureViewDimension::CubeArray, + GPUTextureViewDimension::_3d => wgt::TextureViewDimension::D3, + } +} + +pub fn convert_texture_size_to_dict(size: &GPUExtent3D) -> GPUExtent3DDict { + match *size { + GPUExtent3D::GPUExtent3DDict(ref dict) => GPUExtent3DDict { + width: dict.width, + height: dict.height, + depthOrArrayLayers: dict.depthOrArrayLayers, + }, + GPUExtent3D::RangeEnforcedUnsignedLongSequence(ref v) => { + let mut w = v.clone(); + w.resize(3, 1); + GPUExtent3DDict { + width: w[0], + height: w[1], + depthOrArrayLayers: w[2], + } + }, + } +} + +pub fn convert_texture_size_to_wgt(size: &GPUExtent3DDict) -> wgt::Extent3d { + wgt::Extent3d { + width: size.width, + height: size.height, + depth_or_array_layers: size.depthOrArrayLayers, + } +} + +pub fn convert_image_data_layout(data_layout: &GPUImageDataLayout) -> wgt::ImageDataLayout { + wgt::ImageDataLayout { + offset: data_layout.offset as wgt::BufferAddress, + bytes_per_row: data_layout.bytesPerRow, + rows_per_image: data_layout.rowsPerImage, + } +} + +pub fn convert_vertex_format(format: GPUVertexFormat) -> wgt::VertexFormat { + match format { + GPUVertexFormat::Uint8x2 => wgt::VertexFormat::Uint8x2, + GPUVertexFormat::Uint8x4 => wgt::VertexFormat::Uint8x4, + GPUVertexFormat::Sint8x2 => wgt::VertexFormat::Sint8x2, + GPUVertexFormat::Sint8x4 => wgt::VertexFormat::Sint8x4, + GPUVertexFormat::Unorm8x2 => wgt::VertexFormat::Unorm8x2, + GPUVertexFormat::Unorm8x4 => wgt::VertexFormat::Unorm8x4, + GPUVertexFormat::Snorm8x2 => wgt::VertexFormat::Unorm8x2, + GPUVertexFormat::Snorm8x4 => wgt::VertexFormat::Unorm8x4, + GPUVertexFormat::Uint16x2 => wgt::VertexFormat::Uint16x2, + GPUVertexFormat::Uint16x4 => wgt::VertexFormat::Uint16x4, + GPUVertexFormat::Sint16x2 => wgt::VertexFormat::Sint16x2, + GPUVertexFormat::Sint16x4 => wgt::VertexFormat::Sint16x4, + GPUVertexFormat::Unorm16x2 => wgt::VertexFormat::Unorm16x2, + GPUVertexFormat::Unorm16x4 => wgt::VertexFormat::Unorm16x4, + GPUVertexFormat::Snorm16x2 => wgt::VertexFormat::Snorm16x2, + GPUVertexFormat::Snorm16x4 => wgt::VertexFormat::Snorm16x4, + GPUVertexFormat::Float16x2 => wgt::VertexFormat::Float16x2, + GPUVertexFormat::Float16x4 => wgt::VertexFormat::Float16x4, + GPUVertexFormat::Float32 => wgt::VertexFormat::Float32, + GPUVertexFormat::Float32x2 => wgt::VertexFormat::Float32x2, + GPUVertexFormat::Float32x3 => wgt::VertexFormat::Float32x3, + GPUVertexFormat::Float32x4 => wgt::VertexFormat::Float32x4, + GPUVertexFormat::Uint32 => wgt::VertexFormat::Uint32, + GPUVertexFormat::Uint32x2 => wgt::VertexFormat::Uint32x2, + GPUVertexFormat::Uint32x3 => wgt::VertexFormat::Uint32x3, + GPUVertexFormat::Uint32x4 => wgt::VertexFormat::Uint32x4, + GPUVertexFormat::Sint32 => wgt::VertexFormat::Sint32, + GPUVertexFormat::Sint32x2 => wgt::VertexFormat::Sint32x2, + GPUVertexFormat::Sint32x3 => wgt::VertexFormat::Sint32x3, + GPUVertexFormat::Sint32x4 => wgt::VertexFormat::Sint32x4, + } +} + +pub fn convert_primitive_state(primitive_state: &GPUPrimitiveState) -> wgt::PrimitiveState { + wgt::PrimitiveState { + topology: convert_primitive_topology(&primitive_state.topology), + strip_index_format: primitive_state.stripIndexFormat.map( + |index_format| match index_format { + GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16, + GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32, + }, + ), + front_face: match primitive_state.frontFace { + GPUFrontFace::Ccw => wgt::FrontFace::Ccw, + GPUFrontFace::Cw => wgt::FrontFace::Cw, + }, + cull_mode: match primitive_state.cullMode { + GPUCullMode::None => None, + GPUCullMode::Front => Some(wgt::Face::Front), + GPUCullMode::Back => Some(wgt::Face::Back), + }, + unclipped_depth: primitive_state.clampDepth, + ..Default::default() + } +} + +pub fn convert_primitive_topology( + primitive_topology: &GPUPrimitiveTopology, +) -> wgt::PrimitiveTopology { + match primitive_topology { + GPUPrimitiveTopology::Point_list => wgt::PrimitiveTopology::PointList, + GPUPrimitiveTopology::Line_list => wgt::PrimitiveTopology::LineList, + GPUPrimitiveTopology::Line_strip => wgt::PrimitiveTopology::LineStrip, + GPUPrimitiveTopology::Triangle_list => wgt::PrimitiveTopology::TriangleList, + GPUPrimitiveTopology::Triangle_strip => wgt::PrimitiveTopology::TriangleStrip, + } +} + +pub fn convert_address_mode(address_mode: GPUAddressMode) -> wgt::AddressMode { + match address_mode { + GPUAddressMode::Clamp_to_edge => wgt::AddressMode::ClampToEdge, + GPUAddressMode::Repeat => wgt::AddressMode::Repeat, + GPUAddressMode::Mirror_repeat => wgt::AddressMode::MirrorRepeat, + } +} + +pub fn convert_filter_mode(filter_mode: GPUFilterMode) -> wgt::FilterMode { + match filter_mode { + GPUFilterMode::Nearest => wgt::FilterMode::Nearest, + GPUFilterMode::Linear => wgt::FilterMode::Linear, + } +} + +pub fn convert_view_dimension( + view_dimension: GPUTextureViewDimension, +) -> wgt::TextureViewDimension { + match view_dimension { + GPUTextureViewDimension::_1d => wgt::TextureViewDimension::D1, + GPUTextureViewDimension::_2d => wgt::TextureViewDimension::D2, + GPUTextureViewDimension::_2d_array => wgt::TextureViewDimension::D2Array, + GPUTextureViewDimension::Cube => wgt::TextureViewDimension::Cube, + GPUTextureViewDimension::Cube_array => wgt::TextureViewDimension::CubeArray, + GPUTextureViewDimension::_3d => wgt::TextureViewDimension::D3, + } +} + +pub fn convert_compare_function(compare: GPUCompareFunction) -> wgt::CompareFunction { + match compare { + GPUCompareFunction::Never => wgt::CompareFunction::Never, + GPUCompareFunction::Less => wgt::CompareFunction::Less, + GPUCompareFunction::Equal => wgt::CompareFunction::Equal, + GPUCompareFunction::Less_equal => wgt::CompareFunction::LessEqual, + GPUCompareFunction::Greater => wgt::CompareFunction::Greater, + GPUCompareFunction::Not_equal => wgt::CompareFunction::NotEqual, + GPUCompareFunction::Greater_equal => wgt::CompareFunction::GreaterEqual, + GPUCompareFunction::Always => wgt::CompareFunction::Always, + } +} + +pub fn convert_blend_factor(factor: &GPUBlendFactor) -> wgt::BlendFactor { + match factor { + GPUBlendFactor::Zero => wgt::BlendFactor::Zero, + GPUBlendFactor::One => wgt::BlendFactor::One, + GPUBlendFactor::Src => wgt::BlendFactor::Src, + GPUBlendFactor::One_minus_src => wgt::BlendFactor::OneMinusSrc, + GPUBlendFactor::Src_alpha => wgt::BlendFactor::SrcAlpha, + GPUBlendFactor::One_minus_src_alpha => wgt::BlendFactor::OneMinusSrcAlpha, + GPUBlendFactor::Dst => wgt::BlendFactor::Dst, + GPUBlendFactor::One_minus_dst => wgt::BlendFactor::OneMinusDst, + GPUBlendFactor::Dst_alpha => wgt::BlendFactor::DstAlpha, + GPUBlendFactor::One_minus_dst_alpha => wgt::BlendFactor::OneMinusDstAlpha, + GPUBlendFactor::Src_alpha_saturated => wgt::BlendFactor::SrcAlphaSaturated, + GPUBlendFactor::Constant => wgt::BlendFactor::Constant, + GPUBlendFactor::One_minus_constant => wgt::BlendFactor::OneMinusConstant, + } +} + +pub fn convert_blend_component(blend_component: &GPUBlendComponent) -> wgt::BlendComponent { + wgt::BlendComponent { + src_factor: convert_blend_factor(&blend_component.srcFactor), + dst_factor: convert_blend_factor(&blend_component.dstFactor), + operation: match blend_component.operation { + GPUBlendOperation::Add => wgt::BlendOperation::Add, + GPUBlendOperation::Subtract => wgt::BlendOperation::Subtract, + GPUBlendOperation::Reverse_subtract => wgt::BlendOperation::ReverseSubtract, + GPUBlendOperation::Min => wgt::BlendOperation::Min, + GPUBlendOperation::Max => wgt::BlendOperation::Max, + }, + } +} + +pub fn convert_load_op(op: Option) -> wgpu_com::LoadOp { + match op { + Some(GPULoadOp::Load) => wgpu_com::LoadOp::Load, + Some(GPULoadOp::Clear) => wgpu_com::LoadOp::Clear, + None => wgpu_com::LoadOp::Clear, + } +} + +pub fn convert_store_op(op: Option) -> wgpu_com::StoreOp { + match op { + Some(GPUStoreOp::Store) => wgpu_com::StoreOp::Store, + Some(GPUStoreOp::Discard) => wgpu_com::StoreOp::Discard, + None => wgpu_com::StoreOp::Discard, + } +} + +pub fn convert_stencil_op(operation: GPUStencilOperation) -> wgt::StencilOperation { + match operation { + GPUStencilOperation::Keep => wgt::StencilOperation::Keep, + GPUStencilOperation::Zero => wgt::StencilOperation::Zero, + GPUStencilOperation::Replace => wgt::StencilOperation::Replace, + GPUStencilOperation::Invert => wgt::StencilOperation::Invert, + GPUStencilOperation::Increment_clamp => wgt::StencilOperation::IncrementClamp, + GPUStencilOperation::Decrement_clamp => wgt::StencilOperation::DecrementClamp, + GPUStencilOperation::Increment_wrap => wgt::StencilOperation::IncrementWrap, + GPUStencilOperation::Decrement_wrap => wgt::StencilOperation::DecrementWrap, + } +} + +pub fn convert_ic_buffer(ic_buffer: &GPUImageCopyBuffer) -> wgpu_com::ImageCopyBuffer { + wgpu_com::ImageCopyBuffer { + buffer: ic_buffer.buffer.id().0, + layout: convert_image_data_layout(&ic_buffer.parent), + } +} + +pub fn convert_ic_texture(ic_texture: &GPUImageCopyTexture) -> wgpu_com::ImageCopyTexture { + wgpu_com::ImageCopyTexture { + texture: ic_texture.texture.id().0, + mip_level: ic_texture.mipLevel, + origin: match ic_texture.origin { + Some(GPUOrigin3D::RangeEnforcedUnsignedLongSequence(ref v)) => { + let mut w = v.clone(); + w.resize(3, 0); + wgt::Origin3d { + x: w[0], + y: w[1], + z: w[2], + } + }, + Some(GPUOrigin3D::GPUOrigin3DDict(ref d)) => wgt::Origin3d { + x: d.x, + y: d.y, + z: d.z, + }, + None => wgt::Origin3d::default(), + }, + aspect: match ic_texture.aspect { + GPUTextureAspect::All => wgt::TextureAspect::All, + GPUTextureAspect::Stencil_only => wgt::TextureAspect::StencilOnly, + GPUTextureAspect::Depth_only => wgt::TextureAspect::DepthOnly, + }, + } +} + +pub fn convert_label(parent: &GPUObjectDescriptorBase) -> Option> { + parent.label.as_ref().map(|s| Cow::Owned(s.to_string())) +} diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 89b98f0d232..eb0f83bf4dc 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -20,10 +20,6 @@ use webgpu::wgpu::{ }; use webgpu::{self, wgt, ErrorScopeId, WebGPU, WebGPURequest}; -use super::bindings::codegen::Bindings::WebGPUBinding::{ - GPUBlendComponent, GPUBufferBindingType, GPUDeviceLostReason, GPUPrimitiveState, - GPUSamplerBindingType, GPUStorageTextureAccess, GPUTextureSampleType, GPUVertexStepMode, -}; use super::bindings::codegen::UnionTypes::GPUPipelineLayoutOrGPUAutoLayoutMode; use super::bindings::error::Fallible; use super::gpudevicelostinfo::GPUDeviceLostInfo; @@ -32,15 +28,13 @@ use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::EventBinding::EventInit; use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use crate::dom::bindings::codegen::Bindings::WebGPUBinding::{ - GPUAddressMode, GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBindingResource, - GPUBlendFactor, GPUBlendOperation, GPUBufferDescriptor, GPUCommandEncoderDescriptor, - GPUCompareFunction, GPUComputePipelineDescriptor, GPUCullMode, GPUDeviceMethods, GPUError, - GPUErrorFilter, GPUExtent3D, GPUExtent3DDict, GPUFilterMode, GPUFrontFace, GPUIndexFormat, - GPUObjectDescriptorBase, GPUPipelineLayoutDescriptor, GPUPrimitiveTopology, - GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerDescriptor, - GPUShaderModuleDescriptor, GPUStencilOperation, GPUSupportedLimitsMethods, - GPUTextureDescriptor, GPUTextureDimension, GPUTextureFormat, GPUTextureViewDimension, - GPUUncapturedErrorEventInit, GPUVertexFormat, + GPUBindGroupDescriptor, GPUBindGroupLayoutDescriptor, GPUBindingResource, GPUBufferBindingType, + GPUBufferDescriptor, GPUCommandEncoderDescriptor, GPUComputePipelineDescriptor, + GPUDeviceLostReason, GPUDeviceMethods, GPUError, GPUErrorFilter, GPUPipelineLayoutDescriptor, + GPURenderBundleEncoderDescriptor, GPURenderPipelineDescriptor, GPUSamplerBindingType, + GPUSamplerDescriptor, GPUShaderModuleDescriptor, GPUStorageTextureAccess, + GPUSupportedLimitsMethods, GPUTextureDescriptor, GPUTextureDimension, GPUTextureSampleType, + GPUUncapturedErrorEventInit, GPUVertexStepMode, }; use crate::dom::bindings::error::Error; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; @@ -55,6 +49,12 @@ use crate::dom::gpubindgrouplayout::GPUBindGroupLayout; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferMapInfo, GPUBufferState}; use crate::dom::gpucommandencoder::GPUCommandEncoder; use crate::dom::gpucomputepipeline::GPUComputePipeline; +use crate::dom::gpuconvert::{ + convert_address_mode, convert_blend_component, convert_compare_function, convert_filter_mode, + convert_label, convert_primitive_state, convert_stencil_op, convert_texture_format, + convert_texture_size_to_dict, convert_texture_size_to_wgt, convert_vertex_format, + convert_view_dimension, +}; use crate::dom::gpuoutofmemoryerror::GPUOutOfMemoryError; use crate::dom::gpupipelinelayout::GPUPipelineLayout; use crate::dom::gpuqueue::GPUQueue; @@ -1169,256 +1169,3 @@ impl Drop for GPUDevice { self.Destroy() } } - -fn convert_blend_component(blend_component: &GPUBlendComponent) -> wgt::BlendComponent { - wgt::BlendComponent { - src_factor: convert_blend_factor(&blend_component.srcFactor), - dst_factor: convert_blend_factor(&blend_component.dstFactor), - operation: match blend_component.operation { - GPUBlendOperation::Add => wgt::BlendOperation::Add, - GPUBlendOperation::Subtract => wgt::BlendOperation::Subtract, - GPUBlendOperation::Reverse_subtract => wgt::BlendOperation::ReverseSubtract, - GPUBlendOperation::Min => wgt::BlendOperation::Min, - GPUBlendOperation::Max => wgt::BlendOperation::Max, - }, - } -} - -fn convert_primitive_state(primitive_state: &GPUPrimitiveState) -> wgt::PrimitiveState { - wgt::PrimitiveState { - topology: convert_primitive_topology(&primitive_state.topology), - strip_index_format: primitive_state.stripIndexFormat.map( - |index_format| match index_format { - GPUIndexFormat::Uint16 => wgt::IndexFormat::Uint16, - GPUIndexFormat::Uint32 => wgt::IndexFormat::Uint32, - }, - ), - front_face: match primitive_state.frontFace { - GPUFrontFace::Ccw => wgt::FrontFace::Ccw, - GPUFrontFace::Cw => wgt::FrontFace::Cw, - }, - cull_mode: match primitive_state.cullMode { - GPUCullMode::None => None, - GPUCullMode::Front => Some(wgt::Face::Front), - GPUCullMode::Back => Some(wgt::Face::Back), - }, - unclipped_depth: primitive_state.clampDepth, - ..Default::default() - } -} - -fn convert_primitive_topology(primitive_topology: &GPUPrimitiveTopology) -> wgt::PrimitiveTopology { - match primitive_topology { - GPUPrimitiveTopology::Point_list => wgt::PrimitiveTopology::PointList, - GPUPrimitiveTopology::Line_list => wgt::PrimitiveTopology::LineList, - GPUPrimitiveTopology::Line_strip => wgt::PrimitiveTopology::LineStrip, - GPUPrimitiveTopology::Triangle_list => wgt::PrimitiveTopology::TriangleList, - GPUPrimitiveTopology::Triangle_strip => wgt::PrimitiveTopology::TriangleStrip, - } -} - -fn convert_view_dimension(view_dimension: GPUTextureViewDimension) -> wgt::TextureViewDimension { - match view_dimension { - GPUTextureViewDimension::_1d => wgt::TextureViewDimension::D1, - GPUTextureViewDimension::_2d => wgt::TextureViewDimension::D2, - GPUTextureViewDimension::_2d_array => wgt::TextureViewDimension::D2Array, - GPUTextureViewDimension::Cube => wgt::TextureViewDimension::Cube, - GPUTextureViewDimension::Cube_array => wgt::TextureViewDimension::CubeArray, - GPUTextureViewDimension::_3d => wgt::TextureViewDimension::D3, - } -} - -fn convert_address_mode(address_mode: GPUAddressMode) -> wgt::AddressMode { - match address_mode { - GPUAddressMode::Clamp_to_edge => wgt::AddressMode::ClampToEdge, - GPUAddressMode::Repeat => wgt::AddressMode::Repeat, - GPUAddressMode::Mirror_repeat => wgt::AddressMode::MirrorRepeat, - } -} - -fn convert_filter_mode(filter_mode: GPUFilterMode) -> wgt::FilterMode { - match filter_mode { - GPUFilterMode::Nearest => wgt::FilterMode::Nearest, - GPUFilterMode::Linear => wgt::FilterMode::Linear, - } -} - -fn convert_compare_function(compare: GPUCompareFunction) -> wgt::CompareFunction { - match compare { - GPUCompareFunction::Never => wgt::CompareFunction::Never, - GPUCompareFunction::Less => wgt::CompareFunction::Less, - GPUCompareFunction::Equal => wgt::CompareFunction::Equal, - GPUCompareFunction::Less_equal => wgt::CompareFunction::LessEqual, - GPUCompareFunction::Greater => wgt::CompareFunction::Greater, - GPUCompareFunction::Not_equal => wgt::CompareFunction::NotEqual, - GPUCompareFunction::Greater_equal => wgt::CompareFunction::GreaterEqual, - GPUCompareFunction::Always => wgt::CompareFunction::Always, - } -} - -fn convert_blend_factor(factor: &GPUBlendFactor) -> wgt::BlendFactor { - match factor { - GPUBlendFactor::Zero => wgt::BlendFactor::Zero, - GPUBlendFactor::One => wgt::BlendFactor::One, - GPUBlendFactor::Src => wgt::BlendFactor::Src, - GPUBlendFactor::One_minus_src => wgt::BlendFactor::OneMinusSrc, - GPUBlendFactor::Src_alpha => wgt::BlendFactor::SrcAlpha, - GPUBlendFactor::One_minus_src_alpha => wgt::BlendFactor::OneMinusSrcAlpha, - GPUBlendFactor::Dst => wgt::BlendFactor::Dst, - GPUBlendFactor::One_minus_dst => wgt::BlendFactor::OneMinusDst, - GPUBlendFactor::Dst_alpha => wgt::BlendFactor::DstAlpha, - GPUBlendFactor::One_minus_dst_alpha => wgt::BlendFactor::OneMinusDstAlpha, - GPUBlendFactor::Src_alpha_saturated => wgt::BlendFactor::SrcAlphaSaturated, - GPUBlendFactor::Constant => wgt::BlendFactor::Constant, - GPUBlendFactor::One_minus_constant => wgt::BlendFactor::OneMinusConstant, - } -} - -fn convert_stencil_op(operation: GPUStencilOperation) -> wgt::StencilOperation { - match operation { - GPUStencilOperation::Keep => wgt::StencilOperation::Keep, - GPUStencilOperation::Zero => wgt::StencilOperation::Zero, - GPUStencilOperation::Replace => wgt::StencilOperation::Replace, - GPUStencilOperation::Invert => wgt::StencilOperation::Invert, - GPUStencilOperation::Increment_clamp => wgt::StencilOperation::IncrementClamp, - GPUStencilOperation::Decrement_clamp => wgt::StencilOperation::DecrementClamp, - GPUStencilOperation::Increment_wrap => wgt::StencilOperation::IncrementWrap, - GPUStencilOperation::Decrement_wrap => wgt::StencilOperation::DecrementWrap, - } -} - -fn convert_vertex_format(format: GPUVertexFormat) -> wgt::VertexFormat { - match format { - GPUVertexFormat::Uint8x2 => wgt::VertexFormat::Uint8x2, - GPUVertexFormat::Uint8x4 => wgt::VertexFormat::Uint8x4, - GPUVertexFormat::Sint8x2 => wgt::VertexFormat::Sint8x2, - GPUVertexFormat::Sint8x4 => wgt::VertexFormat::Sint8x4, - GPUVertexFormat::Unorm8x2 => wgt::VertexFormat::Unorm8x2, - GPUVertexFormat::Unorm8x4 => wgt::VertexFormat::Unorm8x4, - GPUVertexFormat::Snorm8x2 => wgt::VertexFormat::Unorm8x2, - GPUVertexFormat::Snorm8x4 => wgt::VertexFormat::Unorm8x4, - GPUVertexFormat::Uint16x2 => wgt::VertexFormat::Uint16x2, - GPUVertexFormat::Uint16x4 => wgt::VertexFormat::Uint16x4, - GPUVertexFormat::Sint16x2 => wgt::VertexFormat::Sint16x2, - GPUVertexFormat::Sint16x4 => wgt::VertexFormat::Sint16x4, - GPUVertexFormat::Unorm16x2 => wgt::VertexFormat::Unorm16x2, - GPUVertexFormat::Unorm16x4 => wgt::VertexFormat::Unorm16x4, - GPUVertexFormat::Snorm16x2 => wgt::VertexFormat::Snorm16x2, - GPUVertexFormat::Snorm16x4 => wgt::VertexFormat::Snorm16x4, - GPUVertexFormat::Float16x2 => wgt::VertexFormat::Float16x2, - GPUVertexFormat::Float16x4 => wgt::VertexFormat::Float16x4, - GPUVertexFormat::Float32 => wgt::VertexFormat::Float32, - GPUVertexFormat::Float32x2 => wgt::VertexFormat::Float32x2, - GPUVertexFormat::Float32x3 => wgt::VertexFormat::Float32x3, - GPUVertexFormat::Float32x4 => wgt::VertexFormat::Float32x4, - GPUVertexFormat::Uint32 => wgt::VertexFormat::Uint32, - GPUVertexFormat::Uint32x2 => wgt::VertexFormat::Uint32x2, - GPUVertexFormat::Uint32x3 => wgt::VertexFormat::Uint32x3, - GPUVertexFormat::Uint32x4 => wgt::VertexFormat::Uint32x4, - GPUVertexFormat::Sint32 => wgt::VertexFormat::Sint32, - GPUVertexFormat::Sint32x2 => wgt::VertexFormat::Sint32x2, - GPUVertexFormat::Sint32x3 => wgt::VertexFormat::Sint32x3, - GPUVertexFormat::Sint32x4 => wgt::VertexFormat::Sint32x4, - } -} - -pub fn convert_texture_format(format: GPUTextureFormat) -> wgt::TextureFormat { - match format { - GPUTextureFormat::R8unorm => wgt::TextureFormat::R8Unorm, - GPUTextureFormat::R8snorm => wgt::TextureFormat::R8Snorm, - GPUTextureFormat::R8uint => wgt::TextureFormat::R8Uint, - GPUTextureFormat::R8sint => wgt::TextureFormat::R8Sint, - GPUTextureFormat::R16uint => wgt::TextureFormat::R16Uint, - GPUTextureFormat::R16sint => wgt::TextureFormat::R16Sint, - GPUTextureFormat::R16float => wgt::TextureFormat::R16Float, - GPUTextureFormat::Rg8unorm => wgt::TextureFormat::Rg8Unorm, - GPUTextureFormat::Rg8snorm => wgt::TextureFormat::Rg8Snorm, - GPUTextureFormat::Rg8uint => wgt::TextureFormat::Rg8Uint, - GPUTextureFormat::Rg8sint => wgt::TextureFormat::Rg8Sint, - GPUTextureFormat::R32uint => wgt::TextureFormat::R32Uint, - GPUTextureFormat::R32sint => wgt::TextureFormat::R32Sint, - GPUTextureFormat::R32float => wgt::TextureFormat::R32Float, - GPUTextureFormat::Rg16uint => wgt::TextureFormat::Rg16Uint, - GPUTextureFormat::Rg16sint => wgt::TextureFormat::Rg16Sint, - GPUTextureFormat::Rg16float => wgt::TextureFormat::Rg16Float, - GPUTextureFormat::Rgba8unorm => wgt::TextureFormat::Rgba8Unorm, - GPUTextureFormat::Rgba8unorm_srgb => wgt::TextureFormat::Rgba8UnormSrgb, - GPUTextureFormat::Rgba8snorm => wgt::TextureFormat::Rgba8Snorm, - GPUTextureFormat::Rgba8uint => wgt::TextureFormat::Rgba8Uint, - GPUTextureFormat::Rgba8sint => wgt::TextureFormat::Rgba8Sint, - GPUTextureFormat::Bgra8unorm => wgt::TextureFormat::Bgra8Unorm, - GPUTextureFormat::Bgra8unorm_srgb => wgt::TextureFormat::Bgra8UnormSrgb, - GPUTextureFormat::Rgb10a2unorm => wgt::TextureFormat::Rgb10a2Unorm, - GPUTextureFormat::Rg32uint => wgt::TextureFormat::Rg32Uint, - GPUTextureFormat::Rg32sint => wgt::TextureFormat::Rg32Sint, - GPUTextureFormat::Rg32float => wgt::TextureFormat::Rg32Float, - GPUTextureFormat::Rgba16uint => wgt::TextureFormat::Rgba16Uint, - GPUTextureFormat::Rgba16sint => wgt::TextureFormat::Rgba16Sint, - GPUTextureFormat::Rgba16float => wgt::TextureFormat::Rgba16Float, - GPUTextureFormat::Rgba32uint => wgt::TextureFormat::Rgba32Uint, - GPUTextureFormat::Rgba32sint => wgt::TextureFormat::Rgba32Sint, - GPUTextureFormat::Rgba32float => wgt::TextureFormat::Rgba32Float, - GPUTextureFormat::Depth32float => wgt::TextureFormat::Depth32Float, - GPUTextureFormat::Depth24plus => wgt::TextureFormat::Depth24Plus, - GPUTextureFormat::Depth24plus_stencil8 => wgt::TextureFormat::Depth24PlusStencil8, - GPUTextureFormat::Bc1_rgba_unorm => wgt::TextureFormat::Bc1RgbaUnorm, - GPUTextureFormat::Bc1_rgba_unorm_srgb => wgt::TextureFormat::Bc1RgbaUnormSrgb, - GPUTextureFormat::Bc2_rgba_unorm => wgt::TextureFormat::Bc2RgbaUnorm, - GPUTextureFormat::Bc2_rgba_unorm_srgb => wgt::TextureFormat::Bc2RgbaUnormSrgb, - GPUTextureFormat::Bc3_rgba_unorm => wgt::TextureFormat::Bc3RgbaUnorm, - GPUTextureFormat::Bc3_rgba_unorm_srgb => wgt::TextureFormat::Bc3RgbaUnormSrgb, - GPUTextureFormat::Bc4_r_unorm => wgt::TextureFormat::Bc4RUnorm, - GPUTextureFormat::Bc4_r_snorm => wgt::TextureFormat::Bc4RSnorm, - GPUTextureFormat::Bc5_rg_unorm => wgt::TextureFormat::Bc5RgUnorm, - GPUTextureFormat::Bc5_rg_snorm => wgt::TextureFormat::Bc5RgSnorm, - GPUTextureFormat::Bc6h_rgb_ufloat => wgt::TextureFormat::Bc6hRgbUfloat, - GPUTextureFormat::Bc7_rgba_unorm => wgt::TextureFormat::Bc7RgbaUnorm, - GPUTextureFormat::Bc7_rgba_unorm_srgb => wgt::TextureFormat::Bc7RgbaUnormSrgb, - GPUTextureFormat::Rg11b10float => wgt::TextureFormat::Rg11b10Float, - GPUTextureFormat::Bc6h_rgb_float => wgt::TextureFormat::Bc6hRgbFloat, - } -} - -pub fn convert_texture_view_dimension( - dimension: GPUTextureViewDimension, -) -> wgt::TextureViewDimension { - match dimension { - GPUTextureViewDimension::_1d => wgt::TextureViewDimension::D1, - GPUTextureViewDimension::_2d => wgt::TextureViewDimension::D2, - GPUTextureViewDimension::_2d_array => wgt::TextureViewDimension::D2Array, - GPUTextureViewDimension::Cube => wgt::TextureViewDimension::Cube, - GPUTextureViewDimension::Cube_array => wgt::TextureViewDimension::CubeArray, - GPUTextureViewDimension::_3d => wgt::TextureViewDimension::D3, - } -} - -pub fn convert_texture_size_to_dict(size: &GPUExtent3D) -> GPUExtent3DDict { - match *size { - GPUExtent3D::GPUExtent3DDict(ref dict) => GPUExtent3DDict { - width: dict.width, - height: dict.height, - depthOrArrayLayers: dict.depthOrArrayLayers, - }, - GPUExtent3D::RangeEnforcedUnsignedLongSequence(ref v) => { - let mut w = v.clone(); - w.resize(3, 1); - GPUExtent3DDict { - width: w[0], - height: w[1], - depthOrArrayLayers: w[2], - } - }, - } -} - -pub fn convert_texture_size_to_wgt(size: &GPUExtent3DDict) -> wgt::Extent3d { - wgt::Extent3d { - width: size.width, - height: size.height, - depth_or_array_layers: size.depthOrArrayLayers, - } -} - -pub fn convert_label(parent: &GPUObjectDescriptorBase) -> Option> { - parent.label.as_ref().map(|s| Cow::Owned(s.to_string())) -} diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs index 41dc1085529..de8766c3365 100644 --- a/components/script/dom/gpuqueue.rs +++ b/components/script/dom/gpuqueue.rs @@ -20,8 +20,11 @@ use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; use crate::dom::gpubuffer::{GPUBuffer, GPUBufferState}; use crate::dom::gpucommandbuffer::GPUCommandBuffer; -use crate::dom::gpucommandencoder::{convert_ic_texture, convert_image_data_layout}; -use crate::dom::gpudevice::{convert_texture_size_to_dict, convert_texture_size_to_wgt, GPUDevice}; +use crate::dom::gpuconvert::{ + convert_ic_texture, convert_image_data_layout, convert_texture_size_to_dict, + convert_texture_size_to_wgt, +}; +use crate::dom::gpudevice::GPUDevice; #[dom_struct] pub struct GPUQueue { diff --git a/components/script/dom/gpurenderbundleencoder.rs b/components/script/dom/gpurenderbundleencoder.rs index a81d7ad6891..6287b8233d7 100644 --- a/components/script/dom/gpurenderbundleencoder.rs +++ b/components/script/dom/gpurenderbundleencoder.rs @@ -17,7 +17,8 @@ use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; use crate::dom::gpubindgroup::GPUBindGroup; use crate::dom::gpubuffer::GPUBuffer; -use crate::dom::gpudevice::{convert_label, GPUDevice}; +use crate::dom::gpuconvert::convert_label; +use crate::dom::gpudevice::GPUDevice; use crate::dom::gpurenderbundle::GPURenderBundle; use crate::dom::gpurenderpipeline::GPURenderPipeline; diff --git a/components/script/dom/gputexture.rs b/components/script/dom/gputexture.rs index d3058f1e5dd..0589e36dd52 100644 --- a/components/script/dom/gputexture.rs +++ b/components/script/dom/gputexture.rs @@ -19,9 +19,10 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::USVString; use crate::dom::globalscope::GlobalScope; -use crate::dom::gpudevice::{ - convert_label, convert_texture_format, convert_texture_view_dimension, GPUDevice, +use crate::dom::gpuconvert::{ + convert_label, convert_texture_format, convert_texture_view_dimension, }; +use crate::dom::gpudevice::GPUDevice; use crate::dom::gputextureview::GPUTextureView; #[dom_struct] diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index 61ec43ac96a..65c870221f7 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -2,29 +2,56 @@ * 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::str::FromStr; + use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use js::rust::HandleObject; +use regex::bytes::Regex; +use script_traits::{HistoryEntryReplacement, MsDuration}; +use servo_url::ServoUrl; use style::str::HTML_SPACE_CHARACTERS; use crate::dom::attr::Attr; use crate::dom::bindings::codegen::Bindings::HTMLMetaElementBinding::HTMLMetaElementMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; 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::document::{DeclarativeRefresh, Document}; use crate::dom::element::{AttributeMutation, Element}; +use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlheadelement::HTMLHeadElement; -use crate::dom::node::{BindContext, Node, UnbindContext}; +use crate::dom::location::NavigationType; +use crate::dom::node::{document_from_node, window_from_node, BindContext, Node, UnbindContext}; use crate::dom::virtualmethods::VirtualMethods; +use crate::dom::window::Window; +use crate::timers::OneshotTimerCallback; #[dom_struct] pub struct HTMLMetaElement { htmlelement: HTMLElement, } +#[derive(JSTraceable, MallocSizeOf)] +pub struct RefreshRedirectDue { + #[no_trace] + pub url: ServoUrl, + #[ignore_malloc_size_of = "non-owning"] + pub window: DomRoot, +} +impl RefreshRedirectDue { + pub fn invoke(self) { + self.window.Location().navigate( + self.url.clone(), + HistoryEntryReplacement::Enabled, + NavigationType::DeclarativeRefresh, + ); + } +} + impl HTMLMetaElement { fn new_inherited( local_name: LocalName, @@ -58,6 +85,8 @@ impl HTMLMetaElement { if name == "referrer" { self.apply_referrer(); } + } else if &*self.HttpEquiv() != "" { + self.declarative_refresh(); } } @@ -81,6 +110,96 @@ impl HTMLMetaElement { } } } + + /// + fn declarative_refresh(&self) { + // 2 + let content = self.Content(); + // 1 + if !content.is_empty() { + // 3 + self.shared_declarative_refresh_steps(content); + } + } + + /// + fn shared_declarative_refresh_steps(&self, content: DOMString) { + // 1 + let document = document_from_node(self); + if document.will_declaratively_refresh() { + return; + } + + // 2-11 + lazy_static::lazy_static! { + static ref REFRESH_REGEX: Regex = Regex::new( + r#"(?x) + ^ + \s* # 3 + ((? { - fn push(&mut self, value: A::Item) { - SmallVec::push(self, value); - } -} diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs deleted file mode 100644 index 560b1e61d81..00000000000 --- a/components/selectors/tree.rs +++ /dev/null @@ -1,163 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency -//! between layout and style. - -use crate::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; -use crate::matching::{ElementSelectorFlags, MatchingContext}; -use crate::parser::SelectorImpl; -use std::fmt::Debug; -use std::ptr::NonNull; - -/// Opaque representation of an Element, for identity comparisons. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub struct OpaqueElement(NonNull<()>); - -unsafe impl Send for OpaqueElement {} - -impl OpaqueElement { - /// Creates a new OpaqueElement from an arbitrarily-typed pointer. - pub fn new(ptr: &T) -> Self { - unsafe { - OpaqueElement(NonNull::new_unchecked( - ptr as *const T as *const () as *mut (), - )) - } - } -} - -pub trait Element: Sized + Clone + Debug { - type Impl: SelectorImpl; - - /// Converts self into an opaque representation. - fn opaque(&self) -> OpaqueElement; - - fn parent_element(&self) -> Option; - - /// Whether the parent node of this element is a shadow root. - fn parent_node_is_shadow_root(&self) -> bool; - - /// The host of the containing shadow root, if any. - fn containing_shadow_host(&self) -> Option; - - /// The parent of a given pseudo-element, after matching a pseudo-element - /// selector. - /// - /// This is guaranteed to be called in a pseudo-element. - fn pseudo_element_originating_element(&self) -> Option { - debug_assert!(self.is_pseudo_element()); - self.parent_element() - } - - /// Whether we're matching on a pseudo-element. - fn is_pseudo_element(&self) -> bool; - - /// Skips non-element nodes - fn prev_sibling_element(&self) -> Option; - - /// Skips non-element nodes - fn next_sibling_element(&self) -> Option; - - /// Skips non-element nodes - fn first_element_child(&self) -> Option; - - fn is_html_element_in_html_document(&self) -> bool; - - fn has_local_name(&self, local_name: &::BorrowedLocalName) -> bool; - - /// Empty string for no namespace - fn has_namespace(&self, ns: &::BorrowedNamespaceUrl) -> bool; - - /// Whether this element and the `other` element have the same local name and namespace. - fn is_same_type(&self, other: &Self) -> bool; - - fn attr_matches( - &self, - ns: &NamespaceConstraint<&::NamespaceUrl>, - local_name: &::LocalName, - operation: &AttrSelectorOperation<&::AttrValue>, - ) -> bool; - - fn has_attr_in_no_namespace( - &self, - local_name: &::LocalName, - ) -> bool { - self.attr_matches( - &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::()), - local_name, - &AttrSelectorOperation::Exists, - ) - } - - fn match_non_ts_pseudo_class( - &self, - pc: &::NonTSPseudoClass, - context: &mut MatchingContext, - ) -> bool; - - fn match_pseudo_element( - &self, - pe: &::PseudoElement, - context: &mut MatchingContext, - ) -> bool; - - /// Sets selector flags on the elemnt itself or the parent, depending on the - /// flags, which indicate what kind of work may need to be performed when - /// DOM state changes. - fn apply_selector_flags(&self, flags: ElementSelectorFlags); - - /// Whether this element is a `link`. - fn is_link(&self) -> bool; - - /// Returns whether the element is an HTML element. - fn is_html_slot_element(&self) -> bool; - - /// Returns the assigned element this element is assigned to. - /// - /// Necessary for the `::slotted` pseudo-class. - fn assigned_slot(&self) -> Option { - None - } - - fn has_id( - &self, - id: &::Identifier, - case_sensitivity: CaseSensitivity, - ) -> bool; - - fn has_class( - &self, - name: &::Identifier, - case_sensitivity: CaseSensitivity, - ) -> bool; - - /// Returns the mapping from the `exportparts` attribute in the reverse - /// direction, that is, in an outer-tree -> inner-tree direction. - fn imported_part( - &self, - name: &::Identifier, - ) -> Option<::Identifier>; - - fn is_part(&self, name: &::Identifier) -> bool; - - /// Returns whether this element matches `:empty`. - /// - /// That is, whether it does not contain any child element or any non-zero-length text node. - /// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo - fn is_empty(&self) -> bool; - - /// Returns whether this element matches `:root`, - /// i.e. whether it is the root element of a document. - /// - /// Note: this can be false even if `.parent_element()` is `None` - /// if the parent node is a `DocumentFragment`. - fn is_root(&self) -> bool; - - /// Returns whether this element should ignore matching nth child - /// selector. - fn ignores_nth_child_selectors(&self) -> bool { - false - } -} diff --git a/components/selectors/visitor.rs b/components/selectors/visitor.rs deleted file mode 100644 index 785c12813a6..00000000000 --- a/components/selectors/visitor.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Visitor traits for selectors. - -#![deny(missing_docs)] - -use crate::attr::NamespaceConstraint; -use crate::parser::{Combinator, Component, Selector, SelectorImpl}; - -/// A trait to visit selector properties. -/// -/// All the `visit_foo` methods return a boolean indicating whether the -/// traversal should continue or not. -pub trait SelectorVisitor: Sized { - /// The selector implementation this visitor wants to visit. - type Impl: SelectorImpl; - - /// Visit an attribute selector that may match (there are other selectors - /// that may never match, like those containing whitespace or the empty - /// string). - fn visit_attribute_selector( - &mut self, - _namespace: &NamespaceConstraint<&::NamespaceUrl>, - _local_name: &::LocalName, - _local_name_lower: &::LocalName, - ) -> bool { - true - } - - /// Visit a simple selector. - fn visit_simple_selector(&mut self, _: &Component) -> bool { - true - } - - /// Visit a nested selector list. The caller is responsible to call visit - /// into the internal selectors if / as needed. - /// - /// The default implementation does this. - fn visit_selector_list( - &mut self, - _list_kind: SelectorListKind, - list: &[Selector], - ) -> bool { - for nested in list { - if !nested.visit(self) { - return false; - } - } - true - } - - /// Visits a complex selector. - /// - /// Gets the combinator to the right of the selector, or `None` if the - /// selector is the rightmost one. - fn visit_complex_selector(&mut self, _combinator_to_right: Option) -> bool { - true - } -} - -bitflags! { - /// The kinds of components the visitor is visiting the selector list of, if any - #[derive(Default)] - pub struct SelectorListKind: u8 { - /// The visitor is inside :not(..) - const NEGATION = 1 << 0; - /// The visitor is inside :is(..) - const IS = 1 << 1; - /// The visitor is inside :where(..) - const WHERE = 1 << 2; - /// The visitor is inside :nth-child(.. of ) or - /// :nth-last-child(.. of ) - const NTH_OF = 1 << 3; - } -} - -impl SelectorListKind { - /// Construct a SelectorListKind for the corresponding component. - pub fn from_component(component: &Component) -> Self { - match component { - Component::Negation(_) => SelectorListKind::NEGATION, - Component::Is(_) => SelectorListKind::IS, - Component::Where(_) => SelectorListKind::WHERE, - Component::NthOf(_) => SelectorListKind::NTH_OF, - _ => SelectorListKind::empty(), - } - } - - /// Whether the visitor is inside :not(..) - pub fn in_negation(&self) -> bool { - self.intersects(SelectorListKind::NEGATION) - } - - /// Whether the visitor is inside :is(..) - pub fn in_is(&self) -> bool { - self.intersects(SelectorListKind::IS) - } - - /// Whether the visitor is inside :where(..) - pub fn in_where(&self) -> bool { - self.intersects(SelectorListKind::WHERE) - } - - /// Whether the visitor is inside :nth-child(.. of ) or - /// :nth-last-child(.. of ) - pub fn in_nth_of(&self) -> bool { - self.intersects(SelectorListKind::NTH_OF) - } -} diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 7d542b19071..e0efe1055f4 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -66,14 +66,14 @@ profile_traits = { workspace = true } script = { path = "../script" } script_layout_interface = { workspace = true } script_traits = { workspace = true } -servo-media = { git = "https://github.com/servo/media" } -servo-media-dummy = { git = "https://github.com/servo/media" } -servo-media-gstreamer = { git = "https://github.com/servo/media", optional = true } +servo-media = { workspace = true } +servo-media-dummy = { workspace = true } +servo-media-gstreamer = { workspace = true, optional = true } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } sparkle = { workspace = true } -style = { path = "../style", features = ["servo"] } +style = { workspace = true } style_traits = { workspace = true } surfman = { workspace = true } webdriver_server = { path = "../webdriver_server", optional = true } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 89f90423281..bd310751316 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -33,8 +33,8 @@ use canvas_traits::webgl::WebGLThreads; use compositing::windowing::{EmbedderEvent, EmbedderMethods, WindowMethods}; use compositing::{CompositeTarget, IOCompositor, InitialCompositorState, ShutdownState}; use compositing_traits::{ - CanvasToCompositorMsg, CompositingReason, CompositorMsg, CompositorProxy, CompositorReceiver, - ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, + CanvasToCompositorMsg, CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, + FontToCompositorMsg, ForwardedToCompositorMsg, }; #[cfg(all( not(target_os = "windows"), @@ -198,26 +198,17 @@ impl webrender_api::RenderNotifier for RenderNotifier { Box::new(RenderNotifier::new(self.compositor_proxy.clone())) } - fn wake_up(&self, composite_needed: bool) { - if composite_needed { - self.compositor_proxy - .recomposite(CompositingReason::NewWebRenderFrame); - } - } + fn wake_up(&self, _composite_needed: bool) {} fn new_frame_ready( &self, _document_id: DocumentId, - scrolled: bool, + _scrolled: bool, composite_needed: bool, _render_time_ns: Option, ) { - if scrolled { - self.compositor_proxy - .send(CompositorMsg::NewScrollFrameReady(composite_needed)); - } else { - self.wake_up(true); - } + self.compositor_proxy + .send(CompositorMsg::NewWebRenderFrameReady(composite_needed)); } } diff --git a/components/servo_arc/Cargo.toml b/components/servo_arc/Cargo.toml deleted file mode 100644 index c975ad55412..00000000000 --- a/components/servo_arc/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "servo_arc" -version = "0.2.0" -authors = ["The Servo Project Developers"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/servo/servo" -description = "A fork of std::sync::Arc with some extra functionality and without weak references" - -[lib] -name = "servo_arc" -path = "lib.rs" - -[features] -gecko_refcount_logging = [] -servo = ["serde"] - -[dependencies] -nodrop = { version = "0.1.8" } -serde = { workspace = true, optional = true } -stable_deref_trait = "1.0.0" diff --git a/components/servo_arc/LICENSE-APACHE b/components/servo_arc/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e8..00000000000 --- a/components/servo_arc/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/components/servo_arc/LICENSE-MIT b/components/servo_arc/LICENSE-MIT deleted file mode 100644 index 31aa79387f2..00000000000 --- a/components/servo_arc/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs deleted file mode 100644 index cc71827283a..00000000000 --- a/components/servo_arc/lib.rs +++ /dev/null @@ -1,1370 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Fork of Arc for Servo. This has the following advantages over std::sync::Arc: -//! -//! * We don't waste storage on the weak reference count. -//! * We don't do extra RMU operations to handle the possibility of weak references. -//! * We can experiment with arena allocation (todo). -//! * We can add methods to support our custom use cases [1]. -//! * We have support for dynamically-sized types (see from_header_and_iter). -//! * We have support for thin arcs to unsized types (see ThinArc). -//! * We have support for references to static data, which don't do any -//! refcounting. -//! -//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883 - -// The semantics of `Arc` are already documented in the Rust docs, so we don't -// duplicate those here. -#![allow(missing_docs)] - -#[cfg(feature = "servo")] -extern crate serde; -extern crate stable_deref_trait; - -#[cfg(feature = "servo")] -use serde::{Deserialize, Serialize}; -use stable_deref_trait::{CloneStableDeref, StableDeref}; -use std::alloc::{self, Layout}; -use std::borrow; -use std::cmp::Ordering; -use std::convert::From; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::iter::{ExactSizeIterator, Iterator}; -use std::marker::PhantomData; -use std::mem::{self, align_of, size_of}; -use std::ops::{Deref, DerefMut}; -use std::os::raw::c_void; -use std::process; -use std::ptr; -use std::slice; -use std::sync::atomic; -use std::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use std::{isize, usize}; - -/// A soft limit on the amount of references that may be made to an `Arc`. -/// -/// Going above this limit will abort your program (although not -/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -/// Special refcount value that means the data is not reference counted, -/// and that the `Arc` is really acting as a read-only static reference. -const STATIC_REFCOUNT: usize = usize::MAX; - -/// An atomically reference counted shared pointer -/// -/// See the documentation for [`Arc`] in the standard library. Unlike the -/// standard library `Arc`, this `Arc` does not support weak reference counting. -/// -/// See the discussion in https://github.com/rust-lang/rust/pull/60594 for the -/// usage of PhantomData. -/// -/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html -/// -/// cbindgen:derive-eq=false -/// cbindgen:derive-neq=false -#[repr(C)] -pub struct Arc { - p: ptr::NonNull>, - phantom: PhantomData, -} - -/// An `Arc` that is known to be uniquely owned -/// -/// When `Arc`s are constructed, they are known to be -/// uniquely owned. In such a case it is safe to mutate -/// the contents of the `Arc`. Normally, one would just handle -/// this by mutating the data on the stack before allocating the -/// `Arc`, however it's possible the data is large or unsized -/// and you need to heap-allocate it earlier in such a way -/// that it can be freely converted into a regular `Arc` once you're -/// done. -/// -/// `UniqueArc` exists for this purpose, when constructed it performs -/// the same allocations necessary for an `Arc`, however it allows mutable access. -/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc` -/// out of it. -/// -/// Ignore the doctest below there's no way to skip building with refcount -/// logging during doc tests (see rust-lang/rust#45599). -/// -/// ```rust,ignore -/// # use servo_arc::UniqueArc; -/// let data = [1, 2, 3, 4, 5]; -/// let mut x = UniqueArc::new(data); -/// x[4] = 7; // mutate! -/// let y = x.shareable(); // y is an Arc -/// ``` -pub struct UniqueArc(Arc); - -impl UniqueArc { - #[inline] - /// Construct a new UniqueArc - pub fn new(data: T) -> Self { - UniqueArc(Arc::new(data)) - } - - /// Construct an uninitialized arc - #[inline] - pub fn new_uninit() -> UniqueArc> { - unsafe { - let layout = Layout::new::>>(); - let ptr = alloc::alloc(layout); - let mut p = ptr::NonNull::new(ptr) - .unwrap_or_else(|| alloc::handle_alloc_error(layout)) - .cast::>>(); - ptr::write(&mut p.as_mut().count, atomic::AtomicUsize::new(1)); - - #[cfg(feature = "gecko_refcount_logging")] - { - NS_LogCtor(p.as_ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8) - } - - UniqueArc(Arc { - p, - phantom: PhantomData, - }) - } - } - - #[inline] - /// Convert to a shareable Arc once we're done mutating it - pub fn shareable(self) -> Arc { - self.0 - } -} - -impl UniqueArc> { - /// Convert to an initialized Arc. - #[inline] - pub unsafe fn assume_init(this: Self) -> UniqueArc { - UniqueArc(Arc { - p: mem::ManuallyDrop::new(this).0.p.cast(), - phantom: PhantomData, - }) - } -} - -impl Deref for UniqueArc { - type Target = T; - fn deref(&self) -> &T { - &*self.0 - } -} - -impl DerefMut for UniqueArc { - fn deref_mut(&mut self) -> &mut T { - // We know this to be uniquely owned - unsafe { &mut (*self.0.ptr()).data } - } -} - -unsafe impl Send for Arc {} -unsafe impl Sync for Arc {} - -/// The object allocated by an Arc -#[repr(C)] -struct ArcInner { - count: atomic::AtomicUsize, - data: T, -} - -unsafe impl Send for ArcInner {} -unsafe impl Sync for ArcInner {} - -/// Computes the offset of the data field within ArcInner. -fn data_offset() -> usize { - let size = size_of::>(); - let align = align_of::(); - // https://github.com/rust-lang/rust/blob/1.36.0/src/libcore/alloc.rs#L187-L207 - size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1) -} - -impl Arc { - /// Construct an `Arc` - #[inline] - pub fn new(data: T) -> Self { - let ptr = Box::into_raw(Box::new(ArcInner { - count: atomic::AtomicUsize::new(1), - data, - })); - - #[cfg(feature = "gecko_refcount_logging")] - unsafe { - // FIXME(emilio): Would be so amazing to have - // std::intrinsics::type_name() around, so that we could also report - // a real size. - NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8); - } - - unsafe { - Arc { - p: ptr::NonNull::new_unchecked(ptr), - phantom: PhantomData, - } - } - } - - /// Construct an intentionally-leaked arc. - #[inline] - pub fn new_leaked(data: T) -> Self { - let arc = Self::new(data); - arc.mark_as_intentionally_leaked(); - arc - } - - /// Convert the Arc to a raw pointer, suitable for use across FFI - /// - /// Note: This returns a pointer to the data T, which is offset in the allocation. - #[inline] - pub fn into_raw(this: Self) -> *const T { - let ptr = unsafe { &((*this.ptr()).data) as *const _ }; - mem::forget(this); - ptr - } - - /// Reconstruct the Arc from a raw pointer obtained from into_raw() - /// - /// Note: This raw pointer will be offset in the allocation and must be preceded - /// by the atomic count. - #[inline] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // To find the corresponding pointer to the `ArcInner` we need - // to subtract the offset of the `data` field from the pointer. - let ptr = (ptr as *const u8).sub(data_offset::()); - Arc { - p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner), - phantom: PhantomData, - } - } - - /// Like from_raw, but returns an addrefed arc instead. - #[inline] - pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self { - let arc = Self::from_raw(ptr); - mem::forget(arc.clone()); - arc - } - - /// Create a new static Arc (one that won't reference count the object) - /// and place it in the allocation provided by the specified `alloc` - /// function. - /// - /// `alloc` must return a pointer into a static allocation suitable for - /// storing data with the `Layout` passed into it. The pointer returned by - /// `alloc` will not be freed. - #[inline] - pub unsafe fn new_static(alloc: F, data: T) -> Arc - where - F: FnOnce(Layout) -> *mut u8, - { - let ptr = alloc(Layout::new::>()) as *mut ArcInner; - - let x = ArcInner { - count: atomic::AtomicUsize::new(STATIC_REFCOUNT), - data, - }; - - ptr::write(ptr, x); - - Arc { - p: ptr::NonNull::new_unchecked(ptr), - phantom: PhantomData, - } - } - - /// Produce a pointer to the data that can be converted back - /// to an Arc. This is basically an `&Arc`, without the extra indirection. - /// It has the benefits of an `&T` but also knows about the underlying refcount - /// and can be converted into more `Arc`s if necessary. - #[inline] - pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> { - ArcBorrow(&**self) - } - - /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory - /// reporting. - /// - /// If this is a static reference, this returns null. - pub fn heap_ptr(&self) -> *const c_void { - if self.inner().count.load(Relaxed) == STATIC_REFCOUNT { - ptr::null() - } else { - self.p.as_ptr() as *const ArcInner as *const c_void - } - } -} - -impl Arc { - #[inline] - fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed - // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` because the inner data is - // `Sync` as well, so we're ok loaning out an immutable pointer to these - // contents. - unsafe { &*self.ptr() } - } - - #[inline(always)] - fn record_drop(&self) { - #[cfg(feature = "gecko_refcount_logging")] - unsafe { - NS_LogDtor(self.ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8); - } - } - - /// Marks this `Arc` as intentionally leaked for the purposes of refcount - /// logging. - /// - /// It's a logic error to call this more than once, but it's not unsafe, as - /// it'd just report negative leaks. - #[inline(always)] - pub fn mark_as_intentionally_leaked(&self) { - self.record_drop(); - } - - // Non-inlined part of `drop`. Just invokes the destructor and calls the - // refcount logging machinery if enabled. - #[inline(never)] - unsafe fn drop_slow(&mut self) { - self.record_drop(); - let _ = Box::from_raw(self.ptr()); - } - - /// Test pointer equality between the two Arcs, i.e. they must be the _same_ - /// allocation - #[inline] - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr() == other.ptr() - } - - fn ptr(&self) -> *mut ArcInner { - self.p.as_ptr() - } -} - -#[cfg(feature = "gecko_refcount_logging")] -extern "C" { - fn NS_LogCtor( - aPtr: *mut std::os::raw::c_void, - aTypeName: *const std::os::raw::c_char, - aSize: u32, - ); - fn NS_LogDtor( - aPtr: *mut std::os::raw::c_void, - aTypeName: *const std::os::raw::c_char, - aSize: u32, - ); -} - -impl Clone for Arc { - #[inline] - fn clone(&self) -> Self { - // NOTE(emilio): If you change anything here, make sure that the - // implementation in layout/style/ServoStyleConstsInlines.h matches! - // - // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since - // `count` never changes between STATIC_REFCOUNT and other values. - if self.inner().count.load(Relaxed) != STATIC_REFCOUNT { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object. - // - // As explained in the [Boost documentation][1], Increasing the - // reference counter can always be done with memory_order_relaxed: New - // references to an object can only be formed from an existing - // reference, and passing an existing reference from one thread to - // another must already provide any required synchronization. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - let old_size = self.inner().count.fetch_add(1, Relaxed); - - // However we need to guard against massive refcounts in case someone - // is `mem::forget`ing Arcs. If we don't do this the count can overflow - // and users will use-after free. We racily saturate to `isize::MAX` on - // the assumption that there aren't ~2 billion threads incrementing - // the reference count at once. This branch will never be taken in - // any realistic program. - // - // We abort because such a program is incredibly degenerate, and we - // don't care to support it. - if old_size > MAX_REFCOUNT { - process::abort(); - } - } - - unsafe { - Arc { - p: ptr::NonNull::new_unchecked(self.ptr()), - phantom: PhantomData, - } - } - } -} - -impl Deref for Arc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().data - } -} - -impl Arc { - /// Makes a mutable reference to the `Arc`, cloning if necessary - /// - /// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library. - /// - /// If this `Arc` is uniquely owned, `make_mut()` will provide a mutable - /// reference to the contents. If not, `make_mut()` will create a _new_ `Arc` - /// with a copy of the contents, update `this` to point to it, and provide - /// a mutable reference to its contents. - /// - /// This is useful for implementing copy-on-write schemes where you wish to - /// avoid copying things if your `Arc` is not shared. - /// - /// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut - #[inline] - pub fn make_mut(this: &mut Self) -> &mut T { - if !this.is_unique() { - // Another pointer exists; clone - *this = Arc::new((**this).clone()); - } - - unsafe { - // This unsafety is ok because we're guaranteed that the pointer - // returned is the *only* pointer that will ever be returned to T. Our - // reference count is guaranteed to be 1 at this point, and we required - // the Arc itself to be `mut`, so we're returning the only possible - // reference to the inner data. - &mut (*this.ptr()).data - } - } -} - -impl Arc { - /// Provides mutable access to the contents _if_ the `Arc` is uniquely owned. - #[inline] - pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { - unsafe { - // See make_mut() for documentation of the threadsafety here. - Some(&mut (*this.ptr()).data) - } - } else { - None - } - } - - /// Whether or not the `Arc` is a static reference. - #[inline] - pub fn is_static(&self) -> bool { - // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since - // `count` never changes between STATIC_REFCOUNT and other values. - self.inner().count.load(Relaxed) == STATIC_REFCOUNT - } - - /// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not - /// a static reference. - #[inline] - pub fn is_unique(&self) -> bool { - // See the extensive discussion in [1] for why this needs to be Acquire. - // - // [1] https://github.com/servo/servo/issues/21186 - self.inner().count.load(Acquire) == 1 - } -} - -impl Drop for Arc { - #[inline] - fn drop(&mut self) { - // NOTE(emilio): If you change anything here, make sure that the - // implementation in layout/style/ServoStyleConstsInlines.h matches! - if self.is_static() { - return; - } - - // Because `fetch_sub` is already atomic, we do not need to synchronize - // with other threads unless we are going to delete the object. - if self.inner().count.fetch_sub(1, Release) != 1 { - return; - } - - // FIXME(bholley): Use the updated comment when [2] is merged. - // - // This load is needed to prevent reordering of use of the data and - // deletion of the data. Because it is marked `Release`, the decreasing - // of the reference count synchronizes with this `Acquire` load. This - // means that use of the data happens before decreasing the reference - // count, which happens before this load, which happens before the - // deletion of the data. - // - // As explained in the [Boost documentation][1], - // - // > It is important to enforce any possible access to the object in one - // > thread (through an existing reference) to *happen before* deleting - // > the object in a different thread. This is achieved by a "release" - // > operation after dropping a reference (any access to the object - // > through this reference must obviously happened before), and an - // > "acquire" operation before deleting the object. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - // [2]: https://github.com/rust-lang/rust/pull/41714 - self.inner().count.load(Acquire); - - unsafe { - self.drop_slow(); - } - } -} - -impl PartialEq for Arc { - fn eq(&self, other: &Arc) -> bool { - Self::ptr_eq(self, other) || *(*self) == *(*other) - } - - fn ne(&self, other: &Arc) -> bool { - !Self::ptr_eq(self, other) && *(*self) != *(*other) - } -} - -impl PartialOrd for Arc { - fn partial_cmp(&self, other: &Arc) -> Option { - (**self).partial_cmp(&**other) - } - - fn lt(&self, other: &Arc) -> bool { - *(*self) < *(*other) - } - - fn le(&self, other: &Arc) -> bool { - *(*self) <= *(*other) - } - - fn gt(&self, other: &Arc) -> bool { - *(*self) > *(*other) - } - - fn ge(&self, other: &Arc) -> bool { - *(*self) >= *(*other) - } -} -impl Ord for Arc { - fn cmp(&self, other: &Arc) -> Ordering { - (**self).cmp(&**other) - } -} -impl Eq for Arc {} - -impl fmt::Display for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -impl fmt::Debug for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl fmt::Pointer for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Pointer::fmt(&self.ptr(), f) - } -} - -impl Default for Arc { - fn default() -> Arc { - Arc::new(Default::default()) - } -} - -impl Hash for Arc { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -impl From for Arc { - #[inline] - fn from(t: T) -> Self { - Arc::new(t) - } -} - -impl borrow::Borrow for Arc { - #[inline] - fn borrow(&self) -> &T { - &**self - } -} - -impl AsRef for Arc { - #[inline] - fn as_ref(&self) -> &T { - &**self - } -} - -unsafe impl StableDeref for Arc {} -unsafe impl CloneStableDeref for Arc {} - -#[cfg(feature = "servo")] -impl<'de, T: Deserialize<'de>> Deserialize<'de> for Arc { - fn deserialize(deserializer: D) -> Result, D::Error> - where - D: ::serde::de::Deserializer<'de>, - { - T::deserialize(deserializer).map(Arc::new) - } -} - -#[cfg(feature = "servo")] -impl Serialize for Arc { - fn serialize(&self, serializer: S) -> Result - where - S: ::serde::ser::Serializer, - { - (**self).serialize(serializer) - } -} - -/// Structure to allow Arc-managing some fixed-sized data and a variably-sized -/// slice in a single allocation. -#[derive(Debug, Eq, PartialEq, PartialOrd)] -#[repr(C)] -pub struct HeaderSlice { - /// The fixed-sized data. - pub header: H, - - /// The dynamically-sized data. - pub slice: T, -} - -#[inline(always)] -fn divide_rounding_up(dividend: usize, divisor: usize) -> usize { - (dividend + divisor - 1) / divisor -} - -impl Arc> { - /// Creates an Arc for a HeaderSlice using the given header struct and - /// iterator to generate the slice. - /// - /// `is_static` indicates whether to create a static Arc. - /// - /// `alloc` is used to get a pointer to the memory into which the - /// dynamically sized ArcInner> value will be - /// written. If `is_static` is true, then `alloc` must return a - /// pointer into some static memory allocation. If it is false, - /// then `alloc` must return an allocation that can be dellocated - /// by calling Box::from_raw::>> on it. - #[inline] - fn from_header_and_iter_alloc( - alloc: F, - header: H, - mut items: I, - num_items: usize, - is_static: bool, - ) -> Self - where - F: FnOnce(Layout) -> *mut u8, - I: Iterator, - { - assert_ne!(size_of::(), 0, "Need to think about ZST"); - - let inner_align = align_of::>>(); - debug_assert!(inner_align >= align_of::()); - - // Compute the required size for the allocation. - let size = { - // Next, synthesize a totally garbage (but properly aligned) pointer - // to a sequence of T. - let fake_slice_ptr = inner_align as *const T; - - // Convert that sequence to a fat pointer. The address component of - // the fat pointer will be garbage, but the length will be correct. - let fake_slice = unsafe { slice::from_raw_parts(fake_slice_ptr, num_items) }; - - // Pretend the garbage address points to our allocation target (with - // a trailing sequence of T), rather than just a sequence of T. - let fake_ptr = fake_slice as *const [T] as *const ArcInner>; - let fake_ref: &ArcInner> = unsafe { &*fake_ptr }; - - // Use size_of_val, which will combine static information about the - // type with the length from the fat pointer. The garbage address - // will not be used. - mem::size_of_val(fake_ref) - }; - - let ptr: *mut ArcInner>; - unsafe { - // Allocate the buffer. - let layout = if inner_align <= align_of::() { - Layout::from_size_align_unchecked(size, align_of::()) - } else if inner_align <= align_of::() { - // On 32-bit platforms may have 8 byte alignment while usize - // has 4 byte aligment. Use u64 to avoid over-alignment. - // This branch will compile away in optimized builds. - Layout::from_size_align_unchecked(size, align_of::()) - } else { - panic!("Over-aligned type not handled"); - }; - - let buffer = alloc(layout); - - // Synthesize the fat pointer. We do this by claiming we have a direct - // pointer to a [T], and then changing the type of the borrow. The key - // point here is that the length portion of the fat pointer applies - // only to the number of elements in the dynamically-sized portion of - // the type, so the value will be the same whether it points to a [T] - // or something else with a [T] as its last member. - let fake_slice: &mut [T] = slice::from_raw_parts_mut(buffer as *mut T, num_items); - ptr = fake_slice as *mut [T] as *mut ArcInner>; - - // Write the data. - // - // Note that any panics here (i.e. from the iterator) are safe, since - // we'll just leak the uninitialized memory. - let count = if is_static { - atomic::AtomicUsize::new(STATIC_REFCOUNT) - } else { - atomic::AtomicUsize::new(1) - }; - ptr::write(&mut ((*ptr).count), count); - ptr::write(&mut ((*ptr).data.header), header); - if num_items != 0 { - let mut current: *mut T = &mut (*ptr).data.slice[0]; - for _ in 0..num_items { - ptr::write( - current, - items - .next() - .expect("ExactSizeIterator over-reported length"), - ); - current = current.offset(1); - } - // We should have consumed the buffer exactly, maybe accounting - // for some padding from the alignment. - debug_assert!( - (buffer.add(size) as usize - current as *mut u8 as usize) < inner_align - ); - } - assert!( - items.next().is_none(), - "ExactSizeIterator under-reported length" - ); - } - #[cfg(feature = "gecko_refcount_logging")] - unsafe { - if !is_static { - // FIXME(emilio): Would be so amazing to have - // std::intrinsics::type_name() around. - NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8) - } - } - - // Return the fat Arc. - assert_eq!( - size_of::(), - size_of::() * 2, - "The Arc will be fat" - ); - unsafe { - Arc { - p: ptr::NonNull::new_unchecked(ptr), - phantom: PhantomData, - } - } - } - - /// Creates an Arc for a HeaderSlice using the given header struct and iterator to generate the - /// slice. Panics if num_items doesn't match the number of items. - #[inline] - pub fn from_header_and_iter_with_size(header: H, items: I, num_items: usize) -> Self - where - I: Iterator, - { - Arc::from_header_and_iter_alloc( - |layout| { - // align will only ever be align_of::() or align_of::() - let align = layout.align(); - unsafe { - if align == mem::align_of::() { - Self::allocate_buffer::(layout.size()) - } else { - assert_eq!(align, mem::align_of::()); - Self::allocate_buffer::(layout.size()) - } - } - }, - header, - items, - num_items, - /* is_static = */ false, - ) - } - - /// Creates an Arc for a HeaderSlice using the given header struct and - /// iterator to generate the slice. The resulting Arc will be fat. - #[inline] - pub fn from_header_and_iter(header: H, items: I) -> Self - where - I: Iterator + ExactSizeIterator, - { - let len = items.len(); - Self::from_header_and_iter_with_size(header, items, len) - } - - #[inline] - unsafe fn allocate_buffer(size: usize) -> *mut u8 { - // We use Vec because the underlying allocation machinery isn't - // available in stable Rust. To avoid alignment issues, we allocate - // words rather than bytes, rounding up to the nearest word size. - let words_to_allocate = divide_rounding_up(size, mem::size_of::()); - let mut vec = Vec::::with_capacity(words_to_allocate); - vec.set_len(words_to_allocate); - Box::into_raw(vec.into_boxed_slice()) as *mut W as *mut u8 - } -} - -/// Header data with an inline length. Consumers that use HeaderWithLength as the -/// Header type in HeaderSlice can take advantage of ThinArc. -#[derive(Debug, Eq, PartialEq, PartialOrd)] -#[repr(C)] -pub struct HeaderWithLength { - /// The fixed-sized data. - pub header: H, - - /// The slice length. - length: usize, -} - -impl HeaderWithLength { - /// Creates a new HeaderWithLength. - pub fn new(header: H, length: usize) -> Self { - HeaderWithLength { header, length } - } -} - -type HeaderSliceWithLength = HeaderSlice, T>; - -/// A "thin" `Arc` containing dynamically sized data -/// -/// This is functionally equivalent to Arc<(H, [T])> -/// -/// When you create an `Arc` containing a dynamically sized type -/// like `HeaderSlice`, the `Arc` is represented on the stack -/// as a "fat pointer", where the length of the slice is stored -/// alongside the `Arc`'s pointer. In some situations you may wish to -/// have a thin pointer instead, perhaps for FFI compatibility -/// or space efficiency. -/// -/// Note that we use `[T; 0]` in order to have the right alignment for `T`. -/// -/// `ThinArc` solves this by storing the length in the allocation itself, -/// via `HeaderSliceWithLength`. -#[repr(C)] -pub struct ThinArc { - ptr: ptr::NonNull>>, - phantom: PhantomData<(H, T)>, -} - -impl fmt::Debug for ThinArc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.deref(), f) - } -} - -unsafe impl Send for ThinArc {} -unsafe impl Sync for ThinArc {} - -// Synthesize a fat pointer from a thin pointer. -// -// See the comment around the analogous operation in from_header_and_iter. -fn thin_to_thick( - thin: *mut ArcInner>, -) -> *mut ArcInner> { - let len = unsafe { (*thin).data.header.length }; - let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) }; - - fake_slice as *mut ArcInner> -} - -impl ThinArc { - /// Temporarily converts |self| into a bonafide Arc and exposes it to the - /// provided callback. The refcount is not modified. - #[inline] - pub fn with_arc(&self, f: F) -> U - where - F: FnOnce(&Arc>) -> U, - { - // Synthesize transient Arc, which never touches the refcount of the ArcInner. - let transient = unsafe { - mem::ManuallyDrop::new(Arc { - p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())), - phantom: PhantomData, - }) - }; - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forward the result. - result - } - - /// Creates a `ThinArc` for a HeaderSlice using the given header struct and - /// iterator to generate the slice. - pub fn from_header_and_iter(header: H, items: I) -> Self - where - I: Iterator + ExactSizeIterator, - { - let header = HeaderWithLength::new(header, items.len()); - Arc::into_thin(Arc::from_header_and_iter(header, items)) - } - - /// Create a static `ThinArc` for a HeaderSlice using the given header - /// struct and iterator to generate the slice, placing it in the allocation - /// provided by the specified `alloc` function. - /// - /// `alloc` must return a pointer into a static allocation suitable for - /// storing data with the `Layout` passed into it. The pointer returned by - /// `alloc` will not be freed. - pub unsafe fn static_from_header_and_iter(alloc: F, header: H, items: I) -> Self - where - F: FnOnce(Layout) -> *mut u8, - I: Iterator + ExactSizeIterator, - { - let len = items.len(); - let header = HeaderWithLength::new(header, len); - Arc::into_thin(Arc::from_header_and_iter_alloc( - alloc, header, items, len, /* is_static = */ true, - )) - } - - /// Returns the address on the heap of the ThinArc itself -- not the T - /// within it -- for memory reporting, and bindings. - #[inline] - pub fn ptr(&self) -> *const c_void { - self.ptr.as_ptr() as *const ArcInner as *const c_void - } - - /// If this is a static ThinArc, this returns null. - #[inline] - pub fn heap_ptr(&self) -> *const c_void { - let is_static = - ThinArc::with_arc(self, |a| a.inner().count.load(Relaxed) == STATIC_REFCOUNT); - if is_static { - ptr::null() - } else { - self.ptr() - } - } -} - -impl Deref for ThinArc { - type Target = HeaderSliceWithLength; - - #[inline] - fn deref(&self) -> &Self::Target { - unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data } - } -} - -impl Clone for ThinArc { - #[inline] - fn clone(&self) -> Self { - ThinArc::with_arc(self, |a| Arc::into_thin(a.clone())) - } -} - -impl Drop for ThinArc { - #[inline] - fn drop(&mut self) { - let _ = Arc::from_thin(ThinArc { - ptr: self.ptr, - phantom: PhantomData, - }); - } -} - -impl Arc> { - /// Converts an `Arc` into a `ThinArc`. This consumes the `Arc`, so the refcount - /// is not modified. - #[inline] - pub fn into_thin(a: Self) -> ThinArc { - assert_eq!( - a.header.length, - a.slice.len(), - "Length needs to be correct for ThinArc to work" - ); - let fat_ptr: *mut ArcInner> = a.ptr(); - mem::forget(a); - let thin_ptr = fat_ptr as *mut [usize] as *mut usize; - ThinArc { - ptr: unsafe { - ptr::NonNull::new_unchecked( - thin_ptr as *mut ArcInner>, - ) - }, - phantom: PhantomData, - } - } - - /// Converts a `ThinArc` into an `Arc`. This consumes the `ThinArc`, so the refcount - /// is not modified. - #[inline] - pub fn from_thin(a: ThinArc) -> Self { - let ptr = thin_to_thick(a.ptr.as_ptr()); - mem::forget(a); - unsafe { - Arc { - p: ptr::NonNull::new_unchecked(ptr), - phantom: PhantomData, - } - } - } -} - -impl UniqueArc> { - #[inline] - pub fn from_header_and_iter(header: HeaderWithLength, items: I) -> Self - where - I: Iterator + ExactSizeIterator, - { - Self(Arc::from_header_and_iter(header, items)) - } - - #[inline] - pub fn from_header_and_iter_with_size( - header: HeaderWithLength, - items: I, - num_items: usize, - ) -> Self - where - I: Iterator, - { - Self(Arc::from_header_and_iter_with_size( - header, items, num_items, - )) - } - - /// Returns a mutable reference to the header. - pub fn header_mut(&mut self) -> &mut H { - // We know this to be uniquely owned - unsafe { &mut (*self.0.ptr()).data.header.header } - } - - /// Returns a mutable reference to the slice. - pub fn data_mut(&mut self) -> &mut [T] { - // We know this to be uniquely owned - unsafe { &mut (*self.0.ptr()).data.slice } - } - - pub fn shareable_thin(self) -> ThinArc { - Arc::into_thin(self.0) - } -} - -impl PartialEq for ThinArc { - #[inline] - fn eq(&self, other: &ThinArc) -> bool { - ThinArc::with_arc(self, |a| ThinArc::with_arc(other, |b| *a == *b)) - } -} - -impl Eq for ThinArc {} - -/// A "borrowed `Arc`". This is a pointer to -/// a T that is known to have been allocated within an -/// `Arc`. -/// -/// This is equivalent in guarantees to `&Arc`, however it is -/// a bit more flexible. To obtain an `&Arc` you must have -/// an `Arc` instance somewhere pinned down until we're done with it. -/// It's also a direct pointer to `T`, so using this involves less pointer-chasing -/// -/// However, C++ code may hand us refcounted things as pointers to T directly, -/// so we have to conjure up a temporary `Arc` on the stack each time. -/// -/// `ArcBorrow` lets us deal with borrows of known-refcounted objects -/// without needing to worry about where the `Arc` is. -#[derive(Debug, Eq, PartialEq)] -pub struct ArcBorrow<'a, T: 'a>(&'a T); - -impl<'a, T> Copy for ArcBorrow<'a, T> {} -impl<'a, T> Clone for ArcBorrow<'a, T> { - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T> ArcBorrow<'a, T> { - /// Clone this as an `Arc`. This bumps the refcount. - #[inline] - pub fn clone_arc(&self) -> Arc { - let arc = unsafe { Arc::from_raw(self.0) }; - // addref it! - mem::forget(arc.clone()); - arc - } - - /// For constructing from a reference known to be Arc-backed, - /// e.g. if we obtain such a reference over FFI - #[inline] - pub unsafe fn from_ref(r: &'a T) -> Self { - ArcBorrow(r) - } - - /// Compare two `ArcBorrow`s via pointer equality. Will only return - /// true if they come from the same allocation - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.0 as *const T == other.0 as *const T - } - - /// Temporarily converts |self| into a bonafide Arc and exposes it to the - /// provided callback. The refcount is not modified. - #[inline] - pub fn with_arc(&self, f: F) -> U - where - F: FnOnce(&Arc) -> U, - T: 'static, - { - // Synthesize transient Arc, which never touches the refcount. - let transient = unsafe { mem::ManuallyDrop::new(Arc::from_raw(self.0)) }; - - // Expose the transient Arc to the callback, which may clone it if it wants. - let result = f(&transient); - - // Forward the result. - result - } - - /// Similar to deref, but uses the lifetime |a| rather than the lifetime of - /// self, which is incompatible with the signature of the Deref trait. - #[inline] - pub fn get(&self) -> &'a T { - self.0 - } -} - -impl<'a, T> Deref for ArcBorrow<'a, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - self.0 - } -} - -/// A tagged union that can represent `Arc` or `Arc` while only consuming a -/// single word. The type is also `NonNull`, and thus can be stored in an Option -/// without increasing size. -/// -/// This is functionally equivalent to -/// `enum ArcUnion { First(Arc), Second(Arc)` but only takes up -/// up a single word of stack space. -/// -/// This could probably be extended to support four types if necessary. -pub struct ArcUnion { - p: ptr::NonNull<()>, - phantom_a: PhantomData, - phantom_b: PhantomData, -} - -unsafe impl Send for ArcUnion {} -unsafe impl Sync for ArcUnion {} - -impl PartialEq for ArcUnion { - fn eq(&self, other: &Self) -> bool { - use crate::ArcUnionBorrow::*; - match (self.borrow(), other.borrow()) { - (First(x), First(y)) => x == y, - (Second(x), Second(y)) => x == y, - (_, _) => false, - } - } -} - -/// This represents a borrow of an `ArcUnion`. -#[derive(Debug)] -pub enum ArcUnionBorrow<'a, A: 'a, B: 'a> { - First(ArcBorrow<'a, A>), - Second(ArcBorrow<'a, B>), -} - -impl ArcUnion { - unsafe fn new(ptr: *mut ()) -> Self { - ArcUnion { - p: ptr::NonNull::new_unchecked(ptr), - phantom_a: PhantomData, - phantom_b: PhantomData, - } - } - - /// Returns true if the two values are pointer-equal. - #[inline] - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.p == other.p - } - - #[inline] - pub fn ptr(&self) -> ptr::NonNull<()> { - self.p - } - - /// Returns an enum representing a borrow of either A or B. - #[inline] - pub fn borrow(&self) -> ArcUnionBorrow { - if self.is_first() { - let ptr = self.p.as_ptr() as *const A; - let borrow = unsafe { ArcBorrow::from_ref(&*ptr) }; - ArcUnionBorrow::First(borrow) - } else { - let ptr = ((self.p.as_ptr() as usize) & !0x1) as *const B; - let borrow = unsafe { ArcBorrow::from_ref(&*ptr) }; - ArcUnionBorrow::Second(borrow) - } - } - - /// Creates an `ArcUnion` from an instance of the first type. - pub fn from_first(other: Arc) -> Self { - unsafe { Self::new(Arc::into_raw(other) as *mut _) } - } - - /// Creates an `ArcUnion` from an instance of the second type. - pub fn from_second(other: Arc) -> Self { - unsafe { Self::new(((Arc::into_raw(other) as usize) | 0x1) as *mut _) } - } - - /// Returns true if this `ArcUnion` contains the first type. - pub fn is_first(&self) -> bool { - self.p.as_ptr() as usize & 0x1 == 0 - } - - /// Returns true if this `ArcUnion` contains the second type. - pub fn is_second(&self) -> bool { - !self.is_first() - } - - /// Returns a borrow of the first type if applicable, otherwise `None`. - pub fn as_first(&self) -> Option> { - match self.borrow() { - ArcUnionBorrow::First(x) => Some(x), - ArcUnionBorrow::Second(_) => None, - } - } - - /// Returns a borrow of the second type if applicable, otherwise None. - pub fn as_second(&self) -> Option> { - match self.borrow() { - ArcUnionBorrow::First(_) => None, - ArcUnionBorrow::Second(x) => Some(x), - } - } -} - -impl Clone for ArcUnion { - fn clone(&self) -> Self { - match self.borrow() { - ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()), - ArcUnionBorrow::Second(x) => ArcUnion::from_second(x.clone_arc()), - } - } -} - -impl Drop for ArcUnion { - fn drop(&mut self) { - match self.borrow() { - ArcUnionBorrow::First(x) => unsafe { - let _ = Arc::from_raw(&*x); - }, - ArcUnionBorrow::Second(x) => unsafe { - let _ = Arc::from_raw(&*x); - }, - } - } -} - -impl fmt::Debug for ArcUnion { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.borrow(), f) - } -} - -#[cfg(test)] -mod tests { - use super::{Arc, HeaderWithLength, ThinArc}; - use std::clone::Clone; - use std::ops::Drop; - use std::sync::atomic; - use std::sync::atomic::Ordering::{Acquire, SeqCst}; - - #[derive(PartialEq)] - struct Canary(*mut atomic::AtomicUsize); - - impl Drop for Canary { - fn drop(&mut self) { - unsafe { - (*self.0).fetch_add(1, SeqCst); - } - } - } - - #[test] - fn empty_thin() { - let header = HeaderWithLength::new(100u32, 0); - let x = Arc::from_header_and_iter(header, std::iter::empty::()); - let y = Arc::into_thin(x.clone()); - assert_eq!(y.header.header, 100); - assert!(y.slice.is_empty()); - assert_eq!(x.header.header, 100); - assert!(x.slice.is_empty()); - } - - #[test] - fn thin_assert_padding() { - #[derive(Clone, Default)] - #[repr(C)] - struct Padded { - i: u16, - } - - // The header will have more alignment than `Padded` - let header = HeaderWithLength::new(0i32, 2); - let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }]; - let a = ThinArc::from_header_and_iter(header, items.into_iter()); - assert_eq!(a.slice.len(), 2); - assert_eq!(a.slice[0].i, 0xdead); - assert_eq!(a.slice[1].i, 0xbeef); - } - - #[test] - fn slices_and_thin() { - let mut canary = atomic::AtomicUsize::new(0); - let c = Canary(&mut canary as *mut atomic::AtomicUsize); - let v = vec![5, 6]; - let header = HeaderWithLength::new(c, v.len()); - { - let x = Arc::into_thin(Arc::from_header_and_iter(header, v.into_iter())); - let y = ThinArc::with_arc(&x, |q| q.clone()); - let _ = y.clone(); - let _ = x == x; - Arc::from_thin(x.clone()); - } - assert_eq!(canary.load(Acquire), 1); - } -} diff --git a/components/servo_arc/rustfmt.toml b/components/servo_arc/rustfmt.toml deleted file mode 100644 index c7ad93bafe3..00000000000 --- a/components/servo_arc/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -disable_all_formatting = true diff --git a/components/shared/canvas/Cargo.toml b/components/shared/canvas/Cargo.toml index 6f3eca8c160..6fd52f4e3a4 100644 --- a/components/shared/canvas/Cargo.toml +++ b/components/shared/canvas/Cargo.toml @@ -20,14 +20,14 @@ cssparser = { workspace = true } euclid = { workspace = true } ipc-channel = { workspace = true } lazy_static = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } pixels = { path = "../../pixels" } serde = { workspace = true } serde_bytes = { workspace = true } servo_config = { path = "../../config" } sparkle = { workspace = true } -style = { path = "../../style" } +style = { workspace = true } time = { workspace = true, optional = true } webrender_api = { workspace = true } webxr-api = { git = "https://github.com/servo/webxr", features = ["ipc"] } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index b931c2110c1..c70fe4ce645 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -27,33 +27,6 @@ use style_traits::CSSPixel; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, DeviceRect}; use webrender_api::{self, FontInstanceKey, FontKey, ImageKey}; -/// Why we performed a composite. This is used for debugging. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CompositingReason { - /// We hit the delayed composition timeout. (See `delayed_composition.rs`.) - DelayedCompositeTimeout, - /// The window has been scrolled and we're starting the first recomposite. - Scroll, - /// A scroll has continued and we need to recomposite again. - ContinueScroll, - /// We're performing the single composite in headless mode. - Headless, - /// We're performing a composite to run an animation. - Animation, - /// A new frame tree has been loaded. - NewFrameTree, - /// New painted buffers have been received. - NewPaintedBuffers, - /// The window has been zoomed. - Zoom, - /// A new WebRender frame has arrived. - NewWebRenderFrame, - /// WebRender has processed a scroll event and has generated a new frame. - NewWebRenderScrollFrame, - /// The window has been resized and will need to be synchronously repainted. - Resize, -} - /// Sends messages to the compositor. pub struct CompositorProxy { pub sender: Sender, @@ -92,12 +65,6 @@ impl CompositorReceiver { } } -impl CompositorProxy { - pub fn recomposite(&self, reason: CompositingReason) { - self.send(CompositorMsg::Recomposite(reason)); - } -} - /// Messages from (or via) the constellation thread to the compositor. pub enum CompositorMsg { /// Informs the compositor that the constellation has completed shutdown. @@ -118,8 +85,6 @@ pub enum CompositorMsg { HideWebView(TopLevelBrowsingContextId), /// Start painting a webview on top of all others. RaiseWebViewToTop(TopLevelBrowsingContextId), - /// Composite. - Recomposite(CompositingReason), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// Composite to a PNG file and return the Image over a passed channel. @@ -128,9 +93,9 @@ pub enum CompositorMsg { IsReadyToSaveImageReply(bool), /// Pipeline visibility changed PipelineVisibilityChanged(PipelineId, bool), - /// WebRender has successfully processed a scroll. The boolean specifies whether a composite is - /// needed. - NewScrollFrameReady(bool), + /// WebRender has produced a new frame. This message informs the compositor that + /// the frame is ready, so that it may trigger a recomposite. + NewWebRenderFrameReady(bool /* composite_needed */), /// A pipeline was shut down. // This message acts as a synchronization point between the constellation, // when it shuts down a pipeline, to the compositor; when the compositor @@ -208,13 +173,12 @@ impl Debug for CompositorMsg { CompositorMsg::ShowWebView(..) => write!(f, "ShowWebView"), CompositorMsg::HideWebView(..) => write!(f, "HideWebView"), CompositorMsg::RaiseWebViewToTop(..) => write!(f, "RaiseWebViewToTop"), - CompositorMsg::Recomposite(..) => write!(f, "Recomposite"), CompositorMsg::TouchEventProcessed(..) => write!(f, "TouchEventProcessed"), CompositorMsg::CreatePng(..) => write!(f, "CreatePng"), CompositorMsg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"), CompositorMsg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"), CompositorMsg::PipelineExited(..) => write!(f, "PipelineExited"), - CompositorMsg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"), + CompositorMsg::NewWebRenderFrameReady(..) => write!(f, "NewWebRenderFrameReady"), CompositorMsg::Dispatch(..) => write!(f, "Dispatch"), CompositorMsg::PendingPaintMetric(..) => write!(f, "PendingPaintMetric"), CompositorMsg::LoadComplete(..) => write!(f, "LoadComplete"), diff --git a/components/shared/devtools/Cargo.toml b/components/shared/devtools/Cargo.toml index d158a3117ab..c3be3bcb01b 100644 --- a/components/shared/devtools/Cargo.toml +++ b/components/shared/devtools/Cargo.toml @@ -14,7 +14,7 @@ path = "lib.rs" bitflags = { workspace = true } http = { workspace = true } ipc-channel = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } msg = { workspace = true } serde = { workspace = true } diff --git a/components/shared/embedder/Cargo.toml b/components/shared/embedder/Cargo.toml index 41ae0b581bf..c1d7456a156 100644 --- a/components/shared/embedder/Cargo.toml +++ b/components/shared/embedder/Cargo.toml @@ -18,7 +18,7 @@ keyboard-types = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } msg = { workspace = true } -num-derive = "0.3" +num-derive = "0.4" num-traits = { workspace = true } serde = { workspace = true } servo_url = { path = "../../url" } diff --git a/components/shared/gfx/Cargo.toml b/components/shared/gfx/Cargo.toml index f94b5870c35..23bf75bfb07 100644 --- a/components/shared/gfx/Cargo.toml +++ b/components/shared/gfx/Cargo.toml @@ -11,7 +11,7 @@ name = "gfx_traits" path = "lib.rs" [dependencies] -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } range = { path = "../../range" } serde = { workspace = true } diff --git a/components/shared/msg/Cargo.toml b/components/shared/msg/Cargo.toml index d64880b5c6f..97abd671eef 100644 --- a/components/shared/msg/Cargo.toml +++ b/components/shared/msg/Cargo.toml @@ -15,9 +15,9 @@ doctest = false [dependencies] ipc-channel = { workspace = true } lazy_static = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } parking_lot = { workspace = true } serde = { workspace = true } -size_of_test = { path = "../../size_of_test" } +size_of_test = { workspace = true } webrender_api = { workspace = true } diff --git a/components/shared/net/Cargo.toml b/components/shared/net/Cargo.toml index dffc41eeb3a..50e9d4eb21d 100644 --- a/components/shared/net/Cargo.toml +++ b/components/shared/net/Cargo.toml @@ -24,7 +24,7 @@ image = { workspace = true } ipc-channel = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } mime = { workspace = true } msg = { workspace = true } @@ -33,7 +33,7 @@ percent-encoding = { workspace = true } pixels = { path = "../../pixels" } rustls = { workspace = true } serde = { workspace = true } -servo_arc = { path = "../../servo_arc" } +servo_arc = { workspace = true } servo_rand = { path = "../../rand" } servo_url = { path = "../../url" } url = { workspace = true } diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs index 63f4e7d8710..e69bd4a317b 100644 --- a/components/shared/net/lib.rs +++ b/components/shared/net/lib.rs @@ -862,5 +862,5 @@ impl WebrenderIpcSender { } lazy_static! { - pub static ref PRIVILEGED_SECRET: u32 = servo_rand::ServoRng::new().next_u32(); + pub static ref PRIVILEGED_SECRET: u32 = servo_rand::ServoRng::default().next_u32(); } diff --git a/components/shared/script/Cargo.toml b/components/shared/script/Cargo.toml index 64ea7be7e66..5cc2b1f8554 100644 --- a/components/shared/script/Cargo.toml +++ b/components/shared/script/Cargo.toml @@ -26,7 +26,7 @@ ipc-channel = { workspace = true } keyboard-types = { workspace = true } libc = { workspace = true } log = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } media = { path = "../../media" } msg = { workspace = true } @@ -34,7 +34,7 @@ net_traits = { workspace = true } pixels = { path = "../../pixels" } profile_traits = { workspace = true } serde = { workspace = true } -servo_atoms = { path = "../../atoms" } +servo_atoms = { workspace = true } servo_url = { path = "../../url" } smallvec = { workspace = true } style_traits = { workspace = true } diff --git a/components/shared/script_layout/Cargo.toml b/components/shared/script_layout/Cargo.toml index 40c92e96c8e..df526c9bec9 100644 --- a/components/shared/script_layout/Cargo.toml +++ b/components/shared/script_layout/Cargo.toml @@ -21,7 +21,7 @@ gfx_traits = { workspace = true } html5ever = { workspace = true } ipc-channel = { workspace = true } libc = { workspace = true } -malloc_size_of = { path = "../../malloc_size_of" } +malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } metrics = { path = "../../metrics" } msg = { workspace = true } @@ -29,10 +29,10 @@ net_traits = { workspace = true } profile_traits = { workspace = true } range = { path = "../../range" } script_traits = { workspace = true } -selectors = { path = "../../selectors" } -servo_arc = { path = "../../servo_arc" } -servo_atoms = { path = "../../atoms" } +selectors = { workspace = true } +servo_arc = { workspace = true } +servo_atoms = { workspace = true } servo_url = { path = "../../url" } -style = { path = "../../style", features = ["servo"] } +style = { workspace = true } style_traits = { workspace = true } webrender_api = { workspace = true } diff --git a/components/size_of_test/Cargo.toml b/components/size_of_test/Cargo.toml deleted file mode 100644 index faea55c5c1c..00000000000 --- a/components/size_of_test/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "size_of_test" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -edition = "2018" -publish = false - -[lib] -path = "lib.rs" - -[dependencies] -static_assertions = "1.1" diff --git a/components/size_of_test/lib.rs b/components/size_of_test/lib.rs deleted file mode 100644 index 18e45175e8c..00000000000 --- a/components/size_of_test/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -pub use static_assertions::const_assert_eq; - -/// Asserts the size of a type at compile time. -#[macro_export] -macro_rules! size_of_test { - ($t: ty, $expected_size: expr) => { - #[cfg(target_pointer_width = "64")] - $crate::const_assert_eq!(std::mem::size_of::<$t>(), $expected_size); - }; -} diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml deleted file mode 100644 index 6040b62116b..00000000000 --- a/components/style/Cargo.toml +++ /dev/null @@ -1,95 +0,0 @@ -[package] -name = "style" -version = "0.0.1" -authors = ["The Servo Project Developers"] -license = "MPL-2.0" -publish = false - -build = "build.rs" -edition = "2018" - -# https://github.com/rust-lang/cargo/issues/3544 -links = "servo_style_crate" - -[lib] -name = "style" -path = "lib.rs" -doctest = false - -[features] -gecko = ["style_traits/gecko", "bindgen", "regex", "toml", "mozbuild"] -servo = [ - "serde", - "style_traits/servo", - "servo_atoms", - "html5ever", - "cssparser/serde", - "encoding_rs", - "malloc_size_of/servo", - "string_cache", - "to_shmem/servo", - "servo_arc/servo", - "url", -] -gecko_debug = [] -gecko_refcount_logging = [] - -[dependencies] -app_units = "0.7" -arrayvec = "0.7" -atomic_refcell = "0.1" -bitflags = "1.0" -byteorder = "1.0" -cssparser = { workspace = true } -derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "from"] } -encoding_rs = { version = "0.8", optional = true } -euclid = "0.22" -fxhash = "0.2" -html5ever = { version = "0.26", optional = true } -indexmap = "1.0" -itertools = "0.10" -itoa = "1.0" -lazy_static = "1" -log = { version = "0.4", features = ["std"] } -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = "0.1" -mime = "0.3.13" -new_debug_unreachable = "1.0" -num-derive = "0.3" -num-integer = "0.1" -num-traits = "0.2" -num_cpus = { version = "1.1.0" } -owning_ref = "0.4" -parking_lot = "0.12" -precomputed-hash = "0.1.1" -rayon = "1" -selectors = { path = "../selectors" } -serde = { version = "1.0", optional = true, features = ["derive"] } -servo_arc = { path = "../servo_arc" } -servo_atoms = { path = "../atoms", optional = true } -smallbitvec = "2.3.0" -smallvec = "1.0" -static_assertions = "1.1" -static_prefs = { path = "../style_static_prefs" } -string_cache = { version = "0.8", optional = true } -style_config = { path = "../style_config" } -style_derive = { path = "../style_derive" } -style_traits = { path = "../style_traits" } -time = "0.1" -thin-vec = { workspace = true } -to_shmem = { path = "../to_shmem" } -to_shmem_derive = { path = "../to_shmem_derive" } -uluru = "3.0" -unicode-bidi = "0.3" -unicode-segmentation = "1.0" -url = { workspace = true, optional = true } -void = "1.0.2" - -[build-dependencies] -bindgen = { version = "0.69", optional = true, default-features = false } -lazy_static = "1" -log = "0.4" -mozbuild = { version = "0.1", optional = true } -regex = { version = "1.1", optional = true } -toml = { version = "0.5", optional = true, default-features = false } -walkdir = "2.1.4" diff --git a/components/style/README.md b/components/style/README.md deleted file mode 100644 index bdbe36e44c4..00000000000 --- a/components/style/README.md +++ /dev/null @@ -1,6 +0,0 @@ -servo-style -=========== - -Style system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing. - - * [Documentation](https://github.com/servo/servo/blob/main/docs/components/style.md). diff --git a/components/style/animation.rs b/components/style/animation.rs deleted file mode 100644 index aafe7067dc9..00000000000 --- a/components/style/animation.rs +++ /dev/null @@ -1,1415 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! CSS transitions and animations. - -// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to -// compile it out so that people remember it exists. - -use crate::context::{CascadeInputs, SharedStyleContext}; -use crate::dom::{OpaqueNode, TDocument, TElement, TNode}; -use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; -use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection; -use crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode; -use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState; -use crate::properties::AnimationDeclarations; -use crate::properties::{ - ComputedValues, Importance, LonghandId, LonghandIdSet, PropertyDeclarationBlock, - PropertyDeclarationId, -}; -use crate::rule_tree::CascadeLevel; -use crate::selector_parser::PseudoElement; -use crate::shared_lock::{Locked, SharedRwLock}; -use crate::style_resolver::StyleResolverForElement; -use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue}; -use crate::stylesheets::layer_rule::LayerOrder; -use crate::values::animated::{Animate, Procedure}; -use crate::values::computed::{Time, TimingFunction}; -use crate::values::generics::easing::BeforeFlag; -use crate::Atom; -use fxhash::FxHashMap; -use parking_lot::RwLock; -use servo_arc::Arc; -use std::fmt; - -/// Represents an animation for a given property. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct PropertyAnimation { - /// The value we are animating from. - from: AnimationValue, - - /// The value we are animating to. - to: AnimationValue, - - /// The timing function of this `PropertyAnimation`. - timing_function: TimingFunction, - - /// The duration of this `PropertyAnimation` in seconds. - pub duration: f64, -} - -impl PropertyAnimation { - /// Returns the given property longhand id. - pub fn property_id(&self) -> LonghandId { - debug_assert_eq!(self.from.id(), self.to.id()); - self.from.id() - } - - fn from_longhand( - longhand: LonghandId, - timing_function: TimingFunction, - duration: Time, - old_style: &ComputedValues, - new_style: &ComputedValues, - ) -> Option { - // FIXME(emilio): Handle the case where old_style and new_style's writing mode differ. - let longhand = longhand.to_physical(new_style.writing_mode); - let from = AnimationValue::from_computed_values(longhand, old_style)?; - let to = AnimationValue::from_computed_values(longhand, new_style)?; - let duration = duration.seconds() as f64; - - if from == to || duration == 0.0 { - return None; - } - - Some(PropertyAnimation { - from, - to, - timing_function, - duration, - }) - } - - /// The output of the timing function given the progress ration of this animation. - fn timing_function_output(&self, progress: f64) -> f64 { - let epsilon = 1. / (200. * self.duration); - // FIXME: Need to set the before flag correctly. - // In order to get the before flag, we have to know the current animation phase - // and whether the iteration is reversed. For now, we skip this calculation - // by treating as if the flag is unset at all times. - // https://drafts.csswg.org/css-easing/#step-timing-function-algo - self.timing_function - .calculate_output(progress, BeforeFlag::Unset, epsilon) - } - - /// Update the given animation at a given point of progress. - fn calculate_value(&self, progress: f64) -> Result { - let procedure = Procedure::Interpolate { - progress: self.timing_function_output(progress), - }; - self.from.animate(&self.to, procedure) - } -} - -/// This structure represents the state of an animation. -#[derive(Clone, Debug, MallocSizeOf, PartialEq)] -pub enum AnimationState { - /// The animation has been created, but is not running yet. This state - /// is also used when an animation is still in the first delay phase. - Pending, - /// This animation is currently running. - Running, - /// This animation is paused. The inner field is the percentage of progress - /// when it was paused, from 0 to 1. - Paused(f64), - /// This animation has finished. - Finished, - /// This animation has been canceled. - Canceled, -} - -impl AnimationState { - /// Whether or not this state requires its owning animation to be ticked. - fn needs_to_be_ticked(&self) -> bool { - *self == AnimationState::Running || *self == AnimationState::Pending - } -} - -/// This structure represents a keyframes animation current iteration state. -/// -/// If the iteration count is infinite, there's no other state, otherwise we -/// have to keep track the current iteration and the max iteration count. -#[derive(Clone, Debug, MallocSizeOf)] -pub enum KeyframesIterationState { - /// Infinite iterations with the current iteration count. - Infinite(f64), - /// Current and max iterations. - Finite(f64, f64), -} - -/// A temporary data structure used when calculating ComputedKeyframes for an -/// animation. This data structure is used to collapse information for steps -/// which may be spread across multiple keyframe declarations into a single -/// instance per `start_percentage`. -struct IntermediateComputedKeyframe { - declarations: PropertyDeclarationBlock, - timing_function: Option, - start_percentage: f32, -} - -impl IntermediateComputedKeyframe { - fn new(start_percentage: f32) -> Self { - IntermediateComputedKeyframe { - declarations: PropertyDeclarationBlock::new(), - timing_function: None, - start_percentage, - } - } - - /// Walk through all keyframe declarations and combine all declarations with the - /// same `start_percentage` into individual `IntermediateComputedKeyframe`s. - fn generate_for_keyframes( - animation: &KeyframesAnimation, - context: &SharedStyleContext, - base_style: &ComputedValues, - ) -> Vec { - let mut intermediate_steps: Vec = Vec::with_capacity(animation.steps.len()); - let mut current_step = IntermediateComputedKeyframe::new(0.); - for step in animation.steps.iter() { - let start_percentage = step.start_percentage.0; - if start_percentage != current_step.start_percentage { - let new_step = IntermediateComputedKeyframe::new(start_percentage); - intermediate_steps.push(std::mem::replace(&mut current_step, new_step)); - } - - current_step.update_from_step(step, context, base_style); - } - intermediate_steps.push(current_step); - - // We should always have a first and a last step, even if these are just - // generated by KeyframesStepValue::ComputedValues. - debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.); - debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.); - - intermediate_steps - } - - fn update_from_step( - &mut self, - step: &KeyframesStep, - context: &SharedStyleContext, - base_style: &ComputedValues, - ) { - // Each keyframe declaration may optionally specify a timing function, falling - // back to the one defined global for the animation. - let guard = &context.guards.author; - if let Some(timing_function) = step.get_animation_timing_function(&guard) { - self.timing_function = Some(timing_function.to_computed_value_without_context()); - } - - let block = match step.value { - KeyframesStepValue::ComputedValues => return, - KeyframesStepValue::Declarations { ref block } => block, - }; - - // Filter out !important, non-animatable properties, and the - // 'display' property (which is only animatable from SMIL). - let guard = block.read_with(&guard); - for declaration in guard.normal_declaration_iter() { - if let PropertyDeclarationId::Longhand(id) = declaration.id() { - if id == LonghandId::Display { - continue; - } - - if !id.is_animatable() { - continue; - } - } - - self.declarations.push( - declaration.to_physical(base_style.writing_mode), - Importance::Normal, - ); - } - } - - fn resolve_style( - self, - element: E, - context: &SharedStyleContext, - base_style: &Arc, - resolver: &mut StyleResolverForElement, - ) -> Arc - where - E: TElement, - { - if !self.declarations.any_normal() { - return base_style.clone(); - } - - let document = element.as_node().owner_doc(); - let locked_block = Arc::new(document.shared_lock().wrap(self.declarations)); - let mut important_rules_changed = false; - let rule_node = base_style.rules().clone(); - let new_node = context.stylist.rule_tree().update_rule_at_level( - CascadeLevel::Animations, - LayerOrder::root(), - Some(locked_block.borrow_arc()), - &rule_node, - &context.guards, - &mut important_rules_changed, - ); - - if new_node.is_none() { - return base_style.clone(); - } - - let inputs = CascadeInputs { - rules: new_node, - visited_rules: base_style.visited_rules().cloned(), - flags: base_style.flags.for_cascade_inputs(), - }; - resolver - .cascade_style_and_visited_with_default_parents(inputs) - .0 - } -} - -/// A single computed keyframe for a CSS Animation. -#[derive(Clone, MallocSizeOf)] -struct ComputedKeyframe { - /// The timing function to use for transitions between this step - /// and the next one. - timing_function: TimingFunction, - - /// The starting percentage (a number between 0 and 1) which represents - /// at what point in an animation iteration this step is. - start_percentage: f32, - - /// The animation values to transition to and from when processing this - /// keyframe animation step. - values: Vec, -} - -impl ComputedKeyframe { - fn generate_for_keyframes( - element: E, - animation: &KeyframesAnimation, - context: &SharedStyleContext, - base_style: &Arc, - default_timing_function: TimingFunction, - resolver: &mut StyleResolverForElement, - ) -> Vec - where - E: TElement, - { - let mut animating_properties = LonghandIdSet::new(); - for property in animation.properties_changed.iter() { - debug_assert!(property.is_animatable()); - animating_properties.insert(property.to_physical(base_style.writing_mode)); - } - - let animation_values_from_style: Vec = animating_properties - .iter() - .map(|property| { - AnimationValue::from_computed_values(property, &**base_style) - .expect("Unexpected non-animatable property.") - }) - .collect(); - - let intermediate_steps = - IntermediateComputedKeyframe::generate_for_keyframes(animation, context, base_style); - - let mut computed_steps: Vec = Vec::with_capacity(intermediate_steps.len()); - for (step_index, step) in intermediate_steps.into_iter().enumerate() { - let start_percentage = step.start_percentage; - let properties_changed_in_step = step.declarations.longhands().clone(); - let step_timing_function = step.timing_function.clone(); - let step_style = step.resolve_style(element, context, base_style, resolver); - let timing_function = - step_timing_function.unwrap_or_else(|| default_timing_function.clone()); - - let values = { - // If a value is not set in a property declaration we use the value from - // the style for the first and last keyframe. For intermediate ones, we - // use the value from the previous keyframe. - // - // TODO(mrobinson): According to the spec, we should use an interpolated - // value for properties missing from keyframe declarations. - let default_values = if start_percentage == 0. || start_percentage == 1.0 { - &animation_values_from_style - } else { - debug_assert!(step_index != 0); - &computed_steps[step_index - 1].values - }; - - // For each property that is animating, pull the value from the resolved - // style for this step if it's in one of the declarations. Otherwise, we - // use the default value from the set we calculated above. - animating_properties - .iter() - .zip(default_values.iter()) - .map(|(longhand, default_value)| { - if properties_changed_in_step.contains(longhand) { - AnimationValue::from_computed_values(longhand, &step_style) - .unwrap_or_else(|| default_value.clone()) - } else { - default_value.clone() - } - }) - .collect() - }; - - computed_steps.push(ComputedKeyframe { - timing_function, - start_percentage, - values, - }); - } - computed_steps - } -} - -/// A CSS Animation -#[derive(Clone, MallocSizeOf)] -pub struct Animation { - /// The name of this animation as defined by the style. - pub name: Atom, - - /// The properties that change in this animation. - properties_changed: LonghandIdSet, - - /// The computed style for each keyframe of this animation. - computed_steps: Vec, - - /// The time this animation started at, which is the current value of the animation - /// timeline when this animation was created plus any animation delay. - pub started_at: f64, - - /// The duration of this animation. - pub duration: f64, - - /// The delay of the animation. - pub delay: f64, - - /// The `animation-fill-mode` property of this animation. - pub fill_mode: AnimationFillMode, - - /// The current iteration state for the animation. - pub iteration_state: KeyframesIterationState, - - /// Whether this animation is paused. - pub state: AnimationState, - - /// The declared animation direction of this animation. - pub direction: AnimationDirection, - - /// The current animation direction. This can only be `normal` or `reverse`. - pub current_direction: AnimationDirection, - - /// The original cascade style, needed to compute the generated keyframes of - /// the animation. - #[ignore_malloc_size_of = "ComputedValues"] - pub cascade_style: Arc, - - /// Whether or not this animation is new and or has already been tracked - /// by the script thread. - pub is_new: bool, -} - -impl Animation { - /// Whether or not this animation is cancelled by changes from a new style. - fn is_cancelled_in_new_style(&self, new_style: &Arc) -> bool { - let new_ui = new_style.get_ui(); - let index = new_ui - .animation_name_iter() - .position(|animation_name| Some(&self.name) == animation_name.as_atom()); - let index = match index { - Some(index) => index, - None => return true, - }; - - new_ui.animation_duration_mod(index).seconds() == 0. - } - - /// Given the current time, advances this animation to the next iteration, - /// updates times, and then toggles the direction if appropriate. Otherwise - /// does nothing. Returns true if this animation has iterated. - pub fn iterate_if_necessary(&mut self, time: f64) -> bool { - if !self.iteration_over(time) { - return false; - } - - // Only iterate animations that are currently running. - if self.state != AnimationState::Running { - return false; - } - - if self.on_last_iteration() { - return false; - } - - self.iterate(); - true - } - - fn iterate(&mut self) { - debug_assert!(!self.on_last_iteration()); - - if let KeyframesIterationState::Finite(ref mut current, max) = self.iteration_state { - *current = (*current + 1.).min(max); - } - - if let AnimationState::Paused(ref mut progress) = self.state { - debug_assert!(*progress > 1.); - *progress -= 1.; - } - - // Update the next iteration direction if applicable. - self.started_at += self.duration; - match self.direction { - AnimationDirection::Alternate | AnimationDirection::AlternateReverse => { - self.current_direction = match self.current_direction { - AnimationDirection::Normal => AnimationDirection::Reverse, - AnimationDirection::Reverse => AnimationDirection::Normal, - _ => unreachable!(), - }; - }, - _ => {}, - } - } - - /// A number (> 0 and <= 1) which represents the fraction of a full iteration - /// that the current iteration of the animation lasts. This will be less than 1 - /// if the current iteration is the fractional remainder of a non-integral - /// iteration count. - pub fn current_iteration_end_progress(&self) -> f64 { - match self.iteration_state { - KeyframesIterationState::Finite(current, max) => (max - current).min(1.), - KeyframesIterationState::Infinite(_) => 1., - } - } - - /// The duration of the current iteration of this animation which may be less - /// than the animation duration if it has a non-integral iteration count. - pub fn current_iteration_duration(&self) -> f64 { - self.current_iteration_end_progress() * self.duration - } - - /// Whether or not the current iteration is over. Note that this method assumes that - /// the animation is still running. - fn iteration_over(&self, time: f64) -> bool { - time > (self.started_at + self.current_iteration_duration()) - } - - /// Assuming this animation is running, whether or not it is on the last iteration. - fn on_last_iteration(&self) -> bool { - match self.iteration_state { - KeyframesIterationState::Finite(current, max) => current >= (max - 1.), - KeyframesIterationState::Infinite(_) => false, - } - } - - /// Whether or not this animation has finished at the provided time. This does - /// not take into account canceling i.e. when an animation or transition is - /// canceled due to changes in the style. - pub fn has_ended(&self, time: f64) -> bool { - if !self.on_last_iteration() { - return false; - } - - let progress = match self.state { - AnimationState::Finished => return true, - AnimationState::Paused(progress) => progress, - AnimationState::Running => (time - self.started_at) / self.duration, - AnimationState::Pending | AnimationState::Canceled => return false, - }; - - progress >= self.current_iteration_end_progress() - } - - /// Updates the appropiate state from other animation. - /// - /// This happens when an animation is re-submitted to layout, presumably - /// because of an state change. - /// - /// There are some bits of state we can't just replace, over all taking in - /// account times, so here's that logic. - pub fn update_from_other(&mut self, other: &Self, now: f64) { - use self::AnimationState::*; - - debug!( - "KeyframesAnimationState::update_from_other({:?}, {:?})", - self, other - ); - - // NB: We shall not touch the started_at field, since we don't want to - // restart the animation. - let old_started_at = self.started_at; - let old_duration = self.duration; - let old_direction = self.current_direction; - let old_state = self.state.clone(); - let old_iteration_state = self.iteration_state.clone(); - - *self = other.clone(); - - self.started_at = old_started_at; - self.current_direction = old_direction; - - // Don't update the iteration count, just the iteration limit. - // TODO: see how changing the limit affects rendering in other browsers. - // We might need to keep the iteration count even when it's infinite. - match (&mut self.iteration_state, old_iteration_state) { - ( - &mut KeyframesIterationState::Finite(ref mut iters, _), - KeyframesIterationState::Finite(old_iters, _), - ) => *iters = old_iters, - _ => {}, - } - - // Don't pause or restart animations that should remain finished. - // We call mem::replace because `has_ended(...)` looks at `Animation::state`. - let new_state = std::mem::replace(&mut self.state, Running); - if old_state == Finished && self.has_ended(now) { - self.state = Finished; - } else { - self.state = new_state; - } - - // If we're unpausing the animation, fake the start time so we seem to - // restore it. - // - // If the animation keeps paused, keep the old value. - // - // If we're pausing the animation, compute the progress value. - match (&mut self.state, &old_state) { - (&mut Pending, &Paused(progress)) => { - self.started_at = now - (self.duration * progress); - }, - (&mut Paused(ref mut new), &Paused(old)) => *new = old, - (&mut Paused(ref mut progress), &Running) => { - *progress = (now - old_started_at) / old_duration - }, - _ => {}, - } - - // Try to detect when we should skip straight to the running phase to - // avoid sending multiple animationstart events. - if self.state == Pending && self.started_at <= now && old_state != Pending { - self.state = Running; - } - } - - /// Fill in an `AnimationValueMap` with values calculated from this animation at - /// the given time value. - fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) { - debug_assert!(!self.computed_steps.is_empty()); - - let total_progress = match self.state { - AnimationState::Running | AnimationState::Pending | AnimationState::Finished => { - (now - self.started_at) / self.duration - }, - AnimationState::Paused(progress) => progress, - AnimationState::Canceled => return, - }; - - if total_progress < 0. && - self.fill_mode != AnimationFillMode::Backwards && - self.fill_mode != AnimationFillMode::Both - { - return; - } - if self.has_ended(now) && - self.fill_mode != AnimationFillMode::Forwards && - self.fill_mode != AnimationFillMode::Both - { - return; - } - let total_progress = total_progress - .min(self.current_iteration_end_progress()) - .max(0.0); - - // Get the indices of the previous (from) keyframe and the next (to) keyframe. - let next_keyframe_index; - let prev_keyframe_index; - let num_steps = self.computed_steps.len(); - match self.current_direction { - AnimationDirection::Normal => { - next_keyframe_index = self - .computed_steps - .iter() - .position(|step| total_progress as f32 <= step.start_percentage); - prev_keyframe_index = next_keyframe_index - .and_then(|pos| if pos != 0 { Some(pos - 1) } else { None }) - .unwrap_or(0); - }, - AnimationDirection::Reverse => { - next_keyframe_index = self - .computed_steps - .iter() - .rev() - .position(|step| total_progress as f32 <= 1. - step.start_percentage) - .map(|pos| num_steps - pos - 1); - prev_keyframe_index = next_keyframe_index - .and_then(|pos| { - if pos != num_steps - 1 { - Some(pos + 1) - } else { - None - } - }) - .unwrap_or(num_steps - 1) - }, - _ => unreachable!(), - } - - debug!( - "Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}", - prev_keyframe_index, next_keyframe_index - ); - - let prev_keyframe = &self.computed_steps[prev_keyframe_index]; - let next_keyframe = match next_keyframe_index { - Some(index) => &self.computed_steps[index], - None => return, - }; - - // If we only need to take into account one keyframe, then exit early - // in order to avoid doing more work. - let mut add_declarations_to_map = |keyframe: &ComputedKeyframe| { - for value in keyframe.values.iter() { - map.insert(value.id(), value.clone()); - } - }; - if total_progress <= 0.0 { - add_declarations_to_map(&prev_keyframe); - return; - } - if total_progress >= 1.0 { - add_declarations_to_map(&next_keyframe); - return; - } - - let percentage_between_keyframes = - (next_keyframe.start_percentage - prev_keyframe.start_percentage).abs() as f64; - let duration_between_keyframes = percentage_between_keyframes * self.duration; - let direction_aware_prev_keyframe_start_percentage = match self.current_direction { - AnimationDirection::Normal => prev_keyframe.start_percentage as f64, - AnimationDirection::Reverse => 1. - prev_keyframe.start_percentage as f64, - _ => unreachable!(), - }; - let progress_between_keyframes = (total_progress - - direction_aware_prev_keyframe_start_percentage) / - percentage_between_keyframes; - - for (from, to) in prev_keyframe.values.iter().zip(next_keyframe.values.iter()) { - let animation = PropertyAnimation { - from: from.clone(), - to: to.clone(), - timing_function: prev_keyframe.timing_function.clone(), - duration: duration_between_keyframes as f64, - }; - - if let Ok(value) = animation.calculate_value(progress_between_keyframes) { - map.insert(value.id(), value); - } - } - } -} - -impl fmt::Debug for Animation { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Animation") - .field("name", &self.name) - .field("started_at", &self.started_at) - .field("duration", &self.duration) - .field("delay", &self.delay) - .field("iteration_state", &self.iteration_state) - .field("state", &self.state) - .field("direction", &self.direction) - .field("current_direction", &self.current_direction) - .field("cascade_style", &()) - .finish() - } -} - -/// A CSS Transition -#[derive(Clone, Debug, MallocSizeOf)] -pub struct Transition { - /// The start time of this transition, which is the current value of the animation - /// timeline when this transition was created plus any animation delay. - pub start_time: f64, - - /// The delay used for this transition. - pub delay: f64, - - /// The internal style `PropertyAnimation` for this transition. - pub property_animation: PropertyAnimation, - - /// The state of this transition. - pub state: AnimationState, - - /// Whether or not this transition is new and or has already been tracked - /// by the script thread. - pub is_new: bool, - - /// If this `Transition` has been replaced by a new one this field is - /// used to help produce better reversed transitions. - pub reversing_adjusted_start_value: AnimationValue, - - /// If this `Transition` has been replaced by a new one this field is - /// used to help produce better reversed transitions. - pub reversing_shortening_factor: f64, -} - -impl Transition { - fn update_for_possibly_reversed_transition( - &mut self, - replaced_transition: &Transition, - delay: f64, - now: f64, - ) { - // If we reach here, we need to calculate a reversed transition according to - // https://drafts.csswg.org/css-transitions/#starting - // - // "...if the reversing-adjusted start value of the running transition - // is the same as the value of the property in the after-change style (see - // the section on reversing of transitions for why these case exists), - // implementations must cancel the running transition and start - // a new transition..." - if replaced_transition.reversing_adjusted_start_value != self.property_animation.to { - return; - } - - // "* reversing-adjusted start value is the end value of the running transition" - let replaced_animation = &replaced_transition.property_animation; - self.reversing_adjusted_start_value = replaced_animation.to.clone(); - - // "* reversing shortening factor is the absolute value, clamped to the - // range [0, 1], of the sum of: - // 1. the output of the timing function of the old transition at the - // time of the style change event, times the reversing shortening - // factor of the old transition - // 2. 1 minus the reversing shortening factor of the old transition." - let transition_progress = ((now - replaced_transition.start_time) / - (replaced_transition.property_animation.duration)) - .min(1.0) - .max(0.0); - let timing_function_output = replaced_animation.timing_function_output(transition_progress); - let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor; - self.reversing_shortening_factor = ((timing_function_output * - old_reversing_shortening_factor) + - (1.0 - old_reversing_shortening_factor)) - .abs() - .min(1.0) - .max(0.0); - - // "* start time is the time of the style change event plus: - // 1. if the matching transition delay is nonnegative, the matching - // transition delay, or. - // 2. if the matching transition delay is negative, the product of the new - // transition’s reversing shortening factor and the matching transition delay," - self.start_time = if delay >= 0. { - now + delay - } else { - now + (self.reversing_shortening_factor * delay) - }; - - // "* end time is the start time plus the product of the matching transition - // duration and the new transition’s reversing shortening factor," - self.property_animation.duration *= self.reversing_shortening_factor; - - // "* start value is the current value of the property in the running transition, - // * end value is the value of the property in the after-change style," - let procedure = Procedure::Interpolate { - progress: timing_function_output, - }; - match replaced_animation - .from - .animate(&replaced_animation.to, procedure) - { - Ok(new_start) => self.property_animation.from = new_start, - Err(..) => {}, - } - } - - /// Whether or not this animation has ended at the provided time. This does - /// not take into account canceling i.e. when an animation or transition is - /// canceled due to changes in the style. - pub fn has_ended(&self, time: f64) -> bool { - time >= self.start_time + (self.property_animation.duration) - } - - /// Update the given animation at a given point of progress. - pub fn calculate_value(&self, time: f64) -> Option { - let progress = (time - self.start_time) / (self.property_animation.duration); - if progress < 0.0 { - return None; - } - - self.property_animation - .calculate_value(progress.min(1.0)) - .ok() - } -} - -/// Holds the animation state for a particular element. -#[derive(Debug, Default, MallocSizeOf)] -pub struct ElementAnimationSet { - /// The animations for this element. - pub animations: Vec, - - /// The transitions for this element. - pub transitions: Vec, - - /// Whether or not this ElementAnimationSet has had animations or transitions - /// which have been added, removed, or had their state changed. - pub dirty: bool, -} - -impl ElementAnimationSet { - /// Cancel all animations in this `ElementAnimationSet`. This is typically called - /// when the element has been removed from the DOM. - pub fn cancel_all_animations(&mut self) { - self.dirty = !self.animations.is_empty(); - for animation in self.animations.iter_mut() { - animation.state = AnimationState::Canceled; - } - self.cancel_active_transitions(); - } - - fn cancel_active_transitions(&mut self) { - for transition in self.transitions.iter_mut() { - if transition.state != AnimationState::Finished { - self.dirty = true; - transition.state = AnimationState::Canceled; - } - } - } - - /// Apply all active animations. - pub fn apply_active_animations( - &self, - context: &SharedStyleContext, - style: &mut Arc, - ) { - let now = context.current_time_for_animations; - let mutable_style = Arc::make_mut(style); - if let Some(map) = self.get_value_map_for_active_animations(now) { - for value in map.values() { - value.set_in_style_for_servo(mutable_style); - } - } - - if let Some(map) = self.get_value_map_for_active_transitions(now) { - for value in map.values() { - value.set_in_style_for_servo(mutable_style); - } - } - } - - /// Clear all canceled animations and transitions from this `ElementAnimationSet`. - pub fn clear_canceled_animations(&mut self) { - self.animations - .retain(|animation| animation.state != AnimationState::Canceled); - self.transitions - .retain(|animation| animation.state != AnimationState::Canceled); - } - - /// Whether this `ElementAnimationSet` is empty, which means it doesn't - /// hold any animations in any state. - pub fn is_empty(&self) -> bool { - self.animations.is_empty() && self.transitions.is_empty() - } - - /// Whether or not this state needs animation ticks for its transitions - /// or animations. - pub fn needs_animation_ticks(&self) -> bool { - self.animations - .iter() - .any(|animation| animation.state.needs_to_be_ticked()) || - self.transitions - .iter() - .any(|transition| transition.state.needs_to_be_ticked()) - } - - /// The number of running animations and transitions for this `ElementAnimationSet`. - pub fn running_animation_and_transition_count(&self) -> usize { - self.animations - .iter() - .filter(|animation| animation.state.needs_to_be_ticked()) - .count() + - self.transitions - .iter() - .filter(|transition| transition.state.needs_to_be_ticked()) - .count() - } - - /// If this `ElementAnimationSet` has any any active animations. - pub fn has_active_animation(&self) -> bool { - self.animations - .iter() - .any(|animation| animation.state != AnimationState::Canceled) - } - - /// If this `ElementAnimationSet` has any any active transitions. - pub fn has_active_transition(&self) -> bool { - self.transitions - .iter() - .any(|transition| transition.state != AnimationState::Canceled) - } - - /// Update our animations given a new style, canceling or starting new animations - /// when appropriate. - pub fn update_animations_for_new_style( - &mut self, - element: E, - context: &SharedStyleContext, - new_style: &Arc, - resolver: &mut StyleResolverForElement, - ) where - E: TElement, - { - for animation in self.animations.iter_mut() { - if animation.is_cancelled_in_new_style(new_style) { - animation.state = AnimationState::Canceled; - } - } - - maybe_start_animations(element, &context, &new_style, self, resolver); - } - - /// Update our transitions given a new style, canceling or starting new animations - /// when appropriate. - pub fn update_transitions_for_new_style( - &mut self, - might_need_transitions_update: bool, - context: &SharedStyleContext, - old_style: Option<&Arc>, - after_change_style: &Arc, - ) { - // If this is the first style, we don't trigger any transitions and we assume - // there were no previously triggered transitions. - let mut before_change_style = match old_style { - Some(old_style) => Arc::clone(old_style), - None => return, - }; - - // If the style of this element is display:none, then cancel all active transitions. - if after_change_style.get_box().clone_display().is_none() { - self.cancel_active_transitions(); - return; - } - - if !might_need_transitions_update { - return; - } - - // We convert old values into `before-change-style` here. - if self.has_active_transition() || self.has_active_animation() { - self.apply_active_animations(context, &mut before_change_style); - } - - let transitioning_properties = start_transitions_if_applicable( - context, - &before_change_style, - after_change_style, - self, - ); - - // Cancel any non-finished transitions that have properties which no longer transition. - for transition in self.transitions.iter_mut() { - if transition.state == AnimationState::Finished { - continue; - } - if transitioning_properties.contains(transition.property_animation.property_id()) { - continue; - } - transition.state = AnimationState::Canceled; - self.dirty = true; - } - } - - fn start_transition_if_applicable( - &mut self, - context: &SharedStyleContext, - longhand_id: LonghandId, - index: usize, - old_style: &ComputedValues, - new_style: &Arc, - ) { - if !longhand_id.is_transitionable() { - return; - } - - let style = new_style.get_ui(); - let timing_function = style.transition_timing_function_mod(index); - let duration = style.transition_duration_mod(index); - let delay = style.transition_delay_mod(index).seconds() as f64; - let now = context.current_time_for_animations; - - // Only start a new transition if the style actually changes between - // the old style and the new style. - let property_animation = match PropertyAnimation::from_longhand( - longhand_id, - timing_function, - duration, - old_style, - new_style, - ) { - Some(property_animation) => property_animation, - None => return, - }; - - // Per [1], don't trigger a new transition if the end state for that - // transition is the same as that of a transition that's running or - // completed. We don't take into account any canceled animations. - // [1]: https://drafts.csswg.org/css-transitions/#starting - if self - .transitions - .iter() - .filter(|transition| transition.state != AnimationState::Canceled) - .any(|transition| transition.property_animation.to == property_animation.to) - { - return; - } - - // We are going to start a new transition, but we might have to update - // it if we are replacing a reversed transition. - let reversing_adjusted_start_value = property_animation.from.clone(); - let mut new_transition = Transition { - start_time: now + delay, - delay, - property_animation, - state: AnimationState::Pending, - is_new: true, - reversing_adjusted_start_value, - reversing_shortening_factor: 1.0, - }; - - if let Some(old_transition) = self - .transitions - .iter_mut() - .filter(|transition| transition.state == AnimationState::Running) - .find(|transition| transition.property_animation.property_id() == longhand_id) - { - // We always cancel any running transitions for the same property. - old_transition.state = AnimationState::Canceled; - new_transition.update_for_possibly_reversed_transition(old_transition, delay, now); - } - - self.transitions.push(new_transition); - self.dirty = true; - } - - /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s - /// active transitions at the given time value. - pub fn get_value_map_for_active_transitions(&self, now: f64) -> Option { - if !self.has_active_transition() { - return None; - } - - let mut map = - AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default()); - for transition in &self.transitions { - if transition.state == AnimationState::Canceled { - continue; - } - let value = match transition.calculate_value(now) { - Some(value) => value, - None => continue, - }; - map.insert(value.id(), value); - } - - Some(map) - } - - /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s - /// active animations at the given time value. - pub fn get_value_map_for_active_animations(&self, now: f64) -> Option { - if !self.has_active_animation() { - return None; - } - - let mut map = Default::default(); - for animation in &self.animations { - animation.get_property_declaration_at_time(now, &mut map); - } - - Some(map) - } -} - -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)] -/// A key that is used to identify nodes in the `DocumentAnimationSet`. -pub struct AnimationSetKey { - /// The node for this `AnimationSetKey`. - pub node: OpaqueNode, - /// The pseudo element for this `AnimationSetKey`. If `None` this key will - /// refer to the main content for its node. - pub pseudo_element: Option, -} - -impl AnimationSetKey { - /// Create a new key given a node and optional pseudo element. - pub fn new(node: OpaqueNode, pseudo_element: Option) -> Self { - AnimationSetKey { - node, - pseudo_element, - } - } - - /// Create a new key for the main content of this node. - pub fn new_for_non_pseudo(node: OpaqueNode) -> Self { - AnimationSetKey { - node, - pseudo_element: None, - } - } - - /// Create a new key for given node and pseudo element. - pub fn new_for_pseudo(node: OpaqueNode, pseudo_element: PseudoElement) -> Self { - AnimationSetKey { - node, - pseudo_element: Some(pseudo_element), - } - } -} - -#[derive(Clone, Debug, Default, MallocSizeOf)] -/// A set of animations for a document. -pub struct DocumentAnimationSet { - /// The `ElementAnimationSet`s that this set contains. - #[ignore_malloc_size_of = "Arc is hard"] - pub sets: Arc>>, -} - -impl DocumentAnimationSet { - /// Return whether or not the provided node has active CSS animations. - pub fn has_active_animations(&self, key: &AnimationSetKey) -> bool { - self.sets - .read() - .get(key) - .map_or(false, |set| set.has_active_animation()) - } - - /// Return whether or not the provided node has active CSS transitions. - pub fn has_active_transitions(&self, key: &AnimationSetKey) -> bool { - self.sets - .read() - .get(key) - .map_or(false, |set| set.has_active_transition()) - } - - /// Return a locked PropertyDeclarationBlock with animation values for the given - /// key and time. - pub fn get_animation_declarations( - &self, - key: &AnimationSetKey, - time: f64, - shared_lock: &SharedRwLock, - ) -> Option>> { - self.sets - .read() - .get(key) - .and_then(|set| set.get_value_map_for_active_animations(time)) - .map(|map| { - let block = PropertyDeclarationBlock::from_animation_value_map(&map); - Arc::new(shared_lock.wrap(block)) - }) - } - - /// Return a locked PropertyDeclarationBlock with transition values for the given - /// key and time. - pub fn get_transition_declarations( - &self, - key: &AnimationSetKey, - time: f64, - shared_lock: &SharedRwLock, - ) -> Option>> { - self.sets - .read() - .get(key) - .and_then(|set| set.get_value_map_for_active_transitions(time)) - .map(|map| { - let block = PropertyDeclarationBlock::from_animation_value_map(&map); - Arc::new(shared_lock.wrap(block)) - }) - } - - /// Get all the animation declarations for the given key, returning an empty - /// `AnimationDeclarations` if there are no animations. - pub fn get_all_declarations( - &self, - key: &AnimationSetKey, - time: f64, - shared_lock: &SharedRwLock, - ) -> AnimationDeclarations { - let sets = self.sets.read(); - let set = match sets.get(key) { - Some(set) => set, - None => return Default::default(), - }; - - let animations = set.get_value_map_for_active_animations(time).map(|map| { - let block = PropertyDeclarationBlock::from_animation_value_map(&map); - Arc::new(shared_lock.wrap(block)) - }); - let transitions = set.get_value_map_for_active_transitions(time).map(|map| { - let block = PropertyDeclarationBlock::from_animation_value_map(&map); - Arc::new(shared_lock.wrap(block)) - }); - AnimationDeclarations { - animations, - transitions, - } - } - - /// Cancel all animations for set at the given key. - pub fn cancel_all_animations_for_key(&self, key: &AnimationSetKey) { - if let Some(set) = self.sets.write().get_mut(key) { - set.cancel_all_animations(); - } - } -} - -/// Kick off any new transitions for this node and return all of the properties that are -/// transitioning. This is at the end of calculating style for a single node. -pub fn start_transitions_if_applicable( - context: &SharedStyleContext, - old_style: &ComputedValues, - new_style: &Arc, - animation_state: &mut ElementAnimationSet, -) -> LonghandIdSet { - let mut properties_that_transition = LonghandIdSet::new(); - for transition in new_style.transition_properties() { - let physical_property = transition.longhand_id.to_physical(new_style.writing_mode); - if properties_that_transition.contains(physical_property) { - continue; - } - - properties_that_transition.insert(physical_property); - animation_state.start_transition_if_applicable( - context, - physical_property, - transition.index, - old_style, - new_style, - ); - } - - properties_that_transition -} - -/// Triggers animations for a given node looking at the animation property -/// values. -pub fn maybe_start_animations( - element: E, - context: &SharedStyleContext, - new_style: &Arc, - animation_state: &mut ElementAnimationSet, - resolver: &mut StyleResolverForElement, -) where - E: TElement, -{ - let style = new_style.get_ui(); - for (i, name) in style.animation_name_iter().enumerate() { - let name = match name.as_atom() { - Some(atom) => atom, - None => continue, - }; - - debug!("maybe_start_animations: name={}", name); - let duration = style.animation_duration_mod(i).seconds() as f64; - if duration == 0. { - continue; - } - - let keyframe_animation = match context.stylist.get_animation(name, element) { - Some(animation) => animation, - None => continue, - }; - - debug!("maybe_start_animations: animation {} found", name); - - // If this animation doesn't have any keyframe, we can just continue - // without submitting it to the compositor, since both the first and - // the second keyframes would be synthetised from the computed - // values. - if keyframe_animation.steps.is_empty() { - continue; - } - - // NB: This delay may be negative, meaning that the animation may be created - // in a state where we have advanced one or more iterations or even that the - // animation begins in a finished state. - let delay = style.animation_delay_mod(i).seconds(); - - let iteration_count = style.animation_iteration_count_mod(i); - let iteration_state = if iteration_count.0.is_infinite() { - KeyframesIterationState::Infinite(0.0) - } else { - KeyframesIterationState::Finite(0.0, iteration_count.0 as f64) - }; - - let animation_direction = style.animation_direction_mod(i); - - let initial_direction = match animation_direction { - AnimationDirection::Normal | AnimationDirection::Alternate => { - AnimationDirection::Normal - }, - AnimationDirection::Reverse | AnimationDirection::AlternateReverse => { - AnimationDirection::Reverse - }, - }; - - let now = context.current_time_for_animations; - let started_at = now + delay as f64; - let mut starting_progress = (now - started_at) / duration; - let state = match style.animation_play_state_mod(i) { - AnimationPlayState::Paused => AnimationState::Paused(starting_progress), - AnimationPlayState::Running => AnimationState::Pending, - }; - - let computed_steps = ComputedKeyframe::generate_for_keyframes( - element, - &keyframe_animation, - context, - new_style, - style.animation_timing_function_mod(i), - resolver, - ); - - let mut new_animation = Animation { - name: name.clone(), - properties_changed: keyframe_animation.properties_changed, - computed_steps, - started_at, - duration, - fill_mode: style.animation_fill_mode_mod(i), - delay: delay as f64, - iteration_state, - state, - direction: animation_direction, - current_direction: initial_direction, - cascade_style: new_style.clone(), - is_new: true, - }; - - // If we started with a negative delay, make sure we iterate the animation if - // the delay moves us past the first iteration. - while starting_progress > 1. && !new_animation.on_last_iteration() { - new_animation.iterate(); - starting_progress -= 1.; - } - - animation_state.dirty = true; - - // If the animation was already present in the list for the node, just update its state. - for existing_animation in animation_state.animations.iter_mut() { - if existing_animation.state == AnimationState::Canceled { - continue; - } - - if new_animation.name == existing_animation.name { - existing_animation - .update_from_other(&new_animation, context.current_time_for_animations); - return; - } - } - - animation_state.animations.push(new_animation); - } -} diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs deleted file mode 100644 index a0dbb60da8e..00000000000 --- a/components/style/applicable_declarations.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Applicable declarations management. - -use crate::properties::PropertyDeclarationBlock; -use crate::rule_tree::{CascadeLevel, StyleSource}; -use crate::shared_lock::Locked; -use crate::stylesheets::layer_rule::LayerOrder; -use servo_arc::Arc; -use smallvec::SmallVec; - -/// List of applicable declarations. This is a transient structure that shuttles -/// declarations between selector matching and inserting into the rule tree, and -/// therefore we want to avoid heap-allocation where possible. -/// -/// In measurements on wikipedia, we pretty much never have more than 8 applicable -/// declarations, so we could consider making this 8 entries instead of 16. -/// However, it may depend a lot on workload, and stack space is cheap. -pub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>; - -/// Blink uses 18 bits to store source order, and does not check overflow [1]. -/// That's a limit that could be reached in realistic webpages, so we use -/// 24 bits and enforce defined behavior in the overflow case. -/// -/// Note that right now this restriction could be lifted if wanted (because we -/// no longer stash the cascade level in the remaining bits), but we keep it in -/// place in case we come up with a use-case for them, lacking reports of the -/// current limit being too small. -/// -/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/ -/// RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3 -const SOURCE_ORDER_BITS: usize = 24; -const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1; -const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX; - -/// The cascade-level+layer order of this declaration. -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] -pub struct CascadePriority { - cascade_level: CascadeLevel, - layer_order: LayerOrder, -} - -const_assert_eq!( - std::mem::size_of::(), - std::mem::size_of::() -); - -impl PartialOrd for CascadePriority { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for CascadePriority { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.cascade_level.cmp(&other.cascade_level).then_with(|| { - let ordering = self.layer_order.cmp(&other.layer_order); - if ordering == std::cmp::Ordering::Equal { - return ordering; - } - // https://drafts.csswg.org/css-cascade-5/#cascade-layering - // - // Cascade layers (like declarations) are ordered by order - // of appearance. When comparing declarations that belong to - // different layers, then for normal rules the declaration - // whose cascade layer is last wins, and for important rules - // the declaration whose cascade layer is first wins. - // - // But the style attribute layer for some reason is special. - if self.cascade_level.is_important() && - !self.layer_order.is_style_attribute_layer() && - !other.layer_order.is_style_attribute_layer() - { - ordering.reverse() - } else { - ordering - } - }) - } -} - -impl CascadePriority { - /// Construct a new CascadePriority for a given (level, order) pair. - pub fn new(cascade_level: CascadeLevel, layer_order: LayerOrder) -> Self { - Self { - cascade_level, - layer_order, - } - } - - /// Returns the layer order. - #[inline] - pub fn layer_order(&self) -> LayerOrder { - self.layer_order - } - - /// Returns the cascade level. - #[inline] - pub fn cascade_level(&self) -> CascadeLevel { - self.cascade_level - } - - /// Whether this declaration should be allowed if `revert` or `revert-layer` - /// have been specified on a given origin. - /// - /// `self` is the priority at which the `revert` or `revert-layer` keyword - /// have been specified. - pub fn allows_when_reverted(&self, other: &Self, origin_revert: bool) -> bool { - if origin_revert { - other.cascade_level.origin() < self.cascade_level.origin() - } else { - other.unimportant() < self.unimportant() - } - } - - /// Convert this priority from "important" to "non-important", if needed. - pub fn unimportant(&self) -> Self { - Self::new(self.cascade_level().unimportant(), self.layer_order()) - } - - /// Convert this priority from "non-important" to "important", if needed. - pub fn important(&self) -> Self { - Self::new(self.cascade_level().important(), self.layer_order()) - } -} - -/// A property declaration together with its precedence among rules of equal -/// specificity so that we can sort them. -/// -/// This represents the declarations in a given declaration block for a given -/// importance. -#[derive(Clone, Debug, MallocSizeOf, PartialEq)] -pub struct ApplicableDeclarationBlock { - /// The style source, either a style rule, or a property declaration block. - #[ignore_malloc_size_of = "Arc"] - pub source: StyleSource, - /// The bits containing the source order, cascade level, and shadow cascade - /// order. - source_order: u32, - /// The specificity of the selector. - pub specificity: u32, - /// The cascade priority of the rule. - pub cascade_priority: CascadePriority, -} - -impl ApplicableDeclarationBlock { - /// Constructs an applicable declaration block from a given property - /// declaration block and importance. - #[inline] - pub fn from_declarations( - declarations: Arc>, - level: CascadeLevel, - layer_order: LayerOrder, - ) -> Self { - ApplicableDeclarationBlock { - source: StyleSource::from_declarations(declarations), - source_order: 0, - specificity: 0, - cascade_priority: CascadePriority::new(level, layer_order), - } - } - - /// Constructs an applicable declaration block from the given components. - #[inline] - pub fn new( - source: StyleSource, - source_order: u32, - level: CascadeLevel, - specificity: u32, - layer_order: LayerOrder, - ) -> Self { - ApplicableDeclarationBlock { - source, - source_order: source_order & SOURCE_ORDER_MASK, - specificity, - cascade_priority: CascadePriority::new(level, layer_order), - } - } - - /// Returns the source order of the block. - #[inline] - pub fn source_order(&self) -> u32 { - self.source_order - } - - /// Returns the cascade level of the block. - #[inline] - pub fn level(&self) -> CascadeLevel { - self.cascade_priority.cascade_level() - } - - /// Returns the cascade level of the block. - #[inline] - pub fn layer_order(&self) -> LayerOrder { - self.cascade_priority.layer_order() - } - - /// Convenience method to consume self and return the right thing for the - /// rule tree to iterate over. - #[inline] - pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) { - (self.source, self.cascade_priority) - } -} - -// Size of this struct determines sorting and selector-matching performance. -size_of_test!(ApplicableDeclarationBlock, 24); diff --git a/components/style/attr.rs b/components/style/attr.rs deleted file mode 100644 index 7747921ffe9..00000000000 --- a/components/style/attr.rs +++ /dev/null @@ -1,601 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Parsed representations of [DOM attributes][attr]. -//! -//! [attr]: https://dom.spec.whatwg.org/#interface-attr - -use crate::properties::PropertyDeclarationBlock; -use crate::shared_lock::Locked; -use crate::str::str_join; -use crate::str::{read_exponent, read_fraction, HTML_SPACE_CHARACTERS}; -use crate::str::{read_numbers, split_commas, split_html_space_chars}; -use crate::values::specified::Length; -use crate::values::AtomString; -use crate::{Atom, LocalName, Namespace, Prefix}; -use app_units::Au; -use cssparser::{self, Color, RGBA}; -use euclid::num::Zero; -use num_traits::ToPrimitive; -use selectors::attr::AttrSelectorOperation; -use servo_arc::Arc; -use std::str::FromStr; - -// Duplicated from script::dom::values. -const UNSIGNED_LONG_MAX: u32 = 2147483647; - -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] -pub enum LengthOrPercentageOrAuto { - Auto, - Percentage(f32), - Length(Au), -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] -pub enum AttrValue { - String(String), - TokenList(String, Vec), - UInt(String, u32), - Int(String, i32), - Double(String, f64), - Atom(Atom), - Length(String, Option), - Color(String, Option), - Dimension(String, LengthOrPercentageOrAuto), - - /// Stores a URL, computed from the input string and a document's base URL. - /// - /// The URL is resolved at setting-time, so this kind of attribute value is - /// not actually suitable for most URL-reflecting IDL attributes. - ResolvedUrl( - String, - #[ignore_malloc_size_of = "Arc"] Option> - ), - - /// Note that this variant is only used transitively as a fast path to set - /// the property declaration block relevant to the style of an element when - /// set from the inline declaration of that element (that is, - /// `element.style`). - /// - /// This can, as of this writing, only correspond to the value of the - /// `style` element, and is set from its relevant CSSInlineStyleDeclaration, - /// and then converted to a string in Element::attribute_mutated. - /// - /// Note that we don't necessarily need to do that (we could just clone the - /// declaration block), but that avoids keeping a refcounted - /// declarationblock for longer than needed. - Declaration( - String, - #[ignore_malloc_size_of = "Arc"] Arc>, - ), -} - -/// Shared implementation to parse an integer according to -/// or -/// -fn do_parse_integer>(input: T) -> Result { - let mut input = input - .skip_while(|c| HTML_SPACE_CHARACTERS.iter().any(|s| s == c)) - .peekable(); - - let sign = match input.peek() { - None => return Err(()), - Some(&'-') => { - input.next(); - -1 - }, - Some(&'+') => { - input.next(); - 1 - }, - Some(_) => 1, - }; - - let (value, _) = read_numbers(input); - - value.and_then(|value| value.checked_mul(sign)).ok_or(()) -} - -/// Parse an integer according to -/// . -pub fn parse_integer>(input: T) -> Result { - do_parse_integer(input).and_then(|result| result.to_i32().ok_or(())) -} - -/// Parse an integer according to -/// -pub fn parse_unsigned_integer>(input: T) -> Result { - do_parse_integer(input).and_then(|result| result.to_u32().ok_or(())) -} - -/// Parse a floating-point number according to -/// -pub fn parse_double(string: &str) -> Result { - let trimmed = string.trim_matches(HTML_SPACE_CHARACTERS); - let mut input = trimmed.chars().peekable(); - - let (value, divisor, chars_skipped) = match input.peek() { - None => return Err(()), - Some(&'-') => { - input.next(); - (-1f64, -1f64, 1) - }, - Some(&'+') => { - input.next(); - (1f64, 1f64, 1) - }, - _ => (1f64, 1f64, 0), - }; - - let (value, value_digits) = if let Some(&'.') = input.peek() { - (0f64, 0) - } else { - let (read_val, read_digits) = read_numbers(input); - ( - value * read_val.and_then(|result| result.to_f64()).unwrap_or(1f64), - read_digits, - ) - }; - - let input = trimmed - .chars() - .skip(value_digits + chars_skipped) - .peekable(); - - let (mut value, fraction_digits) = read_fraction(input, divisor, value); - - let input = trimmed - .chars() - .skip(value_digits + chars_skipped + fraction_digits) - .peekable(); - - if let Some(exp) = read_exponent(input) { - value *= 10f64.powi(exp) - }; - - Ok(value) -} - -impl AttrValue { - pub fn from_serialized_tokenlist(tokens: String) -> AttrValue { - let atoms = - split_html_space_chars(&tokens) - .map(Atom::from) - .fold(vec![], |mut acc, atom| { - if !acc.contains(&atom) { - acc.push(atom) - } - acc - }); - AttrValue::TokenList(tokens, atoms) - } - - pub fn from_comma_separated_tokenlist(tokens: String) -> AttrValue { - let atoms = split_commas(&tokens) - .map(Atom::from) - .fold(vec![], |mut acc, atom| { - if !acc.contains(&atom) { - acc.push(atom) - } - acc - }); - AttrValue::TokenList(tokens, atoms) - } - - pub fn from_atomic_tokens(atoms: Vec) -> AttrValue { - // TODO(ajeffrey): effecient conversion of Vec to String - let tokens = String::from(str_join(&atoms, "\x20")); - AttrValue::TokenList(tokens, atoms) - } - - // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-unsigned-long - pub fn from_u32(string: String, default: u32) -> AttrValue { - let result = parse_unsigned_integer(string.chars()).unwrap_or(default); - let result = if result > UNSIGNED_LONG_MAX { - default - } else { - result - }; - AttrValue::UInt(string, result) - } - - pub fn from_i32(string: String, default: i32) -> AttrValue { - let result = parse_integer(string.chars()).unwrap_or(default); - AttrValue::Int(string, result) - } - - // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-double - pub fn from_double(string: String, default: f64) -> AttrValue { - let result = parse_double(&string).unwrap_or(default); - - if result.is_normal() { - AttrValue::Double(string, result) - } else { - AttrValue::Double(string, default) - } - } - - // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers - pub fn from_limited_i32(string: String, default: i32) -> AttrValue { - let result = parse_integer(string.chars()).unwrap_or(default); - - if result < 0 { - AttrValue::Int(string, default) - } else { - AttrValue::Int(string, result) - } - } - - // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers-greater-than-zero - pub fn from_limited_u32(string: String, default: u32) -> AttrValue { - let result = parse_unsigned_integer(string.chars()).unwrap_or(default); - let result = if result == 0 || result > UNSIGNED_LONG_MAX { - default - } else { - result - }; - AttrValue::UInt(string, result) - } - - pub fn from_atomic(string: String) -> AttrValue { - let value = Atom::from(string); - AttrValue::Atom(value) - } - - pub fn from_resolved_url(base: &Arc<::url::Url>, url: String) -> AttrValue { - let joined = base.join(&url).ok().map(Arc::new); - AttrValue::ResolvedUrl(url, joined) - } - - pub fn from_legacy_color(string: String) -> AttrValue { - let parsed = parse_legacy_color(&string).ok(); - AttrValue::Color(string, parsed) - } - - pub fn from_dimension(string: String) -> AttrValue { - let parsed = parse_length(&string); - AttrValue::Dimension(string, parsed) - } - - pub fn from_nonzero_dimension(string: String) -> AttrValue { - let parsed = parse_nonzero_length(&string); - AttrValue::Dimension(string, parsed) - } - - /// Assumes the `AttrValue` is a `TokenList` and returns its tokens - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `TokenList` - pub fn as_tokens(&self) -> &[Atom] { - match *self { - AttrValue::TokenList(_, ref tokens) => tokens, - _ => panic!("Tokens not found"), - } - } - - /// Assumes the `AttrValue` is an `Atom` and returns its value - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not an `Atom` - pub fn as_atom(&self) -> &Atom { - match *self { - AttrValue::Atom(ref value) => value, - _ => panic!("Atom not found"), - } - } - - /// Assumes the `AttrValue` is a `Color` and returns its value - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `Color` - pub fn as_color(&self) -> Option<&RGBA> { - match *self { - AttrValue::Color(_, ref color) => color.as_ref(), - _ => panic!("Color not found"), - } - } - - /// Assumes the `AttrValue` is a `Dimension` and returns its value - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `Dimension` - pub fn as_dimension(&self) -> &LengthOrPercentageOrAuto { - match *self { - AttrValue::Dimension(_, ref l) => l, - _ => panic!("Dimension not found"), - } - } - - /// Assumes the `AttrValue` is a `ResolvedUrl` and returns its value. - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `ResolvedUrl` - pub fn as_resolved_url(&self) -> Option<&Arc<::url::Url>> { - match *self { - AttrValue::ResolvedUrl(_, ref url) => url.as_ref(), - _ => panic!("Url not found"), - } - } - - /// Return the AttrValue as its integer representation, if any. - /// This corresponds to attribute values returned as `AttrValue::UInt(_)` - /// by `VirtualMethods::parse_plain_attribute()`. - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `UInt` - pub fn as_uint(&self) -> u32 { - if let AttrValue::UInt(_, value) = *self { - value - } else { - panic!("Uint not found"); - } - } - - /// Return the AttrValue as a dimension computed from its integer - /// representation, assuming that integer representation specifies pixels. - /// - /// This corresponds to attribute values returned as `AttrValue::UInt(_)` - /// by `VirtualMethods::parse_plain_attribute()`. - /// - /// ## Panics - /// - /// Panics if the `AttrValue` is not a `UInt` - pub fn as_uint_px_dimension(&self) -> LengthOrPercentageOrAuto { - if let AttrValue::UInt(_, value) = *self { - LengthOrPercentageOrAuto::Length(Au::from_px(value as i32)) - } else { - panic!("Uint not found"); - } - } - - pub fn eval_selector(&self, selector: &AttrSelectorOperation<&AtomString>) -> bool { - // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants - // and doing Atom comparisons instead of string comparisons where possible, - // with SelectorImpl::AttrValue changed to Atom. - selector.eval_str(self) - } -} - -impl ::std::ops::Deref for AttrValue { - type Target = str; - - fn deref(&self) -> &str { - match *self { - AttrValue::String(ref value) | - AttrValue::TokenList(ref value, _) | - AttrValue::UInt(ref value, _) | - AttrValue::Double(ref value, _) | - AttrValue::Length(ref value, _) | - AttrValue::Color(ref value, _) | - AttrValue::Int(ref value, _) | - AttrValue::ResolvedUrl(ref value, _) | - AttrValue::Declaration(ref value, _) | - AttrValue::Dimension(ref value, _) => &value, - AttrValue::Atom(ref value) => &value, - } - } -} - -impl PartialEq for AttrValue { - fn eq(&self, other: &Atom) -> bool { - match *self { - AttrValue::Atom(ref value) => value == other, - _ => other == &**self, - } - } -} - -/// -pub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto { - match parse_length(value) { - LengthOrPercentageOrAuto::Length(x) if x == Au::zero() => LengthOrPercentageOrAuto::Auto, - LengthOrPercentageOrAuto::Percentage(x) if x == 0. => LengthOrPercentageOrAuto::Auto, - x => x, - } -} - -/// Parses a [legacy color][color]. If unparseable, `Err` is returned. -/// -/// [color]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-colour-value -pub fn parse_legacy_color(mut input: &str) -> Result { - // Steps 1 and 2. - if input.is_empty() { - return Err(()); - } - - // Step 3. - input = input.trim_matches(HTML_SPACE_CHARACTERS); - - // Step 4. - if input.eq_ignore_ascii_case("transparent") { - return Err(()); - } - - // Step 5. - if let Ok(Color::Rgba(rgba)) = cssparser::parse_color_keyword(input) { - return Ok(rgba); - } - - // Step 6. - if input.len() == 4 { - if let (b'#', Ok(r), Ok(g), Ok(b)) = ( - input.as_bytes()[0], - hex(input.as_bytes()[1] as char), - hex(input.as_bytes()[2] as char), - hex(input.as_bytes()[3] as char), - ) { - return Ok(RGBA::new(Some(r * 17), Some(g * 17), Some(b * 17), Some(1.0))); - } - } - - // Step 7. - let mut new_input = String::new(); - for ch in input.chars() { - if ch as u32 > 0xffff { - new_input.push_str("00") - } else { - new_input.push(ch) - } - } - let mut input = &*new_input; - - // Step 8. - for (char_count, (index, _)) in input.char_indices().enumerate() { - if char_count == 128 { - input = &input[..index]; - break; - } - } - - // Step 9. - if input.as_bytes()[0] == b'#' { - input = &input[1..] - } - - // Step 10. - let mut new_input = Vec::new(); - for ch in input.chars() { - if hex(ch).is_ok() { - new_input.push(ch as u8) - } else { - new_input.push(b'0') - } - } - let mut input = new_input; - - // Step 11. - while input.is_empty() || (input.len() % 3) != 0 { - input.push(b'0') - } - - // Step 12. - let mut length = input.len() / 3; - let (mut red, mut green, mut blue) = ( - &input[..length], - &input[length..length * 2], - &input[length * 2..], - ); - - // Step 13. - if length > 8 { - red = &red[length - 8..]; - green = &green[length - 8..]; - blue = &blue[length - 8..]; - length = 8 - } - - // Step 14. - while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' { - red = &red[1..]; - green = &green[1..]; - blue = &blue[1..]; - length -= 1 - } - - // Steps 15-20. - return Ok(RGBA::new( - Some(hex_string(red).unwrap()), - Some(hex_string(green).unwrap()), - Some(hex_string(blue).unwrap()), - Some(1.0), - )); - - fn hex(ch: char) -> Result { - match ch { - '0'..='9' => Ok((ch as u8) - b'0'), - 'a'..='f' => Ok((ch as u8) - b'a' + 10), - 'A'..='F' => Ok((ch as u8) - b'A' + 10), - _ => Err(()), - } - } - - fn hex_string(string: &[u8]) -> Result { - match string.len() { - 0 => Err(()), - 1 => hex(string[0] as char), - _ => { - let upper = hex(string[0] as char)?; - let lower = hex(string[1] as char)?; - Ok((upper << 4) | lower) - }, - } - } -} - -/// Parses a [dimension value][dim]. If unparseable, `Auto` is returned. -/// -/// [dim]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values -// TODO: this function can be rewritten to return Result -pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto { - // Steps 1 & 2 are not relevant - - // Step 3 - value = value.trim_start_matches(HTML_SPACE_CHARACTERS); - - // Step 4 - match value.chars().nth(0) { - Some('0'..='9') => {}, - _ => return LengthOrPercentageOrAuto::Auto, - } - - // Steps 5 to 8 - // We trim the string length to the minimum of: - // 1. the end of the string - // 2. the first occurence of a '%' (U+0025 PERCENT SIGN) - // 3. the second occurrence of a '.' (U+002E FULL STOP) - // 4. the occurrence of a character that is neither a digit nor '%' nor '.' - // Note: Step 7.4 is directly subsumed by FromStr::from_str - let mut end_index = value.len(); - let (mut found_full_stop, mut found_percent) = (false, false); - for (i, ch) in value.chars().enumerate() { - match ch { - '0'..='9' => continue, - '%' => { - found_percent = true; - end_index = i; - break; - }, - '.' if !found_full_stop => { - found_full_stop = true; - continue; - }, - _ => { - end_index = i; - break; - }, - } - } - value = &value[..end_index]; - - if found_percent { - let result: Result = FromStr::from_str(value); - match result { - Ok(number) => return LengthOrPercentageOrAuto::Percentage((number as f32) / 100.0), - Err(_) => return LengthOrPercentageOrAuto::Auto, - } - } - - match FromStr::from_str(value) { - Ok(number) => LengthOrPercentageOrAuto::Length(Au::from_f64_px(number)), - Err(_) => LengthOrPercentageOrAuto::Auto, - } -} - -/// A struct that uniquely identifies an element's attribute. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] -pub struct AttrIdentifier { - pub local_name: LocalName, - pub name: LocalName, - pub namespace: Namespace, - pub prefix: Option, -} diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs deleted file mode 100644 index a0223dceccc..00000000000 --- a/components/style/author_styles.rs +++ /dev/null @@ -1,70 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A set of author stylesheets and their computed representation, such as the -//! ones used for ShadowRoot. - -use crate::dom::TElement; -use crate::invalidation::media_queries::ToMediaListKey; -use crate::shared_lock::SharedRwLockReadGuard; -use crate::stylesheet_set::AuthorStylesheetSet; -use crate::stylesheets::StylesheetInDocument; -use crate::stylist::CascadeData; -use crate::stylist::Stylist; -use servo_arc::Arc; - -/// A set of author stylesheets and their computed representation, such as the -/// ones used for ShadowRoot. -#[derive(MallocSizeOf)] -pub struct GenericAuthorStyles -where - S: StylesheetInDocument + PartialEq + 'static, -{ - /// The sheet collection, which holds the sheet pointers, the invalidations, - /// and all that stuff. - pub stylesheets: AuthorStylesheetSet, - /// The actual cascade data computed from the stylesheets. - #[ignore_malloc_size_of = "Measured as part of the stylist"] - pub data: Arc, -} - -pub use self::GenericAuthorStyles as AuthorStyles; - -lazy_static! { - static ref EMPTY_CASCADE_DATA: Arc = Arc::new_leaked(CascadeData::new()); -} - -impl GenericAuthorStyles -where - S: StylesheetInDocument + PartialEq + 'static, -{ - /// Create an empty AuthorStyles. - #[inline] - pub fn new() -> Self { - Self { - stylesheets: AuthorStylesheetSet::new(), - data: EMPTY_CASCADE_DATA.clone(), - } - } - - /// Flush the pending sheet changes, updating `data` as appropriate. - /// - /// TODO(emilio): Need a host element and a snapshot map to do invalidation - /// properly. - #[inline] - pub fn flush(&mut self, stylist: &mut Stylist, guard: &SharedRwLockReadGuard) - where - E: TElement, - S: ToMediaListKey, - { - let flusher = self - .stylesheets - .flush::(/* host = */ None, /* snapshot_map = */ None); - - let result = stylist.rebuild_author_data(&self.data, flusher.sheets, guard); - if let Ok(Some(new_data)) = result { - self.data = new_data; - } - } -} diff --git a/components/style/bezier.rs b/components/style/bezier.rs deleted file mode 100644 index dd520ac0ed5..00000000000 --- a/components/style/bezier.rs +++ /dev/null @@ -1,176 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Parametric Bézier curves. -//! -//! This is based on `WebCore/platform/graphics/UnitBezier.h` in WebKit. - -#![deny(missing_docs)] - -use crate::values::CSSFloat; - -const NEWTON_METHOD_ITERATIONS: u8 = 8; - -/// A unit cubic Bézier curve, used for timing functions in CSS transitions and animations. -pub struct Bezier { - ax: f64, - bx: f64, - cx: f64, - ay: f64, - by: f64, - cy: f64, -} - -impl Bezier { - /// Calculate the output of a unit cubic Bézier curve from the two middle control points. - /// - /// X coordinate is time, Y coordinate is function advancement. - /// The nominal range for both is 0 to 1. - /// - /// The start and end points are always (0, 0) and (1, 1) so that a transition or animation - /// starts at 0% and ends at 100%. - pub fn calculate_bezier_output( - progress: f64, - epsilon: f64, - x1: f32, - y1: f32, - x2: f32, - y2: f32, - ) -> f64 { - // Check for a linear curve. - if x1 == y1 && x2 == y2 { - return progress; - } - - // Ensure that we return 0 or 1 on both edges. - if progress == 0.0 { - return 0.0; - } - if progress == 1.0 { - return 1.0; - } - - // For negative values, try to extrapolate with tangent (p1 - p0) or, - // if p1 is coincident with p0, with (p2 - p0). - if progress < 0.0 { - if x1 > 0.0 { - return progress * y1 as f64 / x1 as f64; - } - if y1 == 0.0 && x2 > 0.0 { - return progress * y2 as f64 / x2 as f64; - } - // If we can't calculate a sensible tangent, don't extrapolate at all. - return 0.0; - } - - // For values greater than 1, try to extrapolate with tangent (p2 - p3) or, - // if p2 is coincident with p3, with (p1 - p3). - if progress > 1.0 { - if x2 < 1.0 { - return 1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0); - } - if y2 == 1.0 && x1 < 1.0 { - return 1.0 + (progress - 1.0) * (y1 as f64 - 1.0) / (x1 as f64 - 1.0); - } - // If we can't calculate a sensible tangent, don't extrapolate at all. - return 1.0; - } - - Bezier::new(x1, y1, x2, y2).solve(progress, epsilon) - } - - #[inline] - fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier { - let cx = 3. * x1 as f64; - let bx = 3. * (x2 as f64 - x1 as f64) - cx; - - let cy = 3. * y1 as f64; - let by = 3. * (y2 as f64 - y1 as f64) - cy; - - Bezier { - ax: 1.0 - cx - bx, - bx: bx, - cx: cx, - ay: 1.0 - cy - by, - by: by, - cy: cy, - } - } - - #[inline] - fn sample_curve_x(&self, t: f64) -> f64 { - // ax * t^3 + bx * t^2 + cx * t - ((self.ax * t + self.bx) * t + self.cx) * t - } - - #[inline] - fn sample_curve_y(&self, t: f64) -> f64 { - ((self.ay * t + self.by) * t + self.cy) * t - } - - #[inline] - fn sample_curve_derivative_x(&self, t: f64) -> f64 { - (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx - } - - #[inline] - fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 { - // Fast path: Use Newton's method. - let mut t = x; - for _ in 0..NEWTON_METHOD_ITERATIONS { - let x2 = self.sample_curve_x(t); - if x2.approx_eq(x, epsilon) { - return t; - } - let dx = self.sample_curve_derivative_x(t); - if dx.approx_eq(0.0, 1e-6) { - break; - } - t -= (x2 - x) / dx; - } - - // Slow path: Use bisection. - let (mut lo, mut hi, mut t) = (0.0, 1.0, x); - - if t < lo { - return lo; - } - if t > hi { - return hi; - } - - while lo < hi { - let x2 = self.sample_curve_x(t); - if x2.approx_eq(x, epsilon) { - return t; - } - if x > x2 { - lo = t - } else { - hi = t - } - t = (hi - lo) / 2.0 + lo - } - - t - } - - /// Solve the bezier curve for a given `x` and an `epsilon`, that should be - /// between zero and one. - #[inline] - fn solve(&self, x: f64, epsilon: f64) -> f64 { - self.sample_curve_y(self.solve_curve_x(x, epsilon)) - } -} - -trait ApproxEq { - fn approx_eq(self, value: Self, epsilon: Self) -> bool; -} - -impl ApproxEq for f64 { - #[inline] - fn approx_eq(self, value: f64, epsilon: f64) -> bool { - (self - value).abs() < epsilon - } -} diff --git a/components/style/bloom.rs b/components/style/bloom.rs deleted file mode 100644 index dc722b7bdba..00000000000 --- a/components/style/bloom.rs +++ /dev/null @@ -1,401 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The style bloom filter is used as an optimization when matching deep -//! descendant selectors. - -#![deny(missing_docs)] - -use crate::dom::{SendElement, TElement}; -use crate::LocalName; -use atomic_refcell::{AtomicRefCell, AtomicRefMut}; -use owning_ref::OwningHandle; -use selectors::bloom::BloomFilter; -use servo_arc::Arc; -use smallvec::SmallVec; -use std::mem::ManuallyDrop; - -thread_local! { - /// Bloom filters are large allocations, so we store them in thread-local storage - /// such that they can be reused across style traversals. StyleBloom is responsible - /// for ensuring that the bloom filter is zeroed when it is dropped. - /// - /// We intentionally leak this from TLS because we don't have the guarantee - /// of TLS destructors to run in worker threads. - /// - /// We could change this once https://github.com/rayon-rs/rayon/issues/688 - /// is fixed, hopefully. - static BLOOM_KEY: ManuallyDrop>> = - ManuallyDrop::new(Arc::new_leaked(Default::default())); -} - -/// A struct that allows us to fast-reject deep descendant selectors avoiding -/// selector-matching. -/// -/// This is implemented using a counting bloom filter, and it's a standard -/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's -/// `SelectorFilter`. -/// -/// The constraints for Servo's style system are a bit different compared to -/// traditional style systems given Servo does a parallel breadth-first -/// traversal instead of a sequential depth-first traversal. -/// -/// This implies that we need to track a bit more state than other browsers to -/// ensure we're doing the correct thing during the traversal, and being able to -/// apply this optimization effectively. -/// -/// Concretely, we have a bloom filter instance per worker thread, and we track -/// the current DOM depth in order to find a common ancestor when it doesn't -/// match the previous element we've styled. -/// -/// This is usually a pretty fast operation (we use to be one level deeper than -/// the previous one), but in the case of work-stealing, we may needed to push -/// and pop multiple elements. -/// -/// See the `insert_parents_recovering`, where most of the magic happens. -/// -/// Regarding thread-safety, this struct is safe because: -/// -/// * We clear this after a restyle. -/// * The DOM shape and attributes (and every other thing we access here) are -/// immutable during a restyle. -/// -pub struct StyleBloom { - /// A handle to the bloom filter from the thread upon which this StyleBloom - /// was created. We use AtomicRefCell so that this is all |Send|, which allows - /// StyleBloom to live in ThreadLocalStyleContext, which is dropped from the - /// parent thread. - filter: OwningHandle>, AtomicRefMut<'static, BloomFilter>>, - - /// The stack of elements that this bloom filter contains, along with the - /// number of hashes pushed for each element. - elements: SmallVec<[PushedElement; 16]>, - - /// Stack of hashes that have been pushed onto this filter. - pushed_hashes: SmallVec<[u32; 64]>, -} - -/// The very rough benchmarks in the selectors crate show clear() -/// costing about 25 times more than remove_hash(). We use this to implement -/// clear() more efficiently when only a small number of hashes have been -/// pushed. -/// -/// One subtly to note is that remove_hash() will not touch the value -/// if the filter overflowed. However, overflow can only occur if we -/// get 255 collisions on the same hash value, and 25 < 255. -const MEMSET_CLEAR_THRESHOLD: usize = 25; - -struct PushedElement { - /// The element that was pushed. - element: SendElement, - - /// The number of hashes pushed for the element. - num_hashes: usize, -} - -impl PushedElement { - fn new(el: E, num_hashes: usize) -> Self { - PushedElement { - element: unsafe { SendElement::new(el) }, - num_hashes, - } - } -} - -/// Returns whether the attribute name is excluded from the bloom filter. -/// -/// We do this for attributes that are very common but not commonly used in -/// selectors. -#[inline] -pub fn is_attr_name_excluded_from_filter(name: &LocalName) -> bool { - return *name == local_name!("class") || *name == local_name!("id") || *name == local_name!("style") -} - -fn each_relevant_element_hash(element: E, mut f: F) -where - E: TElement, - F: FnMut(u32), -{ - f(element.local_name().get_hash()); - f(element.namespace().get_hash()); - - if let Some(id) = element.id() { - f(id.get_hash()); - } - - element.each_class(|class| f(class.get_hash())); - - element.each_attr_name(|name| { - if !is_attr_name_excluded_from_filter(name) { - f(name.get_hash()) - } - }); -} - -impl Drop for StyleBloom { - fn drop(&mut self) { - // Leave the reusable bloom filter in a zeroed state. - self.clear(); - } -} - -impl StyleBloom { - /// Create an empty `StyleBloom`. Because StyleBloom acquires the thread- - /// local filter buffer, creating multiple live StyleBloom instances at - /// the same time on the same thread will panic. - - // Forced out of line to limit stack frame sizes after extra inlining from - // https://github.com/rust-lang/rust/pull/43931 - // - // See https://github.com/servo/servo/pull/18420#issuecomment-328769322 - #[inline(never)] - pub fn new() -> Self { - let bloom_arc = BLOOM_KEY.with(|b| Arc::clone(&*b)); - let filter = - OwningHandle::new_with_fn(bloom_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut()); - debug_assert!( - filter.is_zeroed(), - "Forgot to zero the bloom filter last time" - ); - StyleBloom { - filter, - elements: Default::default(), - pushed_hashes: Default::default(), - } - } - - /// Return the bloom filter used properly by the `selectors` crate. - pub fn filter(&self) -> &BloomFilter { - &*self.filter - } - - /// Push an element to the bloom filter, knowing that it's a child of the - /// last element parent. - pub fn push(&mut self, element: E) { - if cfg!(debug_assertions) { - if self.elements.is_empty() { - assert!(element.traversal_parent().is_none()); - } - } - self.push_internal(element); - } - - /// Same as `push`, but without asserting, in order to use it from - /// `rebuild`. - fn push_internal(&mut self, element: E) { - let mut count = 0; - each_relevant_element_hash(element, |hash| { - count += 1; - self.filter.insert_hash(hash); - self.pushed_hashes.push(hash); - }); - self.elements.push(PushedElement::new(element, count)); - } - - /// Pop the last element in the bloom filter and return it. - #[inline] - fn pop(&mut self) -> Option { - let PushedElement { - element, - num_hashes, - } = self.elements.pop()?; - let popped_element = *element; - - // Verify that the pushed hashes match the ones we'd get from the element. - let mut expected_hashes = vec![]; - if cfg!(debug_assertions) { - each_relevant_element_hash(popped_element, |hash| expected_hashes.push(hash)); - } - - for _ in 0..num_hashes { - let hash = self.pushed_hashes.pop().unwrap(); - debug_assert_eq!(expected_hashes.pop().unwrap(), hash); - self.filter.remove_hash(hash); - } - - Some(popped_element) - } - - /// Returns the DOM depth of elements that can be correctly - /// matched against the bloom filter (that is, the number of - /// elements in our list). - pub fn matching_depth(&self) -> usize { - self.elements.len() - } - - /// Clears the bloom filter. - pub fn clear(&mut self) { - self.elements.clear(); - - if self.pushed_hashes.len() > MEMSET_CLEAR_THRESHOLD { - self.filter.clear(); - self.pushed_hashes.clear(); - } else { - for hash in self.pushed_hashes.drain(..) { - self.filter.remove_hash(hash); - } - debug_assert!(self.filter.is_zeroed()); - } - } - - /// Rebuilds the bloom filter up to the parent of the given element. - pub fn rebuild(&mut self, mut element: E) { - self.clear(); - - let mut parents_to_insert = SmallVec::<[E; 16]>::new(); - while let Some(parent) = element.traversal_parent() { - parents_to_insert.push(parent); - element = parent; - } - - for parent in parents_to_insert.drain(..).rev() { - self.push(parent); - } - } - - /// In debug builds, asserts that all the parents of `element` are in the - /// bloom filter. - /// - /// Goes away in release builds. - pub fn assert_complete(&self, mut element: E) { - if cfg!(debug_assertions) { - let mut checked = 0; - while let Some(parent) = element.traversal_parent() { - assert_eq!( - parent, - *(self.elements[self.elements.len() - 1 - checked].element) - ); - element = parent; - checked += 1; - } - assert_eq!(checked, self.elements.len()); - } - } - - /// Get the element that represents the chain of things inserted - /// into the filter right now. That chain is the given element - /// (if any) and its ancestors. - #[inline] - pub fn current_parent(&self) -> Option { - self.elements.last().map(|ref el| *el.element) - } - - /// Insert the parents of an element in the bloom filter, trying to recover - /// the filter if the last element inserted doesn't match. - /// - /// Gets the element depth in the dom, to make it efficient, or if not - /// provided always rebuilds the filter from scratch. - /// - /// Returns the new bloom filter depth, that the traversal code is - /// responsible to keep around if it wants to get an effective filter. - pub fn insert_parents_recovering(&mut self, element: E, element_depth: usize) { - // Easy case, we're in a different restyle, or we're empty. - if self.elements.is_empty() { - self.rebuild(element); - return; - } - - let traversal_parent = match element.traversal_parent() { - Some(parent) => parent, - None => { - // Yay, another easy case. - self.clear(); - return; - }, - }; - - if self.current_parent() == Some(traversal_parent) { - // Ta da, cache hit, we're all done. - return; - } - - if element_depth == 0 { - self.clear(); - return; - } - - // We should've early exited above. - debug_assert!( - element_depth != 0, - "We should have already cleared the bloom filter" - ); - debug_assert!(!self.elements.is_empty(), "How! We should've just rebuilt!"); - - // Now the fun begins: We have the depth of the dom and the depth of the - // last element inserted in the filter, let's try to find a common - // parent. - // - // The current depth, that is, the depth of the last element inserted in - // the bloom filter, is the number of elements _minus one_, that is: if - // there's one element, it must be the root -> depth zero. - let mut current_depth = self.elements.len() - 1; - - // If the filter represents an element too deep in the dom, we need to - // pop ancestors. - while current_depth > element_depth - 1 { - self.pop().expect("Emilio is bad at math"); - current_depth -= 1; - } - - // Now let's try to find a common parent in the bloom filter chain, - // starting with traversal_parent. - let mut common_parent = traversal_parent; - let mut common_parent_depth = element_depth - 1; - - // Let's collect the parents we are going to need to insert once we've - // found the common one. - let mut parents_to_insert = SmallVec::<[E; 16]>::new(); - - // If the bloom filter still doesn't have enough elements, the common - // parent is up in the dom. - while common_parent_depth > current_depth { - // TODO(emilio): Seems like we could insert parents here, then - // reverse the slice. - parents_to_insert.push(common_parent); - common_parent = common_parent.traversal_parent().expect("We were lied to"); - common_parent_depth -= 1; - } - - // Now the two depths are the same. - debug_assert_eq!(common_parent_depth, current_depth); - - // Happy case: The parents match, we only need to push the ancestors - // we've collected and we'll never enter in this loop. - // - // Not-so-happy case: Parent's don't match, so we need to keep going up - // until we find a common ancestor. - // - // Gecko currently models native anonymous content that conceptually - // hangs off the document (such as scrollbars) as a separate subtree - // from the document root. - // - // Thus it's possible with Gecko that we do not find any common - // ancestor. - while *(self.elements.last().unwrap().element) != common_parent { - parents_to_insert.push(common_parent); - self.pop().unwrap(); - common_parent = match common_parent.traversal_parent() { - Some(parent) => parent, - None => { - debug_assert!(self.elements.is_empty()); - if cfg!(feature = "gecko") { - break; - } else { - panic!("should have found a common ancestor"); - } - }, - } - } - - // Now the parents match, so insert the stack of elements we have been - // collecting so far. - for parent in parents_to_insert.drain(..).rev() { - self.push(parent); - } - - debug_assert_eq!(self.elements.len(), element_depth); - - // We're done! Easy. - } -} diff --git a/components/style/build.rs b/components/style/build.rs deleted file mode 100644 index eacb9b3fc99..00000000000 --- a/components/style/build.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#[macro_use] -extern crate lazy_static; - -use std::env; -use std::path::Path; -use std::process::{exit, Command}; -use walkdir::WalkDir; - -#[cfg(feature = "gecko")] -mod build_gecko; - -#[cfg(not(feature = "gecko"))] -mod build_gecko { - pub fn generate() {} -} - -lazy_static! { - pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| { - let candidates = if cfg!(windows) { - ["python.exe"] - } else { - ["python3"] - }; - for &name in &candidates { - if Command::new(name) - .arg("--version") - .output() - .ok() - .map_or(false, |out| out.status.success()) - { - return name.to_owned(); - } - } - panic!( - "Can't find python (tried {})! Try fixing PATH or setting the PYTHON3 env var", - candidates.join(", ") - ) - }); -} - -fn generate_properties(engine: &str) { - for entry in WalkDir::new("properties") { - let entry = entry.unwrap(); - match entry.path().extension().and_then(|e| e.to_str()) { - Some("mako") | Some("rs") | Some("py") | Some("zip") => { - println!("cargo:rerun-if-changed={}", entry.path().display()); - }, - _ => {}, - } - } - - let script = Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()) - .join("properties") - .join("build.py"); - - let status = Command::new(&*PYTHON) - .arg(&script) - .arg(engine) - .arg("style-crate") - .status() - .unwrap(); - if !status.success() { - exit(1) - } -} - -fn main() { - let gecko = cfg!(feature = "gecko"); - let servo = cfg!(feature = "servo"); - let engine = match (gecko, servo) { - (true, false) => "gecko", - (false, true) => "servo", - _ => panic!( - "\n\n\ - The style crate requires enabling one of its 'servo' or 'gecko' feature flags. \ - \n\n" - ), - }; - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:out_dir={}", env::var("OUT_DIR").unwrap()); - generate_properties(engine); - build_gecko::generate(); -} diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs deleted file mode 100644 index a83c5dbc6d6..00000000000 --- a/components/style/build_gecko.rs +++ /dev/null @@ -1,400 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use super::PYTHON; -use bindgen::{Builder, CodegenConfig}; -use regex::Regex; -use std::cmp; -use std::collections::HashSet; -use std::env; -use std::fs::{self, File}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use std::process::{exit, Command}; -use std::slice; -use std::sync::Mutex; -use std::time::SystemTime; -use toml; -use toml::value::Table; - -lazy_static! { - static ref OUTDIR_PATH: PathBuf = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("gecko"); -} - -const STRUCTS_FILE: &'static str = "structs.rs"; - -fn read_config(path: &PathBuf) -> Table { - println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); - update_last_modified(&path); - - let mut contents = String::new(); - File::open(path) - .expect("Failed to open config file") - .read_to_string(&mut contents) - .expect("Failed to read config file"); - match toml::from_str::(&contents) { - Ok(result) => result, - Err(e) => panic!("Failed to parse config file: {}", e), - } -} - -lazy_static! { - static ref CONFIG: Table = { - // Load Gecko's binding generator config from the source tree. - let path = mozbuild::TOPSRCDIR.join("layout/style/ServoBindings.toml"); - read_config(&path) - }; - static ref BINDGEN_FLAGS: Vec = { - // Load build-specific config overrides. - let path = mozbuild::TOPOBJDIR.join("layout/style/extra-bindgen-flags"); - println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); - fs::read_to_string(path).expect("Failed to read extra-bindgen-flags file") - .split_whitespace() - .map(std::borrow::ToOwned::to_owned) - .collect() - }; - static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap(); - static ref DISTDIR_PATH: PathBuf = mozbuild::TOPOBJDIR.join("dist"); - static ref SEARCH_PATHS: Vec = vec![ - DISTDIR_PATH.join("include"), - DISTDIR_PATH.join("include/nspr"), - ]; - static ref ADDED_PATHS: Mutex> = Mutex::new(HashSet::new()); - static ref LAST_MODIFIED: Mutex = - Mutex::new(get_modified_time(&env::current_exe().unwrap()) - .expect("Failed to get modified time of executable")); -} - -fn get_modified_time(file: &Path) -> Option { - file.metadata().and_then(|m| m.modified()).ok() -} - -fn update_last_modified(file: &Path) { - let modified = get_modified_time(file).expect("Couldn't get file modification time"); - let mut last_modified = LAST_MODIFIED.lock().unwrap(); - *last_modified = cmp::max(modified, *last_modified); -} - -fn search_include(name: &str) -> Option { - for path in SEARCH_PATHS.iter() { - let file = path.join(name); - if file.is_file() { - update_last_modified(&file); - return Some(file); - } - } - None -} - -fn add_headers_recursively(path: PathBuf, added_paths: &mut HashSet) { - if added_paths.contains(&path) { - return; - } - let mut file = File::open(&path).unwrap(); - let mut content = String::new(); - file.read_to_string(&mut content).unwrap(); - added_paths.insert(path); - // Find all includes and add them recursively - for cap in INCLUDE_RE.captures_iter(&content) { - if let Some(path) = search_include(cap.get(1).unwrap().as_str()) { - add_headers_recursively(path, added_paths); - } - } -} - -fn add_include(name: &str) -> String { - let mut added_paths = ADDED_PATHS.lock().unwrap(); - let file = match search_include(name) { - Some(file) => file, - None => panic!("Include not found: {}", name), - }; - let result = String::from(file.to_str().unwrap()); - add_headers_recursively(file, &mut *added_paths); - result -} - -trait BuilderExt { - fn get_initial_builder() -> Builder; - fn include>(self, file: T) -> Builder; -} - -impl BuilderExt for Builder { - fn get_initial_builder() -> Builder { - // Disable rust unions, because we replace some types inside of - // them. - let mut builder = Builder::default() - .size_t_is_usize(true) - .disable_untagged_union(); - - let rustfmt_path = env::var_os("RUSTFMT") - // This can be replaced with - // > .filter(|p| !p.is_empty()).map(PathBuf::from) - // once we can use 1.27+. - .and_then(|p| { - if p.is_empty() { - None - } else { - Some(PathBuf::from(p)) - } - }); - if let Some(path) = rustfmt_path { - builder = builder.with_rustfmt(path); - } - - for dir in SEARCH_PATHS.iter() { - builder = builder.clang_arg("-I").clang_arg(dir.to_str().unwrap()); - } - - builder = builder.include(add_include("mozilla-config.h")); - - if env::var("CARGO_FEATURE_GECKO_DEBUG").is_ok() { - builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1"); - } - - for item in &*BINDGEN_FLAGS { - builder = builder.clang_arg(item); - } - - builder - } - fn include>(self, file: T) -> Builder { - self.clang_arg("-include").clang_arg(file) - } -} - -struct Fixup { - pat: String, - rep: String, -} - -fn write_binding_file(builder: Builder, file: &str, fixups: &[Fixup]) { - let out_file = OUTDIR_PATH.join(file); - if let Some(modified) = get_modified_time(&out_file) { - // Don't generate the file if nothing it depends on was modified. - let last_modified = LAST_MODIFIED.lock().unwrap(); - if *last_modified <= modified { - return; - } - } - let command_line_opts = builder.command_line_flags(); - let result = builder.generate(); - let mut result = match result { - Ok(bindings) => bindings.to_string(), - Err(_) => { - panic!( - "Failed to generate bindings, flags: {:?}", - command_line_opts - ); - }, - }; - - for fixup in fixups.iter() { - result = Regex::new(&fixup.pat) - .unwrap() - .replace_all(&result, &*fixup.rep) - .into_owned() - .into(); - } - let bytes = result.into_bytes(); - File::create(&out_file) - .unwrap() - .write_all(&bytes) - .expect("Unable to write output"); -} - -struct BuilderWithConfig<'a> { - builder: Builder, - config: &'a Table, - used_keys: HashSet<&'static str>, -} -impl<'a> BuilderWithConfig<'a> { - fn new(builder: Builder, config: &'a Table) -> Self { - BuilderWithConfig { - builder, - config, - used_keys: HashSet::new(), - } - } - - fn handle_list(self, key: &'static str, func: F) -> BuilderWithConfig<'a> - where - F: FnOnce(Builder, slice::Iter<'a, toml::Value>) -> Builder, - { - let mut builder = self.builder; - let config = self.config; - let mut used_keys = self.used_keys; - if let Some(list) = config.get(key) { - used_keys.insert(key); - builder = func(builder, list.as_array().unwrap().as_slice().iter()); - } - BuilderWithConfig { - builder, - config, - used_keys, - } - } - fn handle_items(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> - where - F: FnMut(Builder, &'a toml::Value) -> Builder, - { - self.handle_list(key, |b, iter| iter.fold(b, |b, item| func(b, item))) - } - fn handle_str_items(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> - where - F: FnMut(Builder, &'a str) -> Builder, - { - self.handle_items(key, |b, item| func(b, item.as_str().unwrap())) - } - fn handle_table_items(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a> - where - F: FnMut(Builder, &'a Table) -> Builder, - { - self.handle_items(key, |b, item| func(b, item.as_table().unwrap())) - } - fn handle_common(self, fixups: &mut Vec) -> BuilderWithConfig<'a> { - self.handle_str_items("headers", |b, item| b.header(add_include(item))) - .handle_str_items("raw-lines", |b, item| b.raw_line(item)) - .handle_str_items("hide-types", |b, item| b.blocklist_type(item)) - .handle_table_items("fixups", |builder, item| { - fixups.push(Fixup { - pat: item["pat"].as_str().unwrap().into(), - rep: item["rep"].as_str().unwrap().into(), - }); - builder - }) - } - - fn get_builder(self) -> Builder { - for key in self.config.keys() { - if !self.used_keys.contains(key.as_str()) { - panic!("Unknown key: {}", key); - } - } - self.builder - } -} - -fn generate_structs() { - let builder = Builder::get_initial_builder() - .enable_cxx_namespaces() - .with_codegen_config(CodegenConfig::TYPES | CodegenConfig::VARS | CodegenConfig::FUNCTIONS); - let mut fixups = vec![]; - let builder = BuilderWithConfig::new(builder, CONFIG["structs"].as_table().unwrap()) - .handle_common(&mut fixups) - .handle_str_items("allowlist-functions", |b, item| b.allowlist_function(item)) - .handle_str_items("bitfield-enums", |b, item| b.bitfield_enum(item)) - .handle_str_items("rusty-enums", |b, item| b.rustified_enum(item)) - .handle_str_items("allowlist-vars", |b, item| b.allowlist_var(item)) - .handle_str_items("allowlist-types", |b, item| b.allowlist_type(item)) - .handle_str_items("opaque-types", |b, item| b.opaque_type(item)) - .handle_table_items("cbindgen-types", |b, item| { - let gecko = item["gecko"].as_str().unwrap(); - let servo = item["servo"].as_str().unwrap(); - b.blocklist_type(format!("mozilla::{}", gecko)) - .module_raw_line("root::mozilla", format!("pub use {} as {};", servo, gecko)) - }) - .handle_table_items("mapped-generic-types", |builder, item| { - let generic = item["generic"].as_bool().unwrap(); - let gecko = item["gecko"].as_str().unwrap(); - let servo = item["servo"].as_str().unwrap(); - let gecko_name = gecko.rsplit("::").next().unwrap(); - let gecko = gecko - .split("::") - .map(|s| format!("\\s*{}\\s*", s)) - .collect::>() - .join("::"); - - fixups.push(Fixup { - pat: format!("\\broot\\s*::\\s*{}\\b", gecko), - rep: format!("crate::gecko_bindings::structs::{}", gecko_name), - }); - builder.blocklist_type(gecko).raw_line(format!( - "pub type {0}{2} = {1}{2};", - gecko_name, - servo, - if generic { "" } else { "" } - )) - }) - .get_builder(); - write_binding_file(builder, STRUCTS_FILE, &fixups); -} - -fn setup_logging() -> bool { - struct BuildLogger { - file: Option>, - filter: String, - } - - impl log::Log for BuildLogger { - fn enabled(&self, meta: &log::Metadata) -> bool { - self.file.is_some() && meta.target().contains(&self.filter) - } - - fn log(&self, record: &log::Record) { - if !self.enabled(record.metadata()) { - return; - } - - let mut file = self.file.as_ref().unwrap().lock().unwrap(); - let _ = writeln!( - file, - "{} - {} - {} @ {}:{}", - record.level(), - record.target(), - record.args(), - record.file().unwrap_or(""), - record.line().unwrap_or(0) - ); - } - - fn flush(&self) { - if let Some(ref file) = self.file { - file.lock().unwrap().flush().unwrap(); - } - } - } - - if let Some(path) = env::var_os("STYLO_BUILD_LOG") { - log::set_max_level(log::LevelFilter::Debug); - log::set_boxed_logger(Box::new(BuildLogger { - file: fs::File::create(path).ok().map(Mutex::new), - filter: env::var("STYLO_BUILD_FILTER") - .ok() - .unwrap_or_else(|| "bindgen".to_owned()), - })) - .expect("Failed to set logger."); - - true - } else { - false - } -} - -fn generate_atoms() { - let script = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()) - .join("gecko") - .join("regen_atoms.py"); - println!("cargo:rerun-if-changed={}", script.display()); - let status = Command::new(&*PYTHON) - .arg(&script) - .arg(DISTDIR_PATH.as_os_str()) - .arg(OUTDIR_PATH.as_os_str()) - .status() - .unwrap(); - if !status.success() { - exit(1); - } -} - -pub fn generate() { - println!("cargo:rerun-if-changed=build_gecko.rs"); - fs::create_dir_all(&*OUTDIR_PATH).unwrap(); - setup_logging(); - generate_structs(); - generate_atoms(); - - for path in ADDED_PATHS.lock().unwrap().iter() { - println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); - } -} diff --git a/components/style/color/convert.rs b/components/style/color/convert.rs deleted file mode 100644 index 4fa037f9d6d..00000000000 --- a/components/style/color/convert.rs +++ /dev/null @@ -1,888 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Color conversion algorithms. -//! -//! Algorithms, matrices and constants are from the [color-4] specification, -//! unless otherwise specified: -//! -//! https://drafts.csswg.org/css-color-4/#color-conversion-code -//! -//! NOTE: Matrices has to be transposed from the examples in the spec for use -//! with the `euclid` library. - -use crate::color::ColorComponents; -use std::f32::consts::PI; - -type Transform = euclid::default::Transform3D; -type Vector = euclid::default::Vector3D; - -const RAD_PER_DEG: f32 = PI / 180.0; -const DEG_PER_RAD: f32 = 180.0 / PI; - -/// Normalize hue into [0, 360). -#[inline] -fn normalize_hue(hue: f32) -> f32 { - hue - 360. * (hue / 360.).floor() -} - -/// Calculate the hue from RGB components and return it along with the min and -/// max RGB values. -#[inline] -fn rgb_to_hue_min_max(red: f32, green: f32, blue: f32) -> (f32, f32, f32) { - let max = red.max(green).max(blue); - let min = red.min(green).min(blue); - - let delta = max - min; - - let hue = if delta != 0.0 { - 60.0 * if max == red { - (green - blue) / delta + if green < blue { 6.0 } else { 0.0 } - } else if max == green { - (blue - red) / delta + 2.0 - } else { - (red - green) / delta + 4.0 - } - } else { - f32::NAN - }; - - (hue, min, max) -} - -/// Convert a hue value into red, green, blue components. -#[inline] -fn hue_to_rgb(t1: f32, t2: f32, hue: f32) -> f32 { - let hue = normalize_hue(hue); - - if hue * 6.0 < 360.0 { - t1 + (t2 - t1) * hue / 60.0 - } else if hue * 2.0 < 360.0 { - t2 - } else if hue * 3.0 < 720.0 { - t1 + (t2 - t1) * (240.0 - hue) / 60.0 - } else { - t1 - } -} - -/// Convert from HSL notation to RGB notation. -/// https://drafts.csswg.org/css-color-4/#hsl-to-rgb -#[inline] -pub fn hsl_to_rgb(from: &ColorComponents) -> ColorComponents { - let ColorComponents(hue, saturation, lightness) = *from; - - let t2 = if lightness <= 0.5 { - lightness * (saturation + 1.0) - } else { - lightness + saturation - lightness * saturation - }; - let t1 = lightness * 2.0 - t2; - - ColorComponents( - hue_to_rgb(t1, t2, hue + 120.0), - hue_to_rgb(t1, t2, hue), - hue_to_rgb(t1, t2, hue - 120.0), - ) -} - -/// Convert from RGB notation to HSL notation. -/// https://drafts.csswg.org/css-color-4/#rgb-to-hsl -pub fn rgb_to_hsl(from: &ColorComponents) -> ColorComponents { - let ColorComponents(red, green, blue) = *from; - - let (hue, min, max) = rgb_to_hue_min_max(red, green, blue); - - let lightness = (min + max) / 2.0; - let delta = max - min; - - let saturation = if delta != 0.0 { - if lightness == 0.0 || lightness == 1.0 { - 0.0 - } else { - (max - lightness) / lightness.min(1.0 - lightness) - } - } else { - 0.0 - }; - - ColorComponents(hue, saturation, lightness) -} - -/// Convert from HWB notation to RGB notation. -/// https://drafts.csswg.org/css-color-4/#hwb-to-rgb -#[inline] -pub fn hwb_to_rgb(from: &ColorComponents) -> ColorComponents { - let ColorComponents(hue, whiteness, blackness) = *from; - - if whiteness + blackness > 1.0 { - let gray = whiteness / (whiteness + blackness); - return ColorComponents(gray, gray, gray); - } - - let x = 1.0 - whiteness - blackness; - hsl_to_rgb(&ColorComponents(hue, 1.0, 0.5)).map(|v| v * x + whiteness) -} - -/// Convert from RGB notation to HWB notation. -/// https://drafts.csswg.org/css-color-4/#rgb-to-hwb -#[inline] -pub fn rgb_to_hwb(from: &ColorComponents) -> ColorComponents { - let ColorComponents(red, green, blue) = *from; - - let (hue, min, max) = rgb_to_hue_min_max(red, green, blue); - - let whiteness = min; - let blackness = 1.0 - max; - - ColorComponents(hue, whiteness, blackness) -} - -/// Convert from Lab to Lch. This calculation works for both Lab and Olab. -/// -#[inline] -pub fn lab_to_lch(from: &ColorComponents) -> ColorComponents { - let ColorComponents(lightness, a, b) = *from; - - let hue = normalize_hue(b.atan2(a) * 180.0 / PI); - let chroma = (a.powf(2.0) + b.powf(2.0)).sqrt(); - - ColorComponents(lightness, chroma, hue) -} - -/// Convert from Lch to Lab. This calculation works for both Lch and Oklch. -/// -#[inline] -pub fn lch_to_lab(from: &ColorComponents) -> ColorComponents { - let ColorComponents(lightness, chroma, hue) = *from; - - let a = chroma * (hue * PI / 180.0).cos(); - let b = chroma * (hue * PI / 180.0).sin(); - - ColorComponents(lightness, a, b) -} - -#[inline] -fn transform(from: &ColorComponents, mat: &Transform) -> ColorComponents { - let result = mat.transform_vector3d(Vector::new(from.0, from.1, from.2)); - ColorComponents(result.x, result.y, result.z) -} - -fn xyz_d65_to_xyz_d50(from: &ColorComponents) -> ColorComponents { - #[rustfmt::skip] - const MAT: Transform = Transform::new( - 1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0.0, - 0.022946793341019088, 0.990434484573249, 0.015055144896577895, 0.0, - -0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - transform(from, &MAT) -} - -fn xyz_d50_to_xyz_d65(from: &ColorComponents) -> ColorComponents { - #[rustfmt::skip] - const MAT: Transform = Transform::new( - 0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0.0, - -0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0.0, - 0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - transform(from, &MAT) -} - -/// A reference white that is used during color conversion. -pub enum WhitePoint { - /// D50 white reference. - D50, - /// D65 white reference. - D65, -} - -fn convert_white_point(from: WhitePoint, to: WhitePoint, components: &mut ColorComponents) { - match (from, to) { - (WhitePoint::D50, WhitePoint::D65) => *components = xyz_d50_to_xyz_d65(components), - (WhitePoint::D65, WhitePoint::D50) => *components = xyz_d65_to_xyz_d50(components), - - _ => {}, - } -} - -/// A trait that allows conversion of color spaces to and from XYZ coordinate -/// space with a specified white point. -/// -/// Allows following the specified method of converting between color spaces: -/// - Convert to values to sRGB linear light. -/// - Convert to XYZ coordinate space. -/// - Adjust white point to target white point. -/// - Convert to sRGB linear light in target color space. -/// - Convert to sRGB gamma encoded in target color space. -/// -/// https://drafts.csswg.org/css-color-4/#color-conversion -pub trait ColorSpaceConversion { - /// The white point that the implementer is represented in. - const WHITE_POINT: WhitePoint; - - /// Convert the components from sRGB gamma encoded values to sRGB linear - /// light values. - fn to_linear_light(from: &ColorComponents) -> ColorComponents; - - /// Convert the components from sRGB linear light values to XYZ coordinate - /// space. - fn to_xyz(from: &ColorComponents) -> ColorComponents; - - /// Convert the components from XYZ coordinate space to sRGB linear light - /// values. - fn from_xyz(from: &ColorComponents) -> ColorComponents; - - /// Convert the components from sRGB linear light values to sRGB gamma - /// encoded values. - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents; -} - -/// Convert the color components from the specified color space to XYZ and -/// return the components and the white point they are in. -pub fn to_xyz(from: &ColorComponents) -> (ColorComponents, WhitePoint) { - // Convert the color components where in-gamut values are in the range - // [0 - 1] to linear light (un-companded) form. - let result = From::to_linear_light(from); - - // Convert the color components from the source color space to XYZ. - (From::to_xyz(&result), From::WHITE_POINT) -} - -/// Convert the color components from XYZ at the given white point to the -/// specified color space. -pub fn from_xyz( - from: &ColorComponents, - white_point: WhitePoint, -) -> ColorComponents { - let mut xyz = from.clone(); - - // Convert the white point if needed. - convert_white_point(white_point, To::WHITE_POINT, &mut xyz); - - // Convert the color from XYZ to the target color space. - let result = To::from_xyz(&xyz); - - // Convert the color components of linear-light values in the range - // [0 - 1] to a gamma corrected form. - To::to_gamma_encoded(&result) -} - -/// The sRGB color space. -/// https://drafts.csswg.org/css-color-4/#predefined-sRGB -pub struct Srgb; - -impl Srgb { - #[rustfmt::skip] - const TO_XYZ: Transform = Transform::new( - 0.4123907992659595, 0.21263900587151036, 0.01933081871559185, 0.0, - 0.35758433938387796, 0.7151686787677559, 0.11919477979462599, 0.0, - 0.1804807884018343, 0.07219231536073371, 0.9505321522496606, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const FROM_XYZ: Transform = Transform::new( - 3.2409699419045213, -0.9692436362808798, 0.05563007969699361, 0.0, - -1.5373831775700935, 1.8759675015077206, -0.20397695888897657, 0.0, - -0.4986107602930033, 0.04155505740717561, 1.0569715142428786, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for Srgb { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone().map(|value| { - let abs = value.abs(); - - if abs < 0.04045 { - value / 12.92 - } else { - value.signum() * ((abs + 0.055) / 1.055).powf(2.4) - } - }) - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::FROM_XYZ) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - from.clone().map(|value| { - let abs = value.abs(); - - if abs > 0.0031308 { - value.signum() * (1.055 * abs.powf(1.0 / 2.4) - 0.055) - } else { - 12.92 * value - } - }) - } -} - -/// Color specified with hue, saturation and lightness components. -pub struct Hsl; - -impl ColorSpaceConversion for Hsl { - const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - Srgb::to_linear_light(&hsl_to_rgb(from)) - } - - #[inline] - fn to_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::to_xyz(from) - } - - #[inline] - fn from_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::from_xyz(from) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - rgb_to_hsl(&Srgb::to_gamma_encoded(from)) - } -} - -/// Color specified with hue, whiteness and blackness components. -pub struct Hwb; - -impl ColorSpaceConversion for Hwb { - const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - Srgb::to_linear_light(&hwb_to_rgb(from)) - } - - #[inline] - fn to_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::to_xyz(from) - } - - #[inline] - fn from_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::from_xyz(from) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - rgb_to_hwb(&Srgb::to_gamma_encoded(from)) - } -} - -/// The same as sRGB color space, except the transfer function is linear light. -/// https://drafts.csswg.org/css-color-4/#predefined-sRGB-linear -pub struct SrgbLinear; - -impl ColorSpaceConversion for SrgbLinear { - const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - // Already in linear light form. - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::to_xyz(from) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - Srgb::from_xyz(from) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - // Stay in linear light form. - from.clone() - } -} - -/// The Display-P3 color space. -/// https://drafts.csswg.org/css-color-4/#predefined-display-p3 -pub struct DisplayP3; - -impl DisplayP3 { - #[rustfmt::skip] - const TO_XYZ: Transform = Transform::new( - 0.48657094864821626, 0.22897456406974884, 0.0, 0.0, - 0.26566769316909294, 0.6917385218365062, 0.045113381858902575, 0.0, - 0.1982172852343625, 0.079286914093745, 1.0439443689009757, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const FROM_XYZ: Transform = Transform::new( - 2.4934969119414245, -0.829488969561575, 0.035845830243784335, 0.0, - -0.9313836179191236, 1.7626640603183468, -0.07617238926804171, 0.0, - -0.40271078445071684, 0.02362468584194359, 0.9568845240076873, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for DisplayP3 { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - Srgb::to_linear_light(from) - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::FROM_XYZ) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - Srgb::to_gamma_encoded(from) - } -} - -/// The a98-rgb color space. -/// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb -pub struct A98Rgb; - -impl A98Rgb { - #[rustfmt::skip] - const TO_XYZ: Transform = Transform::new( - 0.5766690429101308, 0.29734497525053616, 0.027031361386412378, 0.0, - 0.18555823790654627, 0.627363566255466, 0.07068885253582714, 0.0, - 0.18822864623499472, 0.07529145849399789, 0.9913375368376389, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const FROM_XYZ: Transform = Transform::new( - 2.041587903810746, -0.9692436362808798, 0.013444280632031024, 0.0, - -0.5650069742788596, 1.8759675015077206, -0.11836239223101824, 0.0, - -0.3447313507783295, 0.04155505740717561, 1.0151749943912054, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for A98Rgb { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone().map(|v| v.signum() * v.abs().powf(2.19921875)) - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::FROM_XYZ) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - from.clone() - .map(|v| v.signum() * v.abs().powf(0.4547069271758437)) - } -} - -/// The ProPhoto RGB color space. -/// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb -pub struct ProphotoRgb; - -impl ProphotoRgb { - #[rustfmt::skip] - const TO_XYZ: Transform = Transform::new( - 0.7977604896723027, 0.2880711282292934, 0.0, 0.0, - 0.13518583717574031, 0.7118432178101014, 0.0, 0.0, - 0.0313493495815248, 0.00008565396060525902, 0.8251046025104601, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const FROM_XYZ: Transform = Transform::new( - 1.3457989731028281, -0.5446224939028347, 0.0, 0.0, - -0.25558010007997534, 1.5082327413132781, 0.0, 0.0, - -0.05110628506753401, 0.02053603239147973, 1.2119675456389454, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for ProphotoRgb { - const WHITE_POINT: WhitePoint = WhitePoint::D50; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone().map(|value| { - const ET2: f32 = 16.0 / 512.0; - - let abs = value.abs(); - - if abs <= ET2 { - value / 16.0 - } else { - value.signum() * abs.powf(1.8) - } - }) - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::FROM_XYZ) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - const ET: f32 = 1.0 / 512.0; - - from.clone().map(|v| { - let abs = v.abs(); - if abs >= ET { - v.signum() * abs.powf(1.0 / 1.8) - } else { - 16.0 * v - } - }) - } -} - -/// The Rec.2020 color space. -/// https://drafts.csswg.org/css-color-4/#predefined-rec2020 -pub struct Rec2020; - -impl Rec2020 { - const ALPHA: f32 = 1.09929682680944; - const BETA: f32 = 0.018053968510807; - - #[rustfmt::skip] - const TO_XYZ: Transform = Transform::new( - 0.6369580483012913, 0.26270021201126703, 0.0, 0.0, - 0.14461690358620838, 0.677998071518871, 0.028072693049087508, 0.0, - 0.16888097516417205, 0.059301716469861945, 1.0609850577107909, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const FROM_XYZ: Transform = Transform::new( - 1.7166511879712676, -0.666684351832489, 0.017639857445310915, 0.0, - -0.3556707837763924, 1.616481236634939, -0.042770613257808655, 0.0, - -0.2533662813736598, 0.01576854581391113, 0.942103121235474, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for Rec2020 { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone().map(|value| { - let abs = value.abs(); - - if abs < Self::BETA * 4.5 { - value / 4.5 - } else { - value.signum() * ((abs + Self::ALPHA - 1.0) / Self::ALPHA).powf(1.0 / 0.45) - } - }) - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - transform(from, &Self::FROM_XYZ) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - from.clone().map(|v| { - let abs = v.abs(); - - if abs > Self::BETA { - v.signum() * (Self::ALPHA * abs.powf(0.45) - (Self::ALPHA - 1.0)) - } else { - 4.5 * v - } - }) - } -} - -/// A color in the XYZ coordinate space with a D50 white reference. -/// https://drafts.csswg.org/css-color-4/#predefined-xyz -pub struct XyzD50; - -impl ColorSpaceConversion for XyzD50 { - const WHITE_POINT: WhitePoint = WhitePoint::D50; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - from.clone() - } -} - -/// A color in the XYZ coordinate space with a D65 white reference. -/// https://drafts.csswg.org/css-color-4/#predefined-xyz -pub struct XyzD65; - -impl ColorSpaceConversion for XyzD65 { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - from.clone() - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - from.clone() - } -} - -/// The Lab color space. -/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch -pub struct Lab; - -impl Lab { - const KAPPA: f32 = 24389.0 / 27.0; - const EPSILON: f32 = 216.0 / 24389.0; - const WHITE: ColorComponents = ColorComponents(0.96422, 1.0, 0.82521); -} - -impl ColorSpaceConversion for Lab { - const WHITE_POINT: WhitePoint = WhitePoint::D50; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } - - /// Convert a CIELAB color to XYZ as specified in [1] and [2]. - /// - /// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined - /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code - fn to_xyz(from: &ColorComponents) -> ColorComponents { - let f1 = (from.0 + 16.0) / 116.0; - let f0 = (from.1 / 500.0) + f1; - let f2 = f1 - from.2 / 200.0; - - let x = if f0.powf(3.0) > Self::EPSILON { - f0.powf(3.) - } else { - (116.0 * f0 - 16.0) / Self::KAPPA - }; - let y = if from.0 > Self::KAPPA * Self::EPSILON { - ((from.0 + 16.0) / 116.0).powf(3.0) - } else { - from.0 / Self::KAPPA - }; - let z = if f2.powf(3.0) > Self::EPSILON { - f2.powf(3.0) - } else { - (116.0 * f2 - 16.0) / Self::KAPPA - }; - - ColorComponents(x * Self::WHITE.0, y * Self::WHITE.1, z * Self::WHITE.2) - } - - /// Convert an XYZ colour to LAB as specified in [1] and [2]. - /// - /// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab - /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code - fn from_xyz(from: &ColorComponents) -> ColorComponents { - macro_rules! compute_f { - ($value:expr) => {{ - if $value > Self::EPSILON { - $value.cbrt() - } else { - (Self::KAPPA * $value + 16.0) / 116.0 - } - }}; - } - - // 4. Convert D50-adapted XYZ to Lab. - let f = [ - compute_f!(from.0 / Self::WHITE.0), - compute_f!(from.1 / Self::WHITE.1), - compute_f!(from.2 / Self::WHITE.2), - ]; - - let lightness = 116.0 * f[1] - 16.0; - let a = 500.0 * (f[0] - f[1]); - let b = 200.0 * (f[1] - f[2]); - - ColorComponents(lightness, a, b) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } -} - -/// The Lch color space. -/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch -pub struct Lch; - -impl ColorSpaceConversion for Lch { - const WHITE_POINT: WhitePoint = Lab::WHITE_POINT; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - // Convert LCH to Lab first. - let hue = from.2 * RAD_PER_DEG; - let a = from.1 * hue.cos(); - let b = from.1 * hue.sin(); - - let lab = ColorComponents(from.0, a, b); - - // Then convert the Lab to XYZ. - Lab::to_xyz(&lab) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - // First convert the XYZ to LAB. - let ColorComponents(lightness, a, b) = Lab::from_xyz(&from); - - // Then conver the Lab to LCH. - let hue = b.atan2(a) * DEG_PER_RAD; - let chroma = (a * a + b * b).sqrt(); - - ColorComponents(lightness, chroma, hue) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } -} - -/// The Oklab color space. -/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch -pub struct Oklab; - -impl Oklab { - #[rustfmt::skip] - const XYZ_TO_LMS: Transform = Transform::new( - 0.8190224432164319, 0.0329836671980271, 0.048177199566046255, 0.0, - 0.3619062562801221, 0.9292868468965546, 0.26423952494422764, 0.0, - -0.12887378261216414, 0.03614466816999844, 0.6335478258136937, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const LMS_TO_OKLAB: Transform = Transform::new( - 0.2104542553, 1.9779984951, 0.0259040371, 0.0, - 0.7936177850, -2.4285922050, 0.7827717662, 0.0, - -0.0040720468, 0.4505937099, -0.8086757660, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const LMS_TO_XYZ: Transform = Transform::new( - 1.2268798733741557, -0.04057576262431372, -0.07637294974672142, 0.0, - -0.5578149965554813, 1.1122868293970594, -0.4214933239627914, 0.0, - 0.28139105017721583, -0.07171106666151701, 1.5869240244272418, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); - - #[rustfmt::skip] - const OKLAB_TO_LMS: Transform = Transform::new( - 0.99999999845051981432, 1.0000000088817607767, 1.0000000546724109177, 0.0, - 0.39633779217376785678, -0.1055613423236563494, -0.089484182094965759684, 0.0, - 0.21580375806075880339, -0.063854174771705903402, -1.2914855378640917399, 0.0, - 0.0, 0.0, 0.0, 1.0, - ); -} - -impl ColorSpaceConversion for Oklab { - const WHITE_POINT: WhitePoint = WhitePoint::D65; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - let lms = transform(&from, &Self::OKLAB_TO_LMS); - let lms = lms.map(|v| v.powf(3.0)); - transform(&lms, &Self::LMS_TO_XYZ) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - let lms = transform(&from, &Self::XYZ_TO_LMS); - let lms = lms.map(|v| v.cbrt()); - transform(&lms, &Self::LMS_TO_OKLAB) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } -} - -/// The Oklch color space. -/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch -pub struct Oklch; - -impl ColorSpaceConversion for Oklch { - const WHITE_POINT: WhitePoint = Oklab::WHITE_POINT; - - fn to_linear_light(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } - - fn to_xyz(from: &ColorComponents) -> ColorComponents { - // First convert OkLCH to Oklab. - let hue = from.2 * RAD_PER_DEG; - let a = from.1 * hue.cos(); - let b = from.1 * hue.sin(); - let oklab = ColorComponents(from.0, a, b); - - // Then convert Oklab to XYZ. - Oklab::to_xyz(&oklab) - } - - fn from_xyz(from: &ColorComponents) -> ColorComponents { - // First convert XYZ to Oklab. - let ColorComponents(lightness, a, b) = Oklab::from_xyz(&from); - - // Then convert Oklab to OkLCH. - let hue = b.atan2(a) * DEG_PER_RAD; - let chroma = (a * a + b * b).sqrt(); - - ColorComponents(lightness, chroma, hue) - } - - fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents { - // No need for conversion. - from.clone() - } -} diff --git a/components/style/color/mix.rs b/components/style/color/mix.rs deleted file mode 100644 index 455d0252659..00000000000 --- a/components/style/color/mix.rs +++ /dev/null @@ -1,475 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Color mixing/interpolation. - -use super::{AbsoluteColor, ColorComponents, ColorFlags, ColorSpace}; -use crate::parser::{Parse, ParserContext}; -use cssparser::Parser; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ToCss}; - -/// A hue-interpolation-method as defined in [1]. -/// -/// [1]: https://drafts.csswg.org/css-color-4/#typedef-hue-interpolation-method -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - ToAnimatedValue, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -#[repr(u8)] -pub enum HueInterpolationMethod { - /// https://drafts.csswg.org/css-color-4/#shorter - Shorter, - /// https://drafts.csswg.org/css-color-4/#longer - Longer, - /// https://drafts.csswg.org/css-color-4/#increasing - Increasing, - /// https://drafts.csswg.org/css-color-4/#decreasing - Decreasing, - /// https://drafts.csswg.org/css-color-4/#specified - Specified, -} - -/// https://drafts.csswg.org/css-color-4/#color-interpolation-method -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - PartialEq, - ToShmem, - ToAnimatedValue, - ToComputedValue, - ToResolvedValue, -)] -#[repr(C)] -pub struct ColorInterpolationMethod { - /// The color-space the interpolation should be done in. - pub space: ColorSpace, - /// The hue interpolation method. - pub hue: HueInterpolationMethod, -} - -impl ColorInterpolationMethod { - /// Returns the srgb interpolation method. - pub const fn srgb() -> Self { - Self { - space: ColorSpace::Srgb, - hue: HueInterpolationMethod::Shorter, - } - } - - /// Return the oklab interpolation method used for default color - /// interpolcation. - pub const fn oklab() -> Self { - Self { - space: ColorSpace::Oklab, - hue: HueInterpolationMethod::Shorter, - } - } - - /// Decides the best method for interpolating between the given colors. - /// https://drafts.csswg.org/css-color-4/#interpolation-space - pub fn best_interpolation_between(left: &AbsoluteColor, right: &AbsoluteColor) -> Self { - // The preferred color space to use for interpolating colors is Oklab. - // However, if either of the colors are in legacy rgb(), hsl() or hwb(), - // then interpolation is done in sRGB. - if !left.is_legacy_color() || !right.is_legacy_color() { - Self::oklab() - } else { - Self::srgb() - } - } -} - -impl Parse for ColorInterpolationMethod { - fn parse<'i, 't>( - _: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - input.expect_ident_matching("in")?; - let space = ColorSpace::parse(input)?; - // https://drafts.csswg.org/css-color-4/#hue-interpolation - // Unless otherwise specified, if no specific hue interpolation - // algorithm is selected by the host syntax, the default is shorter. - let hue = if space.is_polar() { - input - .try_parse(|input| -> Result<_, ParseError<'i>> { - let hue = HueInterpolationMethod::parse(input)?; - input.expect_ident_matching("hue")?; - Ok(hue) - }) - .unwrap_or(HueInterpolationMethod::Shorter) - } else { - HueInterpolationMethod::Shorter - }; - Ok(Self { space, hue }) - } -} - -impl ToCss for ColorInterpolationMethod { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("in ")?; - self.space.to_css(dest)?; - if self.hue != HueInterpolationMethod::Shorter { - dest.write_char(' ')?; - self.hue.to_css(dest)?; - dest.write_str(" hue")?; - } - Ok(()) - } -} - -/// Mix two colors into one. -pub fn mix( - interpolation: ColorInterpolationMethod, - left_color: &AbsoluteColor, - mut left_weight: f32, - right_color: &AbsoluteColor, - mut right_weight: f32, - normalize_weights: bool, -) -> AbsoluteColor { - // https://drafts.csswg.org/css-color-5/#color-mix-percent-norm - let mut alpha_multiplier = 1.0; - if normalize_weights { - let sum = left_weight + right_weight; - if sum != 1.0 { - let scale = 1.0 / sum; - left_weight *= scale; - right_weight *= scale; - if sum < 1.0 { - alpha_multiplier = sum; - } - } - } - - mix_in( - interpolation.space, - left_color, - left_weight, - right_color, - right_weight, - interpolation.hue, - alpha_multiplier, - ) -} - -/// What the outcome of each component should be in a mix result. -#[derive(Clone, Copy)] -#[repr(u8)] -enum ComponentMixOutcome { - /// Mix the left and right sides to give the result. - Mix, - /// Carry the left side forward to the result. - UseLeft, - /// Carry the right side forward to the result. - UseRight, - /// The resulting component should also be none. - None, -} - -impl ComponentMixOutcome { - fn from_colors( - left: &AbsoluteColor, - right: &AbsoluteColor, - flags_to_check: ColorFlags, - ) -> Self { - match ( - left.flags.contains(flags_to_check), - right.flags.contains(flags_to_check), - ) { - (true, true) => Self::None, - (true, false) => Self::UseRight, - (false, true) => Self::UseLeft, - (false, false) => Self::Mix, - } - } -} - -fn mix_in( - color_space: ColorSpace, - left_color: &AbsoluteColor, - left_weight: f32, - right_color: &AbsoluteColor, - right_weight: f32, - hue_interpolation: HueInterpolationMethod, - alpha_multiplier: f32, -) -> AbsoluteColor { - let outcomes = [ - ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C1_IS_NONE), - ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C2_IS_NONE), - ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::C3_IS_NONE), - ComponentMixOutcome::from_colors(left_color, right_color, ColorFlags::ALPHA_IS_NONE), - ]; - - // Convert both colors into the interpolation color space. - let left = left_color.to_color_space(color_space); - let left = left.raw_components(); - - let right = right_color.to_color_space(color_space); - let right = right.raw_components(); - - let (result, result_flags) = interpolate_premultiplied( - &left, - left_weight, - &right, - right_weight, - color_space.hue_index(), - hue_interpolation, - &outcomes, - ); - - let alpha = if alpha_multiplier != 1.0 { - result[3] * alpha_multiplier - } else { - result[3] - }; - - // FIXME: In rare cases we end up with 0.999995 in the alpha channel, - // so we reduce the precision to avoid serializing to - // rgba(?, ?, ?, 1). This is not ideal, so we should look into - // ways to avoid it. Maybe pre-multiply all color components and - // then divide after calculations? - let alpha = (alpha * 1000.0).round() / 1000.0; - - let mut result = AbsoluteColor::new( - color_space, - ColorComponents(result[0], result[1], result[2]), - alpha, - ); - - result.flags = result_flags; - // If both sides are legacy RGB, then the result stays in legacy RGB. - if !left_color.is_legacy_color() || !right_color.is_legacy_color() { - result.flags.insert(ColorFlags::AS_COLOR_FUNCTION); - } - - result -} - -fn interpolate_premultiplied_component( - left: f32, - left_weight: f32, - left_alpha: f32, - right: f32, - right_weight: f32, - right_alpha: f32, -) -> f32 { - left * left_weight * left_alpha + right * right_weight * right_alpha -} - -// Normalize hue into [0, 360) -#[inline] -fn normalize_hue(v: f32) -> f32 { - v - 360. * (v / 360.).floor() -} - -fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolationMethod) { - // Adjust the hue angle as per - // https://drafts.csswg.org/css-color/#hue-interpolation. - // - // If both hue angles are NAN, they should be set to 0. Otherwise, if a - // single hue angle is NAN, it should use the other hue angle. - if left.is_nan() { - if right.is_nan() { - *left = 0.; - *right = 0.; - } else { - *left = *right; - } - } else if right.is_nan() { - *right = *left; - } - - if hue_interpolation == HueInterpolationMethod::Specified { - // Angles are not adjusted. They are interpolated like any other - // component. - return; - } - - *left = normalize_hue(*left); - *right = normalize_hue(*right); - - match hue_interpolation { - // https://drafts.csswg.org/css-color/#shorter - HueInterpolationMethod::Shorter => { - let delta = *right - *left; - - if delta > 180. { - *left += 360.; - } else if delta < -180. { - *right += 360.; - } - }, - // https://drafts.csswg.org/css-color/#longer - HueInterpolationMethod::Longer => { - let delta = *right - *left; - if 0. < delta && delta < 180. { - *left += 360.; - } else if -180. < delta && delta < 0. { - *right += 360.; - } - }, - // https://drafts.csswg.org/css-color/#increasing - HueInterpolationMethod::Increasing => { - if *right < *left { - *right += 360.; - } - }, - // https://drafts.csswg.org/css-color/#decreasing - HueInterpolationMethod::Decreasing => { - if *left < *right { - *left += 360.; - } - }, - HueInterpolationMethod::Specified => unreachable!("Handled above"), - } -} - -fn interpolate_hue( - mut left: f32, - left_weight: f32, - mut right: f32, - right_weight: f32, - hue_interpolation: HueInterpolationMethod, -) -> f32 { - adjust_hue(&mut left, &mut right, hue_interpolation); - left * left_weight + right * right_weight -} - -struct InterpolatedAlpha { - /// The adjusted left alpha value. - left: f32, - /// The adjusted right alpha value. - right: f32, - /// The interpolated alpha value. - interpolated: f32, - /// Whether the alpha component should be `none`. - is_none: bool, -} - -fn interpolate_alpha( - left: f32, - left_weight: f32, - right: f32, - right_weight: f32, - outcome: ComponentMixOutcome, -) -> InterpolatedAlpha { - // - let mut result = match outcome { - ComponentMixOutcome::Mix => { - let interpolated = left * left_weight + right * right_weight; - InterpolatedAlpha { - left, - right, - interpolated, - is_none: false, - } - }, - ComponentMixOutcome::UseLeft => InterpolatedAlpha { - left, - right: left, - interpolated: left, - is_none: false, - }, - ComponentMixOutcome::UseRight => InterpolatedAlpha { - left: right, - right, - interpolated: right, - is_none: false, - }, - ComponentMixOutcome::None => InterpolatedAlpha { - left: 1.0, - right: 1.0, - interpolated: 0.0, - is_none: true, - }, - }; - - // Clip all alpha values to [0.0..1.0]. - result.left = result.left.clamp(0.0, 1.0); - result.right = result.right.clamp(0.0, 1.0); - result.interpolated = result.interpolated.clamp(0.0, 1.0); - - result -} - -fn interpolate_premultiplied( - left: &[f32; 4], - left_weight: f32, - right: &[f32; 4], - right_weight: f32, - hue_index: Option, - hue_interpolation: HueInterpolationMethod, - outcomes: &[ComponentMixOutcome; 4], -) -> ([f32; 4], ColorFlags) { - let alpha = interpolate_alpha(left[3], left_weight, right[3], right_weight, outcomes[3]); - let mut flags = if alpha.is_none { - ColorFlags::ALPHA_IS_NONE - } else { - ColorFlags::empty() - }; - - let mut result = [0.; 4]; - - for i in 0..3 { - match outcomes[i] { - ComponentMixOutcome::Mix => { - let is_hue = hue_index == Some(i); - result[i] = if is_hue { - normalize_hue(interpolate_hue( - left[i], - left_weight, - right[i], - right_weight, - hue_interpolation, - )) - } else { - let interpolated = interpolate_premultiplied_component( - left[i], - left_weight, - alpha.left, - right[i], - right_weight, - alpha.right, - ); - - if alpha.interpolated == 0.0 { - interpolated - } else { - interpolated / alpha.interpolated - } - }; - }, - ComponentMixOutcome::UseLeft => result[i] = left[i], - ComponentMixOutcome::UseRight => result[i] = right[i], - ComponentMixOutcome::None => { - result[i] = 0.0; - match i { - 0 => flags.insert(ColorFlags::C1_IS_NONE), - 1 => flags.insert(ColorFlags::C2_IS_NONE), - 2 => flags.insert(ColorFlags::C3_IS_NONE), - _ => unreachable!(), - } - }, - } - } - result[3] = alpha.interpolated; - - (result, flags) -} diff --git a/components/style/color/mod.rs b/components/style/color/mod.rs deleted file mode 100644 index f5a36aba2e8..00000000000 --- a/components/style/color/mod.rs +++ /dev/null @@ -1,465 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Color support functions. - -/// cbindgen:ignore -pub mod convert; -pub mod mix; - -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; - -/// The 3 components that make up a color. (Does not include the alpha component) -#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] -#[repr(C)] -pub struct ColorComponents(pub f32, pub f32, pub f32); - -impl ColorComponents { - /// Apply a function to each of the 3 components of the color. - pub fn map(self, f: impl Fn(f32) -> f32) -> Self { - Self(f(self.0), f(self.1), f(self.2)) - } -} - -/// A color space representation in the CSS specification. -/// -/// https://drafts.csswg.org/css-color-4/#typedef-color-space -#[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - ToAnimatedValue, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] -#[repr(u8)] -pub enum ColorSpace { - /// A color specified in the sRGB color space with either the rgb/rgba(..) - /// functions or the newer color(srgb ..) function. If the color(..) - /// function is used, the AS_COLOR_FUNCTION flag will be set. Examples: - /// "color(srgb 0.691 0.139 0.259)", "rgb(176, 35, 66)" - Srgb = 0, - /// A color specified in the Hsl notation in the sRGB color space, e.g. - /// "hsl(289.18 93.136% 65.531%)" - /// https://drafts.csswg.org/css-color-4/#the-hsl-notation - Hsl, - /// A color specified in the Hwb notation in the sRGB color space, e.g. - /// "hwb(740deg 20% 30%)" - /// https://drafts.csswg.org/css-color-4/#the-hwb-notation - Hwb, - /// A color specified in the Lab color format, e.g. - /// "lab(29.2345% 39.3825 20.0664)". - /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors - Lab, - /// A color specified in the Lch color format, e.g. - /// "lch(29.2345% 44.2 27)". - /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors - Lch, - /// A color specified in the Oklab color format, e.g. - /// "oklab(40.101% 0.1147 0.0453)". - /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors - Oklab, - /// A color specified in the Oklch color format, e.g. - /// "oklch(40.101% 0.12332 21.555)". - /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors - Oklch, - /// A color specified with the color(..) function and the "srgb-linear" - /// color space, e.g. "color(srgb-linear 0.435 0.017 0.055)". - SrgbLinear, - /// A color specified with the color(..) function and the "display-p3" - /// color space, e.g. "color(display-p3 0.84 0.19 0.72)". - DisplayP3, - /// A color specified with the color(..) function and the "a98-rgb" color - /// space, e.g. "color(a98-rgb 0.44091 0.49971 0.37408)". - A98Rgb, - /// A color specified with the color(..) function and the "prophoto-rgb" - /// color space, e.g. "color(prophoto-rgb 0.36589 0.41717 0.31333)". - ProphotoRgb, - /// A color specified with the color(..) function and the "rec2020" color - /// space, e.g. "color(rec2020 0.42210 0.47580 0.35605)". - Rec2020, - /// A color specified with the color(..) function and the "xyz-d50" color - /// space, e.g. "color(xyz-d50 0.2005 0.14089 0.4472)". - XyzD50, - /// A color specified with the color(..) function and the "xyz-d65" or "xyz" - /// color space, e.g. "color(xyz-d65 0.21661 0.14602 0.59452)". - /// NOTE: https://drafts.csswg.org/css-color-4/#resolving-color-function-values - /// specifies that `xyz` is an alias for the `xyz-d65` color space. - #[parse(aliases = "xyz")] - XyzD65, -} - -impl ColorSpace { - /// Returns whether this is a ``. - #[inline] - pub fn is_rectangular(&self) -> bool { - !self.is_polar() - } - - /// Returns whether this is a ``. - #[inline] - pub fn is_polar(&self) -> bool { - matches!(self, Self::Hsl | Self::Hwb | Self::Lch | Self::Oklch) - } - - /// Returns an index of the hue component in the color space, otherwise - /// `None`. - #[inline] - pub fn hue_index(&self) -> Option { - match self { - Self::Hsl | Self::Hwb => Some(0), - Self::Lch | Self::Oklch => Some(2), - - _ => { - debug_assert!(!self.is_polar()); - None - }, - } - } -} - -bitflags! { - /// Flags used when serializing colors. - #[derive(Default, MallocSizeOf, ToShmem)] - #[repr(C)] - pub struct ColorFlags : u8 { - /// If set, serializes sRGB colors into `color(srgb ...)` instead of - /// `rgba(...)`. - const AS_COLOR_FUNCTION = 1 << 0; - /// Whether the 1st color component is `none`. - const C1_IS_NONE = 1 << 1; - /// Whether the 2nd color component is `none`. - const C2_IS_NONE = 1 << 2; - /// Whether the 3rd color component is `none`. - const C3_IS_NONE = 1 << 3; - /// Whether the alpha component is `none`. - const ALPHA_IS_NONE = 1 << 4; - } -} - -/// An absolutely specified color, using either rgb(), rgba(), lab(), lch(), -/// oklab(), oklch() or color(). -#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] -#[repr(C)] -pub struct AbsoluteColor { - /// The 3 components that make up colors in any color space. - pub components: ColorComponents, - /// The alpha component of the color. - pub alpha: f32, - /// The current color space that the components represent. - pub color_space: ColorSpace, - /// Extra flags used durring serialization of this color. - pub flags: ColorFlags, -} - -/// Given an [`AbsoluteColor`], return the 4 float components as the type given, -/// e.g.: -/// -/// ```rust -/// let srgb = AbsoluteColor::new(ColorSpace::Srgb, 1.0, 0.0, 0.0, 0.0); -/// let floats = color_components_as!(&srgb, [f32; 4]); // [1.0, 0.0, 0.0, 0.0] -/// ``` -macro_rules! color_components_as { - ($c:expr, $t:ty) => {{ - // This macro is not an inline function, because we can't use the - // generic type ($t) in a constant expression as per: - // https://github.com/rust-lang/rust/issues/76560 - const_assert_eq!(std::mem::size_of::<$t>(), std::mem::size_of::<[f32; 4]>()); - const_assert_eq!(std::mem::align_of::<$t>(), std::mem::align_of::<[f32; 4]>()); - const_assert!(std::mem::size_of::() >= std::mem::size_of::<$t>()); - const_assert_eq!( - std::mem::align_of::(), - std::mem::align_of::<$t>() - ); - - std::mem::transmute::<&ColorComponents, &$t>(&$c.components) - }}; -} - -impl AbsoluteColor { - /// Create a new [`AbsoluteColor`] with the given [`ColorSpace`] and - /// components. - pub fn new(color_space: ColorSpace, components: ColorComponents, alpha: f32) -> Self { - let mut components = components; - - // Lightness must not be less than 0. - if matches!( - color_space, - ColorSpace::Lab | ColorSpace::Lch | ColorSpace::Oklab | ColorSpace::Oklch - ) { - components.0 = components.0.max(0.0); - } - - // Chroma must not be less than 0. - if matches!(color_space, ColorSpace::Lch | ColorSpace::Oklch) { - components.1 = components.1.max(0.0); - } - - Self { - components, - alpha: alpha.clamp(0.0, 1.0), - color_space, - flags: ColorFlags::empty(), - } - } - - /// Create a new [`AbsoluteColor`] from rgba values in the sRGB color space. - pub fn srgb(red: f32, green: f32, blue: f32, alpha: f32) -> Self { - Self::new(ColorSpace::Srgb, ColorComponents(red, green, blue), alpha) - } - - /// Create a new transparent color. - pub fn transparent() -> Self { - Self::srgb(0.0, 0.0, 0.0, 0.0) - } - - /// Create a new opaque black color. - pub fn black() -> Self { - Self::srgb(0.0, 0.0, 0.0, 1.0) - } - - /// Create a new opaque white color. - pub fn white() -> Self { - Self::srgb(1.0, 1.0, 1.0, 1.0) - } - - /// Return all the components of the color in an array. (Includes alpha) - #[inline] - pub fn raw_components(&self) -> &[f32; 4] { - unsafe { color_components_as!(self, [f32; 4]) } - } - - /// Returns true if this color is in one of the legacy color formats. - #[inline] - pub fn is_legacy_color(&self) -> bool { - // rgb(), rgba(), hsl(), hsla(), hwb(), hwba() - match self.color_space { - ColorSpace::Srgb => !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION), - ColorSpace::Hsl | ColorSpace::Hwb => true, - _ => false, - } - } - - /// Return the alpha component. - #[inline] - pub fn alpha(&self) -> f32 { - self.alpha - } - - /// Convert this color to the specified color space. - pub fn to_color_space(&self, color_space: ColorSpace) -> Self { - use ColorSpace::*; - - if self.color_space == color_space { - return self.clone(); - } - - // We have simplified conversions that do not need to convert to XYZ - // first. This improves performance, because it skips 2 matrix - // multiplications and reduces float rounding errors. - match (self.color_space, color_space) { - (Srgb, Hsl) => { - return Self::new( - color_space, - convert::rgb_to_hsl(&self.components), - self.alpha, - ); - }, - - (Srgb, Hwb) => { - return Self::new( - color_space, - convert::rgb_to_hwb(&self.components), - self.alpha, - ); - }, - - (Hsl, Srgb) => { - return Self::new( - color_space, - convert::hsl_to_rgb(&self.components), - self.alpha, - ); - }, - - (Hwb, Srgb) => { - return Self::new( - color_space, - convert::hwb_to_rgb(&self.components), - self.alpha, - ); - }, - - (Lab, Lch) | (Oklab, Oklch) => { - return Self::new( - color_space, - convert::lab_to_lch(&self.components), - self.alpha, - ); - }, - - (Lch, Lab) | (Oklch, Oklab) => { - return Self::new( - color_space, - convert::lch_to_lab(&self.components), - self.alpha, - ); - }, - - _ => {}, - } - - let (xyz, white_point) = match self.color_space { - Lab => convert::to_xyz::(&self.components), - Lch => convert::to_xyz::(&self.components), - Oklab => convert::to_xyz::(&self.components), - Oklch => convert::to_xyz::(&self.components), - Srgb => convert::to_xyz::(&self.components), - Hsl => convert::to_xyz::(&self.components), - Hwb => convert::to_xyz::(&self.components), - SrgbLinear => convert::to_xyz::(&self.components), - DisplayP3 => convert::to_xyz::(&self.components), - A98Rgb => convert::to_xyz::(&self.components), - ProphotoRgb => convert::to_xyz::(&self.components), - Rec2020 => convert::to_xyz::(&self.components), - XyzD50 => convert::to_xyz::(&self.components), - XyzD65 => convert::to_xyz::(&self.components), - }; - - let result = match color_space { - Lab => convert::from_xyz::(&xyz, white_point), - Lch => convert::from_xyz::(&xyz, white_point), - Oklab => convert::from_xyz::(&xyz, white_point), - Oklch => convert::from_xyz::(&xyz, white_point), - Srgb => convert::from_xyz::(&xyz, white_point), - Hsl => convert::from_xyz::(&xyz, white_point), - Hwb => convert::from_xyz::(&xyz, white_point), - SrgbLinear => convert::from_xyz::(&xyz, white_point), - DisplayP3 => convert::from_xyz::(&xyz, white_point), - A98Rgb => convert::from_xyz::(&xyz, white_point), - ProphotoRgb => convert::from_xyz::(&xyz, white_point), - Rec2020 => convert::from_xyz::(&xyz, white_point), - XyzD50 => convert::from_xyz::(&xyz, white_point), - XyzD65 => convert::from_xyz::(&xyz, white_point), - }; - - Self::new(color_space, result, self.alpha) - } -} - -impl From for ColorSpace { - fn from(value: cssparser::PredefinedColorSpace) -> Self { - match value { - cssparser::PredefinedColorSpace::Srgb => ColorSpace::Srgb, - cssparser::PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear, - cssparser::PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3, - cssparser::PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb, - cssparser::PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb, - cssparser::PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020, - cssparser::PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50, - cssparser::PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65, - } - } -} - -impl ToCss for AbsoluteColor { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - macro_rules! value_or_none { - ($v:expr,$flag:tt) => {{ - if self.flags.contains(ColorFlags::$flag) { - None - } else { - Some($v) - } - }}; - } - - let maybe_c1 = value_or_none!(self.components.0, C1_IS_NONE); - let maybe_c2 = value_or_none!(self.components.1, C2_IS_NONE); - let maybe_c3 = value_or_none!(self.components.2, C3_IS_NONE); - let maybe_alpha = value_or_none!(self.alpha, ALPHA_IS_NONE); - - match self.color_space { - ColorSpace::Hsl => { - let rgb = convert::hsl_to_rgb(&self.components); - Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest) - }, - - ColorSpace::Hwb => { - let rgb = convert::hwb_to_rgb(&self.components); - - Self::new(ColorSpace::Srgb, rgb, self.alpha).to_css(dest) - }, - - ColorSpace::Srgb if !self.flags.contains(ColorFlags::AS_COLOR_FUNCTION) => { - // Althought we are passing Option<_> in here, the to_css fn - // knows that the "none" keyword is not supported in the - // rgb/rgba legacy syntax. - cssparser::ToCss::to_css( - &cssparser::RGBA::from_floats(maybe_c1, maybe_c2, maybe_c3, maybe_alpha), - dest, - ) - }, - ColorSpace::Lab => cssparser::ToCss::to_css( - &cssparser::Lab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha), - dest, - ), - ColorSpace::Lch => cssparser::ToCss::to_css( - &cssparser::Lch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha), - dest, - ), - ColorSpace::Oklab => cssparser::ToCss::to_css( - &cssparser::Oklab::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha), - dest, - ), - ColorSpace::Oklch => cssparser::ToCss::to_css( - &cssparser::Oklch::new(maybe_c1, maybe_c2, maybe_c3, maybe_alpha), - dest, - ), - _ => { - let color_space = match self.color_space { - ColorSpace::Srgb => { - debug_assert!( - self.flags.contains(ColorFlags::AS_COLOR_FUNCTION), - "The case without this flag should be handled in the wrapping match case!!" - ); - - cssparser::PredefinedColorSpace::Srgb - }, - ColorSpace::SrgbLinear => cssparser::PredefinedColorSpace::SrgbLinear, - ColorSpace::DisplayP3 => cssparser::PredefinedColorSpace::DisplayP3, - ColorSpace::A98Rgb => cssparser::PredefinedColorSpace::A98Rgb, - ColorSpace::ProphotoRgb => cssparser::PredefinedColorSpace::ProphotoRgb, - ColorSpace::Rec2020 => cssparser::PredefinedColorSpace::Rec2020, - ColorSpace::XyzD50 => cssparser::PredefinedColorSpace::XyzD50, - ColorSpace::XyzD65 => cssparser::PredefinedColorSpace::XyzD65, - - _ => { - unreachable!("other color spaces do not support color() syntax") - }, - }; - - let color_function = cssparser::ColorFunction { - color_space, - c1: maybe_c1, - c2: maybe_c2, - c3: maybe_c3, - alpha: maybe_alpha, - }; - let color = cssparser::Color::ColorFunction(color_function); - cssparser::ToCss::to_css(&color, dest) - }, - } - } -} diff --git a/components/style/context.rs b/components/style/context.rs deleted file mode 100644 index 8f717eca61f..00000000000 --- a/components/style/context.rs +++ /dev/null @@ -1,698 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The context within which style is calculated. - -#[cfg(feature = "servo")] -use crate::animation::DocumentAnimationSet; -use crate::bloom::StyleBloom; -use crate::computed_value_flags::ComputedValueFlags; -use crate::data::{EagerPseudoStyles, ElementData}; -use crate::dom::{SendElement, TElement}; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::structs; -use crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB}; -use crate::properties::ComputedValues; -#[cfg(feature = "servo")] -use crate::properties::PropertyId; -use crate::rule_cache::RuleCache; -use crate::rule_tree::StrongRuleNode; -use crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT}; -use crate::shared_lock::StylesheetGuards; -use crate::sharing::StyleSharingCache; -use crate::stylist::Stylist; -use crate::thread_state::{self, ThreadState}; -use crate::traversal::DomTraversal; -use crate::traversal_flags::TraversalFlags; -use app_units::Au; -use euclid::default::Size2D; -use euclid::Scale; -#[cfg(feature = "servo")] -use fxhash::FxHashMap; -use selectors::NthIndexCache; -#[cfg(feature = "gecko")] -use servo_arc::Arc; -#[cfg(feature = "servo")] -use servo_atoms::Atom; -use std::fmt; -use std::ops; -use style_traits::CSSPixel; -use style_traits::DevicePixel; -#[cfg(feature = "servo")] -use style_traits::SpeculativePainter; -use time; - -pub use selectors::matching::QuirksMode; - -/// A global options structure for the style system. We use this instead of -/// opts to abstract across Gecko and Servo. -#[derive(Clone)] -pub struct StyleSystemOptions { - /// Whether the style sharing cache is disabled. - pub disable_style_sharing_cache: bool, - /// Whether we should dump statistics about the style system. - pub dump_style_statistics: bool, - /// The minimum number of elements that must be traversed to trigger a dump - /// of style statistics. - pub style_statistics_threshold: usize, -} - -#[cfg(feature = "gecko")] -fn get_env_bool(name: &str) -> bool { - use std::env; - match env::var(name) { - Ok(s) => !s.is_empty(), - Err(_) => false, - } -} - -const DEFAULT_STATISTICS_THRESHOLD: usize = 50; - -#[cfg(feature = "gecko")] -fn get_env_usize(name: &str) -> Option { - use std::env; - env::var(name).ok().map(|s| { - s.parse::() - .expect("Couldn't parse environmental variable as usize") - }) -} - -/// A global variable holding the state of -/// `StyleSystemOptions::default().disable_style_sharing_cache`. -/// See [#22854](https://github.com/servo/servo/issues/22854). -#[cfg(feature = "servo")] -pub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool = - std::sync::atomic::AtomicBool::new(false); - -/// A global variable holding the state of -/// `StyleSystemOptions::default().dump_style_statistics`. -/// See [#22854](https://github.com/servo/servo/issues/22854). -#[cfg(feature = "servo")] -pub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool = - std::sync::atomic::AtomicBool::new(false); - -impl Default for StyleSystemOptions { - #[cfg(feature = "servo")] - fn default() -> Self { - use std::sync::atomic::Ordering; - - StyleSystemOptions { - disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE - .load(Ordering::Relaxed), - dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed), - style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD, - } - } - - #[cfg(feature = "gecko")] - fn default() -> Self { - StyleSystemOptions { - disable_style_sharing_cache: get_env_bool("DISABLE_STYLE_SHARING_CACHE"), - dump_style_statistics: get_env_bool("DUMP_STYLE_STATISTICS"), - style_statistics_threshold: get_env_usize("STYLE_STATISTICS_THRESHOLD") - .unwrap_or(DEFAULT_STATISTICS_THRESHOLD), - } - } -} - -/// A shared style context. -/// -/// There's exactly one of these during a given restyle traversal, and it's -/// shared among the worker threads. -pub struct SharedStyleContext<'a> { - /// The CSS selector stylist. - pub stylist: &'a Stylist, - - /// Whether visited styles are enabled. - /// - /// They may be disabled when Gecko's pref layout.css.visited_links_enabled - /// is false, or when in private browsing mode. - pub visited_styles_enabled: bool, - - /// Configuration options. - pub options: StyleSystemOptions, - - /// Guards for pre-acquired locks - pub guards: StylesheetGuards<'a>, - - /// The current time for transitions and animations. This is needed to ensure - /// a consistent sampling time and also to adjust the time for testing. - pub current_time_for_animations: f64, - - /// Flags controlling how we traverse the tree. - pub traversal_flags: TraversalFlags, - - /// A map with our snapshots in order to handle restyle hints. - pub snapshot_map: &'a SnapshotMap, - - /// The state of all animations for our styled elements. - #[cfg(feature = "servo")] - pub animations: DocumentAnimationSet, - - /// Paint worklets - #[cfg(feature = "servo")] - pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters, -} - -impl<'a> SharedStyleContext<'a> { - /// Return a suitable viewport size in order to be used for viewport units. - pub fn viewport_size(&self) -> Size2D { - self.stylist.device().au_viewport_size() - } - - /// The device pixel ratio - pub fn device_pixel_ratio(&self) -> Scale { - self.stylist.device().device_pixel_ratio() - } - - /// The quirks mode of the document. - pub fn quirks_mode(&self) -> QuirksMode { - self.stylist.quirks_mode() - } -} - -/// The structure holds various intermediate inputs that are eventually used by -/// by the cascade. -/// -/// The matching and cascading process stores them in this format temporarily -/// within the `CurrentElementInfo`. At the end of the cascade, they are folded -/// down into the main `ComputedValues` to reduce memory usage per element while -/// still remaining accessible. -#[derive(Clone, Debug, Default)] -pub struct CascadeInputs { - /// The rule node representing the ordered list of rules matched for this - /// node. - pub rules: Option, - - /// The rule node representing the ordered list of rules matched for this - /// node if visited, only computed if there's a relevant link for this - /// element. A element's "relevant link" is the element being matched if it - /// is a link or the nearest ancestor link. - pub visited_rules: Option, - - /// The set of flags from container queries that we need for invalidation. - pub flags: ComputedValueFlags, -} - -impl CascadeInputs { - /// Construct inputs from previous cascade results, if any. - pub fn new_from_style(style: &ComputedValues) -> Self { - Self { - rules: style.rules.clone(), - visited_rules: style.visited_style().and_then(|v| v.rules.clone()), - flags: style.flags.for_cascade_inputs(), - } - } -} - -/// A list of cascade inputs for eagerly-cascaded pseudo-elements. -/// The list is stored inline. -#[derive(Debug)] -pub struct EagerPseudoCascadeInputs(Option<[Option; EAGER_PSEUDO_COUNT]>); - -// Manually implement `Clone` here because the derived impl of `Clone` for -// array types assumes the value inside is `Copy`. -impl Clone for EagerPseudoCascadeInputs { - fn clone(&self) -> Self { - if self.0.is_none() { - return EagerPseudoCascadeInputs(None); - } - let self_inputs = self.0.as_ref().unwrap(); - let mut inputs: [Option; EAGER_PSEUDO_COUNT] = Default::default(); - for i in 0..EAGER_PSEUDO_COUNT { - inputs[i] = self_inputs[i].clone(); - } - EagerPseudoCascadeInputs(Some(inputs)) - } -} - -impl EagerPseudoCascadeInputs { - /// Construct inputs from previous cascade results, if any. - fn new_from_style(styles: &EagerPseudoStyles) -> Self { - EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| { - let mut inputs: [Option; EAGER_PSEUDO_COUNT] = Default::default(); - for i in 0..EAGER_PSEUDO_COUNT { - inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s)); - } - inputs - })) - } - - /// Returns the list of rules, if they exist. - pub fn into_array(self) -> Option<[Option; EAGER_PSEUDO_COUNT]> { - self.0 - } -} - -/// The cascade inputs associated with a node, including those for any -/// pseudo-elements. -/// -/// The matching and cascading process stores them in this format temporarily -/// within the `CurrentElementInfo`. At the end of the cascade, they are folded -/// down into the main `ComputedValues` to reduce memory usage per element while -/// still remaining accessible. -#[derive(Clone, Debug)] -pub struct ElementCascadeInputs { - /// The element's cascade inputs. - pub primary: CascadeInputs, - /// A list of the inputs for the element's eagerly-cascaded pseudo-elements. - pub pseudos: EagerPseudoCascadeInputs, -} - -impl ElementCascadeInputs { - /// Construct inputs from previous cascade results, if any. - #[inline] - pub fn new_from_element_data(data: &ElementData) -> Self { - debug_assert!(data.has_styles()); - ElementCascadeInputs { - primary: CascadeInputs::new_from_style(data.styles.primary()), - pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos), - } - } -} - -/// Statistics gathered during the traversal. We gather statistics on each -/// thread and then combine them after the threads join via the Add -/// implementation below. -#[derive(AddAssign, Clone, Default)] -pub struct PerThreadTraversalStatistics { - /// The total number of elements traversed. - pub elements_traversed: u32, - /// The number of elements where has_styles() went from false to true. - pub elements_styled: u32, - /// The number of elements for which we performed selector matching. - pub elements_matched: u32, - /// The number of cache hits from the StyleSharingCache. - pub styles_shared: u32, - /// The number of styles reused via rule node comparison from the - /// StyleSharingCache. - pub styles_reused: u32, -} - -/// Statistics gathered during the traversal plus some information from -/// other sources including stylist. -#[derive(Default)] -pub struct TraversalStatistics { - /// Aggregated statistics gathered during the traversal. - pub aggregated: PerThreadTraversalStatistics, - /// The number of selectors in the stylist. - pub selectors: u32, - /// The number of revalidation selectors. - pub revalidation_selectors: u32, - /// The number of state/attr dependencies in the dependency set. - pub dependency_selectors: u32, - /// The number of declarations in the stylist. - pub declarations: u32, - /// The number of times the stylist was rebuilt. - pub stylist_rebuilds: u32, - /// Time spent in the traversal, in milliseconds. - pub traversal_time_ms: f64, - /// Whether this was a parallel traversal. - pub is_parallel: bool, - /// Whether this is a "large" traversal. - pub is_large: bool, -} - -/// Format the statistics in a way that the performance test harness understands. -/// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2 -impl fmt::Display for TraversalStatistics { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - debug_assert!( - self.traversal_time_ms != 0.0, - "should have set traversal time" - ); - writeln!(f, "[PERF] perf block start")?; - writeln!( - f, - "[PERF],traversal,{}", - if self.is_parallel { - "parallel" - } else { - "sequential" - } - )?; - writeln!( - f, - "[PERF],elements_traversed,{}", - self.aggregated.elements_traversed - )?; - writeln!( - f, - "[PERF],elements_styled,{}", - self.aggregated.elements_styled - )?; - writeln!( - f, - "[PERF],elements_matched,{}", - self.aggregated.elements_matched - )?; - writeln!(f, "[PERF],styles_shared,{}", self.aggregated.styles_shared)?; - writeln!(f, "[PERF],styles_reused,{}", self.aggregated.styles_reused)?; - writeln!(f, "[PERF],selectors,{}", self.selectors)?; - writeln!( - f, - "[PERF],revalidation_selectors,{}", - self.revalidation_selectors - )?; - writeln!( - f, - "[PERF],dependency_selectors,{}", - self.dependency_selectors - )?; - writeln!(f, "[PERF],declarations,{}", self.declarations)?; - writeln!(f, "[PERF],stylist_rebuilds,{}", self.stylist_rebuilds)?; - writeln!(f, "[PERF],traversal_time_ms,{}", self.traversal_time_ms)?; - writeln!(f, "[PERF] perf block end") - } -} - -impl TraversalStatistics { - /// Generate complete traversal statistics. - /// - /// The traversal time is computed given the start time in seconds. - pub fn new( - aggregated: PerThreadTraversalStatistics, - traversal: &D, - parallel: bool, - start: f64, - ) -> TraversalStatistics - where - E: TElement, - D: DomTraversal, - { - let threshold = traversal - .shared_context() - .options - .style_statistics_threshold; - let stylist = traversal.shared_context().stylist; - let is_large = aggregated.elements_traversed as usize >= threshold; - TraversalStatistics { - aggregated, - selectors: stylist.num_selectors() as u32, - revalidation_selectors: stylist.num_revalidation_selectors() as u32, - dependency_selectors: stylist.num_invalidations() as u32, - declarations: stylist.num_declarations() as u32, - stylist_rebuilds: stylist.num_rebuilds() as u32, - traversal_time_ms: (time::precise_time_s() - start) * 1000.0, - is_parallel: parallel, - is_large, - } - } -} - -#[cfg(feature = "gecko")] -bitflags! { - /// Represents which tasks are performed in a SequentialTask of - /// UpdateAnimations which is a result of normal restyle. - pub struct UpdateAnimationsTasks: u8 { - /// Update CSS Animations. - const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations; - /// Update CSS Transitions. - const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions; - /// Update effect properties. - const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties; - /// Update animation cacade results for animations running on the compositor. - const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults; - /// Display property was changed from none. - /// Script animations keep alive on display:none elements, so we need to trigger - /// the second animation restyles for the script animations in the case where - /// the display property was changed from 'none' to others. - const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone; - /// Update CSS named scroll progress timelines. - const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines; - /// Update CSS named view progress timelines. - const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines; - } -} - -#[cfg(feature = "gecko")] -bitflags! { - /// Represents which tasks are performed in a SequentialTask as a result of - /// animation-only restyle. - pub struct PostAnimationTasks: u8 { - /// Display property was changed from none in animation-only restyle so - /// that we need to resolve styles for descendants in a subsequent - /// normal restyle. - const DISPLAY_CHANGED_FROM_NONE_FOR_SMIL = 0x01; - } -} - -/// A task to be run in sequential mode on the parent (non-worker) thread. This -/// is used by the style system to queue up work which is not safe to do during -/// the parallel traversal. -pub enum SequentialTask { - /// Entry to avoid an unused type parameter error on servo. - Unused(SendElement), - - /// Performs one of a number of possible tasks related to updating - /// animations based on the |tasks| field. These include updating CSS - /// animations/transitions that changed as part of the non-animation style - /// traversal, and updating the computed effect properties. - #[cfg(feature = "gecko")] - UpdateAnimations { - /// The target element or pseudo-element. - el: SendElement, - /// The before-change style for transitions. We use before-change style - /// as the initial value of its Keyframe. Required if |tasks| includes - /// CSSTransitions. - before_change_style: Option>, - /// The tasks which are performed in this SequentialTask. - tasks: UpdateAnimationsTasks, - }, - - /// Performs one of a number of possible tasks as a result of animation-only - /// restyle. - /// - /// Currently we do only process for resolving descendant elements that were - /// display:none subtree for SMIL animation. - #[cfg(feature = "gecko")] - PostAnimation { - /// The target element. - el: SendElement, - /// The tasks which are performed in this SequentialTask. - tasks: PostAnimationTasks, - }, -} - -impl SequentialTask { - /// Executes this task. - pub fn execute(self) { - use self::SequentialTask::*; - debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); - match self { - Unused(_) => unreachable!(), - #[cfg(feature = "gecko")] - UpdateAnimations { - el, - before_change_style, - tasks, - } => { - el.update_animations(before_change_style, tasks); - }, - #[cfg(feature = "gecko")] - PostAnimation { el, tasks } => { - el.process_post_animation(tasks); - }, - } - } - - /// Creates a task to update various animation-related state on a given - /// (pseudo-)element. - #[cfg(feature = "gecko")] - pub fn update_animations( - el: E, - before_change_style: Option>, - tasks: UpdateAnimationsTasks, - ) -> Self { - use self::SequentialTask::*; - UpdateAnimations { - el: unsafe { SendElement::new(el) }, - before_change_style, - tasks, - } - } - - /// Creates a task to do post-process for a given element as a result of - /// animation-only restyle. - #[cfg(feature = "gecko")] - pub fn process_post_animation(el: E, tasks: PostAnimationTasks) -> Self { - use self::SequentialTask::*; - PostAnimation { - el: unsafe { SendElement::new(el) }, - tasks, - } - } -} - -/// A list of SequentialTasks that get executed on Drop. -pub struct SequentialTaskList(Vec>) -where - E: TElement; - -impl ops::Deref for SequentialTaskList -where - E: TElement, -{ - type Target = Vec>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl ops::DerefMut for SequentialTaskList -where - E: TElement, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Drop for SequentialTaskList -where - E: TElement, -{ - fn drop(&mut self) { - debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); - for task in self.0.drain(..) { - task.execute() - } - } -} - -/// A helper type for stack limit checking. This assumes that stacks grow -/// down, which is true for all non-ancient CPU architectures. -pub struct StackLimitChecker { - lower_limit: usize, -} - -impl StackLimitChecker { - /// Create a new limit checker, for this thread, allowing further use - /// of up to |stack_size| bytes beyond (below) the current stack pointer. - #[inline(never)] - pub fn new(stack_size_limit: usize) -> Self { - StackLimitChecker { - lower_limit: StackLimitChecker::get_sp() - stack_size_limit, - } - } - - /// Checks whether the previously stored stack limit has now been exceeded. - #[inline(never)] - pub fn limit_exceeded(&self) -> bool { - let curr_sp = StackLimitChecker::get_sp(); - - // Do some sanity-checking to ensure that our invariants hold, even in - // the case where we've exceeded the soft limit. - // - // The correctness of depends on the assumption that no stack wraps - // around the end of the address space. - if cfg!(debug_assertions) { - // Compute the actual bottom of the stack by subtracting our safety - // margin from our soft limit. Note that this will be slightly below - // the actual bottom of the stack, because there are a few initial - // frames on the stack before we do the measurement that computes - // the limit. - let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024; - - // The bottom of the stack should be below the current sp. If it - // isn't, that means we've either waited too long to check the limit - // and burned through our safety margin (in which case we probably - // would have segfaulted by now), or we're using a limit computed for - // a different thread. - debug_assert!(stack_bottom < curr_sp); - - // Compute the distance between the current sp and the bottom of - // the stack, and compare it against the current stack. It should be - // no further from us than the total stack size. We allow some slop - // to handle the fact that stack_bottom is a bit further than the - // bottom of the stack, as discussed above. - let distance_to_stack_bottom = curr_sp - stack_bottom; - let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024; - debug_assert!(distance_to_stack_bottom <= max_allowable_distance); - } - - // The actual bounds check. - curr_sp <= self.lower_limit - } - - // Technically, rustc can optimize this away, but shouldn't for now. - // We should fix this once black_box is stable. - #[inline(always)] - fn get_sp() -> usize { - let mut foo: usize = 42; - (&mut foo as *mut usize) as usize - } -} - -/// A thread-local style context. -/// -/// This context contains data that needs to be used during restyling, but is -/// not required to be unique among worker threads, so we create one per worker -/// thread in order to be able to mutate it without locking. -pub struct ThreadLocalStyleContext { - /// A cache to share style among siblings. - pub sharing_cache: StyleSharingCache, - /// A cache from matched properties to elements that match those. - pub rule_cache: RuleCache, - /// The bloom filter used to fast-reject selector-matching. - pub bloom_filter: StyleBloom, - /// A set of tasks to be run (on the parent thread) in sequential mode after - /// the rest of the styling is complete. This is useful for - /// infrequently-needed non-threadsafe operations. - /// - /// It's important that goes after the style sharing cache and the bloom - /// filter, to ensure they're dropped before we execute the tasks, which - /// could create another ThreadLocalStyleContext for style computation. - pub tasks: SequentialTaskList, - /// Statistics about the traversal. - pub statistics: PerThreadTraversalStatistics, - /// A checker used to ensure that parallel.rs does not recurse indefinitely - /// even on arbitrarily deep trees. See Gecko bug 1376883. - pub stack_limit_checker: StackLimitChecker, - /// A cache for nth-index-like selectors. - pub nth_index_cache: NthIndexCache, -} - -impl ThreadLocalStyleContext { - /// Creates a new `ThreadLocalStyleContext` - pub fn new() -> Self { - ThreadLocalStyleContext { - sharing_cache: StyleSharingCache::new(), - rule_cache: RuleCache::new(), - bloom_filter: StyleBloom::new(), - tasks: SequentialTaskList(Vec::new()), - statistics: PerThreadTraversalStatistics::default(), - stack_limit_checker: StackLimitChecker::new( - (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024, - ), - nth_index_cache: NthIndexCache::default(), - } - } -} - -/// A `StyleContext` is just a simple container for a immutable reference to a -/// shared style context, and a mutable reference to a local one. -pub struct StyleContext<'a, E: TElement + 'a> { - /// The shared style context reference. - pub shared: &'a SharedStyleContext<'a>, - /// The thread-local style context (mutable) reference. - pub thread_local: &'a mut ThreadLocalStyleContext, -} - -/// A registered painter -#[cfg(feature = "servo")] -pub trait RegisteredSpeculativePainter: SpeculativePainter { - /// The name it was registered with - fn name(&self) -> Atom; - /// The properties it was registered with - fn properties(&self) -> &FxHashMap; -} - -/// A set of registered painters -#[cfg(feature = "servo")] -pub trait RegisteredSpeculativePainters: Sync { - /// Look up a speculative painter - fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>; -} diff --git a/components/style/counter_style/mod.rs b/components/style/counter_style/mod.rs deleted file mode 100644 index 65143d69906..00000000000 --- a/components/style/counter_style/mod.rs +++ /dev/null @@ -1,697 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The [`@counter-style`][counter-style] at-rule. -//! -//! [counter-style]: https://drafts.csswg.org/css-counter-styles/ - -use crate::error_reporting::ContextualParseError; -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::values::specified::Integer; -use crate::values::CustomIdent; -use crate::Atom; -use cssparser::{ - AtRuleParser, DeclarationParser, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, -}; -use cssparser::{CowRcStr, Parser, SourceLocation, Token}; -use selectors::parser::SelectorParseErrorKind; -use std::fmt::{self, Write}; -use std::mem; -use std::num::Wrapping; -use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError}; -use style_traits::{StyleParseErrorKind, ToCss}; - -/// Parse a counter style name reference. -/// -/// This allows the reserved counter style names "decimal" and "disc". -pub fn parse_counter_style_name<'i, 't>( - input: &mut Parser<'i, 't>, -) -> Result> { - macro_rules! predefined { - ($($name: expr,)+) => { - { - ascii_case_insensitive_phf_map! { - // FIXME: use static atoms https://github.com/rust-lang/rust/issues/33156 - predefined -> &'static str = { - $( - $name => $name, - )+ - } - } - - let location = input.current_source_location(); - let ident = input.expect_ident()?; - if let Some(&lower_cased) = predefined(&ident) { - Ok(CustomIdent(Atom::from(lower_cased))) - } else { - // none is always an invalid value. - CustomIdent::from_ident(location, ident, &["none"]) - } - } - } - } - include!("predefined.rs") -} - -fn is_valid_name_definition(ident: &CustomIdent) -> bool { - ident.0 != atom!("decimal") && - ident.0 != atom!("disc") && - ident.0 != atom!("circle") && - ident.0 != atom!("square") && - ident.0 != atom!("disclosure-closed") && - ident.0 != atom!("disclosure-open") -} - -/// Parse the prelude of an @counter-style rule -pub fn parse_counter_style_name_definition<'i, 't>( - input: &mut Parser<'i, 't>, -) -> Result> { - parse_counter_style_name(input).and_then(|ident| { - if !is_valid_name_definition(&ident) { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } else { - Ok(ident) - } - }) -} - -/// Parse the body (inside `{}`) of an @counter-style rule -pub fn parse_counter_style_body<'i, 't>( - name: CustomIdent, - context: &ParserContext, - input: &mut Parser<'i, 't>, - location: SourceLocation, -) -> Result> { - let start = input.current_source_location(); - let mut rule = CounterStyleRuleData::empty(name, location); - { - let mut parser = CounterStyleRuleParser { - context, - rule: &mut rule, - }; - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(declaration) = iter.next() { - if let Err((error, slice)) = declaration { - let location = error.location; - let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration( - slice, error, - ); - context.log_css_error(location, error) - } - } - } - let error = match *rule.resolved_system() { - ref system @ System::Cyclic | - ref system @ System::Fixed { .. } | - ref system @ System::Symbolic | - ref system @ System::Alphabetic | - ref system @ System::Numeric - if rule.symbols.is_none() => - { - let system = system.to_css_string(); - Some(ContextualParseError::InvalidCounterStyleWithoutSymbols( - system, - )) - }, - ref system @ System::Alphabetic | ref system @ System::Numeric - if rule.symbols().unwrap().0.len() < 2 => - { - let system = system.to_css_string(); - Some(ContextualParseError::InvalidCounterStyleNotEnoughSymbols( - system, - )) - }, - System::Additive if rule.additive_symbols.is_none() => { - Some(ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols) - }, - System::Extends(_) if rule.symbols.is_some() => { - Some(ContextualParseError::InvalidCounterStyleExtendsWithSymbols) - }, - System::Extends(_) if rule.additive_symbols.is_some() => { - Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols) - }, - _ => None, - }; - if let Some(error) = error { - context.log_css_error(start, error); - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } else { - Ok(rule) - } -} - -struct CounterStyleRuleParser<'a, 'b: 'a> { - context: &'a ParserContext<'b>, - rule: &'a mut CounterStyleRuleData, -} - -/// Default methods reject all at rules. -impl<'a, 'b, 'i> AtRuleParser<'i> for CounterStyleRuleParser<'a, 'b> { - type Prelude = (); - type AtRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for CounterStyleRuleParser<'a, 'b> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for CounterStyleRuleParser<'a, 'b> -{ - fn parse_qualified(&self) -> bool { - false - } - fn parse_declarations(&self) -> bool { - true - } -} - -macro_rules! checker { - ($self:ident._($value:ident)) => {}; - ($self:ident. $checker:ident($value:ident)) => { - if !$self.$checker(&$value) { - return false; - } - }; -} - -macro_rules! counter_style_descriptors { - ( - $( #[$doc: meta] $name: tt $ident: ident / $setter: ident [$checker: tt]: $ty: ty, )+ - ) => { - /// An @counter-style rule - #[derive(Clone, Debug, ToShmem)] - pub struct CounterStyleRuleData { - name: CustomIdent, - generation: Wrapping, - $( - #[$doc] - $ident: Option<$ty>, - )+ - /// Line and column of the @counter-style rule source code. - pub source_location: SourceLocation, - } - - impl CounterStyleRuleData { - fn empty(name: CustomIdent, source_location: SourceLocation) -> Self { - CounterStyleRuleData { - name: name, - generation: Wrapping(0), - $( - $ident: None, - )+ - source_location, - } - } - - $( - #[$doc] - pub fn $ident(&self) -> Option<&$ty> { - self.$ident.as_ref() - } - )+ - - $( - #[$doc] - pub fn $setter(&mut self, value: $ty) -> bool { - checker!(self.$checker(value)); - self.$ident = Some(value); - self.generation += Wrapping(1); - true - } - )+ - } - - impl<'a, 'b, 'i> DeclarationParser<'i> for CounterStyleRuleParser<'a, 'b> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - match_ignore_ascii_case! { &*name, - $( - $name => { - // DeclarationParser also calls parse_entirely so we’d normally not - // need to, but in this case we do because we set the value as a side - // effect rather than returning it. - let value = input.parse_entirely(|i| Parse::parse(self.context, i))?; - self.rule.$ident = Some(value) - }, - )* - _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), - } - Ok(()) - } - } - - impl ToCssWithGuard for CounterStyleRuleData { - fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@counter-style ")?; - self.name.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" { ")?; - $( - if let Some(ref value) = self.$ident { - dest.write_str(concat!($name, ": "))?; - ToCss::to_css(value, &mut CssWriter::new(dest))?; - dest.write_str("; ")?; - } - )+ - dest.write_char('}') - } - } - } -} - -counter_style_descriptors! { - /// - "system" system / set_system [check_system]: System, - - /// - "negative" negative / set_negative [_]: Negative, - - /// - "prefix" prefix / set_prefix [_]: Symbol, - - /// - "suffix" suffix / set_suffix [_]: Symbol, - - /// - "range" range / set_range [_]: CounterRanges, - - /// - "pad" pad / set_pad [_]: Pad, - - /// - "fallback" fallback / set_fallback [_]: Fallback, - - /// - "symbols" symbols / set_symbols [check_symbols]: Symbols, - - /// - "additive-symbols" additive_symbols / - set_additive_symbols [check_additive_symbols]: AdditiveSymbols, - - /// - "speak-as" speak_as / set_speak_as [_]: SpeakAs, -} - -// Implements the special checkers for some setters. -// See -impl CounterStyleRuleData { - /// Check that the system is effectively not changed. Only params - /// of system descriptor is changeable. - fn check_system(&self, value: &System) -> bool { - mem::discriminant(self.resolved_system()) == mem::discriminant(value) - } - - fn check_symbols(&self, value: &Symbols) -> bool { - match *self.resolved_system() { - // These two systems require at least two symbols. - System::Numeric | System::Alphabetic => value.0.len() >= 2, - // No symbols should be set for extends system. - System::Extends(_) => false, - _ => true, - } - } - - fn check_additive_symbols(&self, _value: &AdditiveSymbols) -> bool { - match *self.resolved_system() { - // No additive symbols should be set for extends system. - System::Extends(_) => false, - _ => true, - } - } -} - -impl CounterStyleRuleData { - /// Get the name of the counter style rule. - pub fn name(&self) -> &CustomIdent { - &self.name - } - - /// Set the name of the counter style rule. Caller must ensure that - /// the name is valid. - pub fn set_name(&mut self, name: CustomIdent) { - debug_assert!(is_valid_name_definition(&name)); - self.name = name; - } - - /// Get the current generation of the counter style rule. - pub fn generation(&self) -> u32 { - self.generation.0 - } - - /// Get the system of this counter style rule, default to - /// `symbolic` if not specified. - pub fn resolved_system(&self) -> &System { - match self.system { - Some(ref system) => system, - None => &System::Symbolic, - } - } -} - -/// -#[derive(Clone, Debug, ToShmem)] -pub enum System { - /// 'cyclic' - Cyclic, - /// 'numeric' - Numeric, - /// 'alphabetic' - Alphabetic, - /// 'symbolic' - Symbolic, - /// 'additive' - Additive, - /// 'fixed ?' - Fixed { - /// '?' - first_symbol_value: Option, - }, - /// 'extends ' - Extends(CustomIdent), -} - -impl Parse for System { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - try_match_ident_ignore_ascii_case! { input, - "cyclic" => Ok(System::Cyclic), - "numeric" => Ok(System::Numeric), - "alphabetic" => Ok(System::Alphabetic), - "symbolic" => Ok(System::Symbolic), - "additive" => Ok(System::Additive), - "fixed" => { - let first_symbol_value = input.try_parse(|i| Integer::parse(context, i)).ok(); - Ok(System::Fixed { first_symbol_value }) - }, - "extends" => { - let other = parse_counter_style_name(input)?; - Ok(System::Extends(other)) - }, - } - } -} - -impl ToCss for System { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - match *self { - System::Cyclic => dest.write_str("cyclic"), - System::Numeric => dest.write_str("numeric"), - System::Alphabetic => dest.write_str("alphabetic"), - System::Symbolic => dest.write_str("symbolic"), - System::Additive => dest.write_str("additive"), - System::Fixed { first_symbol_value } => { - if let Some(value) = first_symbol_value { - dest.write_str("fixed ")?; - value.to_css(dest) - } else { - dest.write_str("fixed") - } - }, - System::Extends(ref other) => { - dest.write_str("extends ")?; - other.to_css(dest) - }, - } - } -} - -/// -#[derive( - Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem, -)] -#[repr(u8)] -pub enum Symbol { - /// - String(crate::OwnedStr), - /// - Ident(CustomIdent), - // Not implemented: - // /// - // Image(Image), -} - -impl Parse for Symbol { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let location = input.current_source_location(); - match *input.next()? { - Token::QuotedString(ref s) => Ok(Symbol::String(s.as_ref().to_owned().into())), - Token::Ident(ref s) => Ok(Symbol::Ident(CustomIdent::from_ident(location, s, &[])?)), - ref t => Err(location.new_unexpected_token_error(t.clone())), - } - } -} - -impl Symbol { - /// Returns whether this symbol is allowed in symbols() function. - pub fn is_allowed_in_symbols(&self) -> bool { - match self { - // Identifier is not allowed. - &Symbol::Ident(_) => false, - _ => true, - } - } -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -pub struct Negative(pub Symbol, pub Option); - -impl Parse for Negative { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - Ok(Negative( - Symbol::parse(context, input)?, - input.try_parse(|input| Symbol::parse(context, input)).ok(), - )) - } -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -pub struct CounterRange { - /// The start of the range. - pub start: CounterBound, - /// The end of the range. - pub end: CounterBound, -} - -/// -/// -/// Empty represents 'auto' -#[derive(Clone, Debug, ToCss, ToShmem)] -#[css(comma)] -pub struct CounterRanges(#[css(iterable, if_empty = "auto")] pub crate::OwnedSlice); - -/// A bound found in `CounterRanges`. -#[derive(Clone, Copy, Debug, ToCss, ToShmem)] -pub enum CounterBound { - /// An integer bound. - Integer(Integer), - /// The infinite bound. - Infinite, -} - -impl Parse for CounterRanges { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - if input - .try_parse(|input| input.expect_ident_matching("auto")) - .is_ok() - { - return Ok(CounterRanges(Default::default())); - } - - let ranges = input.parse_comma_separated(|input| { - let start = parse_bound(context, input)?; - let end = parse_bound(context, input)?; - if let (CounterBound::Integer(start), CounterBound::Integer(end)) = (start, end) { - if start > end { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - } - Ok(CounterRange { start, end }) - })?; - - Ok(CounterRanges(ranges.into())) - } -} - -fn parse_bound<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, -) -> Result> { - if let Ok(integer) = input.try_parse(|input| Integer::parse(context, input)) { - return Ok(CounterBound::Integer(integer)); - } - input.expect_ident_matching("infinite")?; - Ok(CounterBound::Infinite) -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -pub struct Pad(pub Integer, pub Symbol); - -impl Parse for Pad { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let pad_with = input.try_parse(|input| Symbol::parse(context, input)); - let min_length = Integer::parse_non_negative(context, input)?; - let pad_with = pad_with.or_else(|_| Symbol::parse(context, input))?; - Ok(Pad(min_length, pad_with)) - } -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -pub struct Fallback(pub CustomIdent); - -impl Parse for Fallback { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - Ok(Fallback(parse_counter_style_name(input)?)) - } -} - -/// -#[derive( - Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem, -)] -#[repr(C)] -pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice); - -impl Parse for Symbols { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let mut symbols = Vec::new(); - while let Ok(s) = input.try_parse(|input| Symbol::parse(context, input)) { - symbols.push(s); - } - if symbols.is_empty() { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - Ok(Symbols(symbols.into())) - } -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -#[css(comma)] -pub struct AdditiveSymbols(#[css(iterable)] pub crate::OwnedSlice); - -impl Parse for AdditiveSymbols { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let tuples = Vec::::parse(context, input)?; - // FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220 - if tuples - .windows(2) - .any(|window| window[0].weight <= window[1].weight) - { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - Ok(AdditiveSymbols(tuples.into())) - } -} - -/// && -#[derive(Clone, Debug, ToCss, ToShmem)] -pub struct AdditiveTuple { - /// - pub weight: Integer, - /// - pub symbol: Symbol, -} - -impl OneOrMoreSeparated for AdditiveTuple { - type S = Comma; -} - -impl Parse for AdditiveTuple { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let symbol = input.try_parse(|input| Symbol::parse(context, input)); - let weight = Integer::parse_non_negative(context, input)?; - let symbol = symbol.or_else(|_| Symbol::parse(context, input))?; - Ok(Self { weight, symbol }) - } -} - -/// -#[derive(Clone, Debug, ToCss, ToShmem)] -pub enum SpeakAs { - /// auto - Auto, - /// bullets - Bullets, - /// numbers - Numbers, - /// words - Words, - // /// spell-out, not supported, see bug 1024178 - // SpellOut, - /// - Other(CustomIdent), -} - -impl Parse for SpeakAs { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let mut is_spell_out = false; - let result = input.try_parse(|input| { - let ident = input.expect_ident().map_err(|_| ())?; - match_ignore_ascii_case! { &*ident, - "auto" => Ok(SpeakAs::Auto), - "bullets" => Ok(SpeakAs::Bullets), - "numbers" => Ok(SpeakAs::Numbers), - "words" => Ok(SpeakAs::Words), - "spell-out" => { - is_spell_out = true; - Err(()) - }, - _ => Err(()), - } - }); - if is_spell_out { - // spell-out is not supported, but don’t parse it as a . - // See bug 1024178. - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - result.or_else(|_| Ok(SpeakAs::Other(parse_counter_style_name(input)?))) - } -} diff --git a/components/style/counter_style/predefined.rs b/components/style/counter_style/predefined.rs deleted file mode 100644 index 7243e3b3f32..00000000000 --- a/components/style/counter_style/predefined.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -predefined! { - "decimal", - "decimal-leading-zero", - "arabic-indic", - "armenian", - "upper-armenian", - "lower-armenian", - "bengali", - "cambodian", - "khmer", - "cjk-decimal", - "devanagari", - "georgian", - "gujarati", - "gurmukhi", - "hebrew", - "kannada", - "lao", - "malayalam", - "mongolian", - "myanmar", - "oriya", - "persian", - "lower-roman", - "upper-roman", - "tamil", - "telugu", - "thai", - "tibetan", - "lower-alpha", - "lower-latin", - "upper-alpha", - "upper-latin", - "cjk-earthly-branch", - "cjk-heavenly-stem", - "lower-greek", - "hiragana", - "hiragana-iroha", - "katakana", - "katakana-iroha", - "disc", - "circle", - "square", - "disclosure-open", - "disclosure-closed", - "japanese-informal", - "japanese-formal", - "korean-hangul-formal", - "korean-hanja-informal", - "korean-hanja-formal", - "simp-chinese-informal", - "simp-chinese-formal", - "trad-chinese-informal", - "trad-chinese-formal", - "cjk-ideographic", - "ethiopic-numeric", -} diff --git a/components/style/counter_style/update_predefined.py b/components/style/counter_style/update_predefined.py deleted file mode 100755 index 1523958ff3e..00000000000 --- a/components/style/counter_style/update_predefined.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -# 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/. */ - -import os.path -import re -import urllib - - -def main(filename): - names = [ - re.search('>([^>]+)(| VariableValue; - -struct EnvironmentVariable { - name: Atom, - evaluator: EnvironmentEvaluator, -} - -macro_rules! make_variable { - ($name:expr, $evaluator:expr) => {{ - EnvironmentVariable { - name: $name, - evaluator: $evaluator, - } - }}; -} - -fn get_safearea_inset_top(device: &Device) -> VariableValue { - VariableValue::pixels(device.safe_area_insets().top) -} - -fn get_safearea_inset_bottom(device: &Device) -> VariableValue { - VariableValue::pixels(device.safe_area_insets().bottom) -} - -fn get_safearea_inset_left(device: &Device) -> VariableValue { - VariableValue::pixels(device.safe_area_insets().left) -} - -fn get_safearea_inset_right(device: &Device) -> VariableValue { - VariableValue::pixels(device.safe_area_insets().right) -} - -#[cfg(feature = "gecko")] -fn get_content_preferred_color_scheme(device: &Device) -> VariableValue { - use crate::gecko::media_features::PrefersColorScheme; - let prefers_color_scheme = unsafe { - crate::gecko_bindings::bindings::Gecko_MediaFeatures_PrefersColorScheme( - device.document(), - /* use_content = */ true, - ) - }; - VariableValue::ident(match prefers_color_scheme { - PrefersColorScheme::Light => "light", - PrefersColorScheme::Dark => "dark", - }) -} - -#[cfg(feature = "servo")] -fn get_content_preferred_color_scheme(_device: &Device) -> VariableValue { - // TODO: implement this. - VariableValue::ident("light") -} - -fn get_scrollbar_inline_size(device: &Device) -> VariableValue { - VariableValue::pixels(device.scrollbar_inline_size().px()) -} - -static ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [ - make_variable!(atom!("safe-area-inset-top"), get_safearea_inset_top), - make_variable!(atom!("safe-area-inset-bottom"), get_safearea_inset_bottom), - make_variable!(atom!("safe-area-inset-left"), get_safearea_inset_left), - make_variable!(atom!("safe-area-inset-right"), get_safearea_inset_right), -]; - -#[cfg(feature = "gecko")] -macro_rules! lnf_int { - ($id:ident) => { - unsafe { - crate::gecko_bindings::bindings::Gecko_GetLookAndFeelInt( - crate::gecko_bindings::bindings::LookAndFeel_IntID::$id as i32, - ) - } - }; -} - -#[cfg(feature = "servo")] -macro_rules! lnf_int { - ($id:ident) => { - // TODO: implement this. - 0 - }; -} - -macro_rules! lnf_int_variable { - ($atom:expr, $id:ident, $ctor:ident) => {{ - fn __eval(_: &Device) -> VariableValue { - VariableValue::$ctor(lnf_int!($id)) - } - make_variable!($atom, __eval) - }}; -} - -static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 6] = [ - lnf_int_variable!( - atom!("-moz-gtk-csd-titlebar-radius"), - TitlebarRadius, - int_pixels - ), - lnf_int_variable!( - atom!("-moz-gtk-csd-close-button-position"), - GTKCSDCloseButtonPosition, - integer - ), - lnf_int_variable!( - atom!("-moz-gtk-csd-minimize-button-position"), - GTKCSDMinimizeButtonPosition, - integer - ), - lnf_int_variable!( - atom!("-moz-gtk-csd-maximize-button-position"), - GTKCSDMaximizeButtonPosition, - integer - ), - make_variable!( - atom!("-moz-content-preferred-color-scheme"), - get_content_preferred_color_scheme - ), - make_variable!(atom!("scrollbar-inline-size"), get_scrollbar_inline_size), -]; - -impl CssEnvironment { - #[inline] - fn get(&self, name: &Atom, device: &Device) -> Option { - if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) { - return Some((var.evaluator)(device)); - } - if !device.chrome_rules_enabled_for_document() { - return None; - } - let var = CHROME_ENVIRONMENT_VARIABLES - .iter() - .find(|var| var.name == *name)?; - Some((var.evaluator)(device)) - } -} - -/// A custom property name is just an `Atom`. -/// -/// Note that this does not include the `--` prefix -pub type Name = Atom; - -/// Parse a custom property name. -/// -/// -pub fn parse_name(s: &str) -> Result<&str, ()> { - if s.starts_with("--") && s.len() > 2 { - Ok(&s[2..]) - } else { - Err(()) - } -} - -/// A value for a custom property is just a set of tokens. -/// -/// We preserve the original CSS for serialization, and also the variable -/// references to other custom property names. -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] -pub struct VariableValue { - css: String, - - first_token_type: TokenSerializationType, - last_token_type: TokenSerializationType, - - /// Whether a variable value has a reference to an environment variable. - /// - /// If this is the case, we need to perform variable substitution on the - /// value. - references_environment: bool, - - /// Custom property names in var() functions. - references: Box<[Name]>, -} - -impl ToCss for SpecifiedValue { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str(&self.css) - } -} - -/// A map from CSS variable names to CSS variable computed values, used for -/// resolving. -/// -/// A consistent ordering is required for CSSDeclaration objects in the -/// DOM. CSSDeclarations expose property names as indexed properties, which -/// need to be stable. So we keep an array of property names which order is -/// determined on the order that they are added to the name-value map. -/// -/// The variable values are guaranteed to not have references to other -/// properties. -pub type CustomPropertiesMap = - IndexMap, BuildHasherDefault>; - -/// Both specified and computed values are VariableValues, the difference is -/// whether var() functions are expanded. -pub type SpecifiedValue = VariableValue; -/// Both specified and computed values are VariableValues, the difference is -/// whether var() functions are expanded. -pub type ComputedValue = VariableValue; - -/// A struct holding information about the external references to that a custom -/// property value may have. -#[derive(Default)] -struct VarOrEnvReferences { - custom_property_references: PrecomputedHashSet, - references_environment: bool, -} - -impl VariableValue { - fn empty() -> Self { - Self { - css: String::new(), - last_token_type: TokenSerializationType::nothing(), - first_token_type: TokenSerializationType::nothing(), - references: Default::default(), - references_environment: false, - } - } - - fn push<'i>( - &mut self, - input: &Parser<'i, '_>, - css: &str, - css_first_token_type: TokenSerializationType, - css_last_token_type: TokenSerializationType, - ) -> Result<(), ParseError<'i>> { - /// Prevent values from getting terribly big since you can use custom - /// properties exponentially. - /// - /// This number (2MB) is somewhat arbitrary, but silly enough that no - /// reasonable page should hit it. We could limit by number of total - /// substitutions, but that was very easy to work around in practice - /// (just choose a larger initial value and boom). - const MAX_VALUE_LENGTH_IN_BYTES: usize = 2 * 1024 * 1024; - - if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - // This happens e.g. between two subsequent var() functions: - // `var(--a)var(--b)`. - // - // In that case, css_*_token_type is nonsensical. - if css.is_empty() { - return Ok(()); - } - - self.first_token_type.set_if_nothing(css_first_token_type); - // If self.first_token_type was nothing, - // self.last_token_type is also nothing and this will be false: - if self - .last_token_type - .needs_separator_when_before(css_first_token_type) - { - self.css.push_str("/**/") - } - self.css.push_str(css); - self.last_token_type = css_last_token_type; - Ok(()) - } - - fn push_from<'i>( - &mut self, - input: &Parser<'i, '_>, - position: (SourcePosition, TokenSerializationType), - last_token_type: TokenSerializationType, - ) -> Result<(), ParseError<'i>> { - self.push( - input, - input.slice_from(position.0), - position.1, - last_token_type, - ) - } - - fn push_variable<'i>( - &mut self, - input: &Parser<'i, '_>, - variable: &ComputedValue, - ) -> Result<(), ParseError<'i>> { - debug_assert!(variable.references.is_empty()); - self.push( - input, - &variable.css, - variable.first_token_type, - variable.last_token_type, - ) - } - - /// Parse a custom property value. - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result, ParseError<'i>> { - let mut references = VarOrEnvReferences::default(); - - let (first_token_type, css, last_token_type) = - parse_self_contained_declaration_value(input, Some(&mut references))?; - - let custom_property_references = references - .custom_property_references - .into_iter() - .collect::>() - .into_boxed_slice(); - - let mut css = css.into_owned(); - css.shrink_to_fit(); - - Ok(Arc::new(VariableValue { - css, - first_token_type, - last_token_type, - references: custom_property_references, - references_environment: references.references_environment, - })) - } - - /// Create VariableValue from an int. - fn integer(number: i32) -> Self { - Self::from_token(Token::Number { - has_sign: false, - value: number as f32, - int_value: Some(number), - }) - } - - /// Create VariableValue from an int. - fn ident(ident: &'static str) -> Self { - Self::from_token(Token::Ident(ident.into())) - } - - /// Create VariableValue from a float amount of CSS pixels. - fn pixels(number: f32) -> Self { - // FIXME (https://github.com/servo/rust-cssparser/issues/266): - // No way to get TokenSerializationType::Dimension without creating - // Token object. - Self::from_token(Token::Dimension { - has_sign: false, - value: number, - int_value: None, - unit: CowRcStr::from("px"), - }) - } - - /// Create VariableValue from an integer amount of CSS pixels. - fn int_pixels(number: i32) -> Self { - Self::from_token(Token::Dimension { - has_sign: false, - value: number as f32, - int_value: Some(number), - unit: CowRcStr::from("px"), - }) - } - - fn from_token(token: Token) -> Self { - let token_type = token.serialization_type(); - let mut css = token.to_css_string(); - css.shrink_to_fit(); - - VariableValue { - css, - first_token_type: token_type, - last_token_type: token_type, - references: Default::default(), - references_environment: false, - } - } -} - -/// Parse the value of a non-custom property that contains `var()` references. -pub fn parse_non_custom_with_var<'i, 't>( - input: &mut Parser<'i, 't>, -) -> Result<(TokenSerializationType, Cow<'i, str>), ParseError<'i>> { - let (first_token_type, css, _) = parse_self_contained_declaration_value(input, None)?; - Ok((first_token_type, css)) -} - -fn parse_self_contained_declaration_value<'i, 't>( - input: &mut Parser<'i, 't>, - references: Option<&mut VarOrEnvReferences>, -) -> Result<(TokenSerializationType, Cow<'i, str>, TokenSerializationType), ParseError<'i>> { - let start_position = input.position(); - let mut missing_closing_characters = String::new(); - let (first, last) = - parse_declaration_value(input, references, &mut missing_closing_characters)?; - let mut css: Cow = input.slice_from(start_position).into(); - if !missing_closing_characters.is_empty() { - // Unescaped backslash at EOF in a quoted string is ignored. - if css.ends_with("\\") && matches!(missing_closing_characters.as_bytes()[0], b'"' | b'\'') { - css.to_mut().pop(); - } - css.to_mut().push_str(&missing_closing_characters); - } - Ok((first, css, last)) -} - -/// -fn parse_declaration_value<'i, 't>( - input: &mut Parser<'i, 't>, - references: Option<&mut VarOrEnvReferences>, - missing_closing_characters: &mut String, -) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { - input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| { - parse_declaration_value_block(input, references, missing_closing_characters) - }) -} - -/// Like parse_declaration_value, but accept `!` and `;` since they are only -/// invalid at the top level -fn parse_declaration_value_block<'i, 't>( - input: &mut Parser<'i, 't>, - mut references: Option<&mut VarOrEnvReferences>, - missing_closing_characters: &mut String, -) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { - input.skip_whitespace(); - let mut token_start = input.position(); - let mut token = match input.next_including_whitespace_and_comments() { - Ok(token) => token, - Err(_) => { - return Ok(( - TokenSerializationType::nothing(), - TokenSerializationType::nothing(), - )); - }, - }; - let first_token_type = token.serialization_type(); - loop { - macro_rules! nested { - () => { - input.parse_nested_block(|input| { - parse_declaration_value_block( - input, - references.as_mut().map(|r| &mut **r), - missing_closing_characters, - ) - })? - }; - } - macro_rules! check_closed { - ($closing:expr) => { - if !input.slice_from(token_start).ends_with($closing) { - missing_closing_characters.push_str($closing) - } - }; - } - let last_token_type = match *token { - Token::Comment(_) => { - let serialization_type = token.serialization_type(); - let token_slice = input.slice_from(token_start); - if !token_slice.ends_with("*/") { - missing_closing_characters.push_str(if token_slice.ends_with('*') { - "/" - } else { - "*/" - }) - } - serialization_type - }, - Token::BadUrl(ref u) => { - let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone()); - return Err(input.new_custom_error(e)); - }, - Token::BadString(ref s) => { - let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone()); - return Err(input.new_custom_error(e)); - }, - Token::CloseParenthesis => { - let e = StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock; - return Err(input.new_custom_error(e)); - }, - Token::CloseSquareBracket => { - let e = StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock; - return Err(input.new_custom_error(e)); - }, - Token::CloseCurlyBracket => { - let e = StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock; - return Err(input.new_custom_error(e)); - }, - Token::Function(ref name) => { - if name.eq_ignore_ascii_case("var") { - let args_start = input.state(); - input.parse_nested_block(|input| { - parse_var_function(input, references.as_mut().map(|r| &mut **r)) - })?; - input.reset(&args_start); - } else if name.eq_ignore_ascii_case("env") { - let args_start = input.state(); - input.parse_nested_block(|input| { - parse_env_function(input, references.as_mut().map(|r| &mut **r)) - })?; - input.reset(&args_start); - } - nested!(); - check_closed!(")"); - Token::CloseParenthesis.serialization_type() - }, - Token::ParenthesisBlock => { - nested!(); - check_closed!(")"); - Token::CloseParenthesis.serialization_type() - }, - Token::CurlyBracketBlock => { - nested!(); - check_closed!("}"); - Token::CloseCurlyBracket.serialization_type() - }, - Token::SquareBracketBlock => { - nested!(); - check_closed!("]"); - Token::CloseSquareBracket.serialization_type() - }, - Token::QuotedString(_) => { - let serialization_type = token.serialization_type(); - let token_slice = input.slice_from(token_start); - let quote = &token_slice[..1]; - debug_assert!(matches!(quote, "\"" | "'")); - if !(token_slice.ends_with(quote) && token_slice.len() > 1) { - missing_closing_characters.push_str(quote) - } - serialization_type - }, - Token::Ident(ref value) | - Token::AtKeyword(ref value) | - Token::Hash(ref value) | - Token::IDHash(ref value) | - Token::UnquotedUrl(ref value) | - Token::Dimension { - unit: ref value, .. - } => { - let serialization_type = token.serialization_type(); - let is_unquoted_url = matches!(token, Token::UnquotedUrl(_)); - if value.ends_with("�") && input.slice_from(token_start).ends_with("\\") { - // Unescaped backslash at EOF in these contexts is interpreted as U+FFFD - // Check the value in case the final backslash was itself escaped. - // Serialize as escaped U+FFFD, which is also interpreted as U+FFFD. - // (Unescaped U+FFFD would also work, but removing the backslash is annoying.) - missing_closing_characters.push_str("�") - } - if is_unquoted_url { - check_closed!(")"); - } - serialization_type - }, - _ => token.serialization_type(), - }; - - token_start = input.position(); - token = match input.next_including_whitespace_and_comments() { - Ok(token) => token, - Err(..) => return Ok((first_token_type, last_token_type)), - }; - } -} - -fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> { - // Exclude `!` and `;` at the top level - // https://drafts.csswg.org/css-syntax/#typedef-declaration-value - input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| { - // Skip until the end. - while input.next_including_whitespace_and_comments().is_ok() {} - Ok(()) - }) -} - -// If the var function is valid, return Ok((custom_property_name, fallback)) -fn parse_var_function<'i, 't>( - input: &mut Parser<'i, 't>, - references: Option<&mut VarOrEnvReferences>, -) -> Result<(), ParseError<'i>> { - let name = input.expect_ident_cloned()?; - let name = parse_name(&name).map_err(|()| { - input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())) - })?; - if input.try_parse(|input| input.expect_comma()).is_ok() { - parse_fallback(input)?; - } - if let Some(refs) = references { - refs.custom_property_references.insert(Atom::from(name)); - } - Ok(()) -} - -fn parse_env_function<'i, 't>( - input: &mut Parser<'i, 't>, - references: Option<&mut VarOrEnvReferences>, -) -> Result<(), ParseError<'i>> { - // TODO(emilio): This should be per spec, but no other - // browser does that, see https://github.com/w3c/csswg-drafts/issues/3262. - input.expect_ident()?; - if input.try_parse(|input| input.expect_comma()).is_ok() { - parse_fallback(input)?; - } - if let Some(references) = references { - references.references_environment = true; - } - Ok(()) -} - -/// A struct that takes care of encapsulating the cascade process for custom -/// properties. -pub struct CustomPropertiesBuilder<'a> { - seen: PrecomputedHashSet<&'a Name>, - may_have_cycles: bool, - custom_properties: Option, - inherited: Option<&'a Arc>, - reverted: PrecomputedHashMap<&'a Name, (CascadePriority, bool)>, - device: &'a Device, -} - -impl<'a> CustomPropertiesBuilder<'a> { - /// Create a new builder, inheriting from a given custom properties map. - pub fn new(inherited: Option<&'a Arc>, device: &'a Device) -> Self { - Self { - seen: PrecomputedHashSet::default(), - reverted: Default::default(), - may_have_cycles: false, - custom_properties: None, - inherited, - device, - } - } - - /// Cascade a given custom property declaration. - pub fn cascade(&mut self, declaration: &'a CustomDeclaration, priority: CascadePriority) { - let CustomDeclaration { - ref name, - ref value, - } = *declaration; - - if let Some(&(reverted_priority, is_origin_revert)) = self.reverted.get(&name) { - if !reverted_priority.allows_when_reverted(&priority, is_origin_revert) { - return; - } - } - - let was_already_present = !self.seen.insert(name); - if was_already_present { - return; - } - - if !self.value_may_affect_style(name, value) { - return; - } - - if self.custom_properties.is_none() { - self.custom_properties = Some(match self.inherited { - Some(inherited) => (**inherited).clone(), - None => CustomPropertiesMap::default(), - }); - } - - let map = self.custom_properties.as_mut().unwrap(); - match *value { - CustomDeclarationValue::Value(ref unparsed_value) => { - let has_references = !unparsed_value.references.is_empty(); - self.may_have_cycles |= has_references; - - // If the variable value has no references and it has an - // environment variable here, perform substitution here instead - // of forcing a full traversal in `substitute_all` afterwards. - let value = if !has_references && unparsed_value.references_environment { - let result = substitute_references_in_value(unparsed_value, &map, &self.device); - match result { - Ok(new_value) => new_value, - Err(..) => { - map.remove(name); - return; - }, - } - } else { - (*unparsed_value).clone() - }; - map.insert(name.clone(), value); - }, - CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword { - CSSWideKeyword::RevertLayer | CSSWideKeyword::Revert => { - let origin_revert = keyword == CSSWideKeyword::Revert; - self.seen.remove(name); - self.reverted.insert(name, (priority, origin_revert)); - }, - CSSWideKeyword::Initial => { - map.remove(name); - }, - // handled in value_may_affect_style - CSSWideKeyword::Unset | CSSWideKeyword::Inherit => unreachable!(), - }, - } - } - - fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool { - match *value { - CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) | - CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => { - // Custom properties are inherited by default. So - // explicit 'inherit' or 'unset' means we can just use - // any existing value in the inherited CustomPropertiesMap. - return false; - }, - _ => {}, - } - - let existing_value = self - .custom_properties - .as_ref() - .and_then(|m| m.get(name)) - .or_else(|| self.inherited.and_then(|m| m.get(name))); - - match (existing_value, value) { - (None, &CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)) => { - // The initial value of a custom property is the same as it - // not existing in the map. - return false; - }, - (Some(existing_value), &CustomDeclarationValue::Value(ref value)) => { - // Don't bother overwriting an existing inherited value with - // the same specified value. - if existing_value == value { - return false; - } - }, - _ => {}, - } - - true - } - - fn inherited_properties_match(&self, map: &CustomPropertiesMap) -> bool { - let inherited = match self.inherited { - Some(inherited) => inherited, - None => return false, - }; - if inherited.len() != map.len() { - return false; - } - for name in self.seen.iter() { - if inherited.get(*name) != map.get(*name) { - return false; - } - } - true - } - - /// Returns the final map of applicable custom properties. - /// - /// If there was any specified property, we've created a new map and now we - /// need to remove any potential cycles, and wrap it in an arc. - /// - /// Otherwise, just use the inherited custom properties map. - pub fn build(mut self) -> Option> { - let mut map = match self.custom_properties.take() { - Some(m) => m, - None => return self.inherited.cloned(), - }; - - if self.may_have_cycles { - substitute_all(&mut map, &self.seen, self.device); - } - - // Some pages apply a lot of redundant custom properties, see e.g. - // bug 1758974 comment 5. Try to detect the case where the values - // haven't really changed, and save some memory by reusing the inherited - // map in that case. - if self.inherited_properties_match(&map) { - return self.inherited.cloned(); - } - - map.shrink_to_fit(); - Some(Arc::new(map)) - } -} - -/// Resolve all custom properties to either substituted, invalid, or unset -/// (meaning we should use the inherited value). -/// -/// It does cycle dependencies removal at the same time as substitution. -fn substitute_all( - custom_properties_map: &mut CustomPropertiesMap, - seen: &PrecomputedHashSet<&Name>, - device: &Device, -) { - // The cycle dependencies removal in this function is a variant - // of Tarjan's algorithm. It is mostly based on the pseudo-code - // listed in - // https://en.wikipedia.org/w/index.php? - // title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495 - - /// Struct recording necessary information for each variable. - #[derive(Debug)] - struct VarInfo { - /// The name of the variable. It will be taken to save addref - /// when the corresponding variable is popped from the stack. - /// This also serves as a mark for whether the variable is - /// currently in the stack below. - name: Option, - /// If the variable is in a dependency cycle, lowlink represents - /// a smaller index which corresponds to a variable in the same - /// strong connected component, which is known to be accessible - /// from this variable. It is not necessarily the root, though. - lowlink: usize, - } - /// Context struct for traversing the variable graph, so that we can - /// avoid referencing all the fields multiple times. - #[derive(Debug)] - struct Context<'a> { - /// Number of variables visited. This is used as the order index - /// when we visit a new unresolved variable. - count: usize, - /// The map from custom property name to its order index. - index_map: PrecomputedHashMap, - /// Information of each variable indexed by the order index. - var_info: SmallVec<[VarInfo; 5]>, - /// The stack of order index of visited variables. It contains - /// all unfinished strong connected components. - stack: SmallVec<[usize; 5]>, - map: &'a mut CustomPropertiesMap, - /// To resolve the environment to substitute `env()` variables. - device: &'a Device, - } - - /// This function combines the traversal for cycle removal and value - /// substitution. It returns either a signal None if this variable - /// has been fully resolved (to either having no reference or being - /// marked invalid), or the order index for the given name. - /// - /// When it returns, the variable corresponds to the name would be - /// in one of the following states: - /// * It is still in context.stack, which means it is part of an - /// potentially incomplete dependency circle. - /// * It has been removed from the map. It can be either that the - /// substitution failed, or it is inside a dependency circle. - /// When this function removes a variable from the map because - /// of dependency circle, it would put all variables in the same - /// strong connected component to the set together. - /// * It doesn't have any reference, because either this variable - /// doesn't have reference at all in specified value, or it has - /// been completely resolved. - /// * There is no such variable at all. - fn traverse<'a>(name: &Name, context: &mut Context<'a>) -> Option { - // Some shortcut checks. - let (name, value) = { - let value = context.map.get(name)?; - - // Nothing to resolve. - if value.references.is_empty() { - debug_assert!( - !value.references_environment, - "Should've been handled earlier" - ); - return None; - } - - // Whether this variable has been visited in this traversal. - let key; - match context.index_map.entry(name.clone()) { - Entry::Occupied(entry) => { - return Some(*entry.get()); - }, - Entry::Vacant(entry) => { - key = entry.key().clone(); - entry.insert(context.count); - }, - } - - // Hold a strong reference to the value so that we don't - // need to keep reference to context.map. - (key, value.clone()) - }; - - // Add new entry to the information table. - let index = context.count; - context.count += 1; - debug_assert_eq!(index, context.var_info.len()); - context.var_info.push(VarInfo { - name: Some(name), - lowlink: index, - }); - context.stack.push(index); - - let mut self_ref = false; - let mut lowlink = index; - for next in value.references.iter() { - let next_index = match traverse(next, context) { - Some(index) => index, - // There is nothing to do if the next variable has been - // fully resolved at this point. - None => { - continue; - }, - }; - let next_info = &context.var_info[next_index]; - if next_index > index { - // The next variable has a larger index than us, so it - // must be inserted in the recursive call above. We want - // to get its lowlink. - lowlink = cmp::min(lowlink, next_info.lowlink); - } else if next_index == index { - self_ref = true; - } else if next_info.name.is_some() { - // The next variable has a smaller order index and it is - // in the stack, so we are at the same component. - lowlink = cmp::min(lowlink, next_index); - } - } - - context.var_info[index].lowlink = lowlink; - if lowlink != index { - // This variable is in a loop, but it is not the root of - // this strong connected component. We simply return for - // now, and the root would remove it from the map. - // - // This cannot be removed from the map here, because - // otherwise the shortcut check at the beginning of this - // function would return the wrong value. - return Some(index); - } - - // This is the root of a strong-connected component. - let mut in_loop = self_ref; - let name; - loop { - let var_index = context - .stack - .pop() - .expect("The current variable should still be in stack"); - let var_info = &mut context.var_info[var_index]; - // We should never visit the variable again, so it's safe - // to take the name away, so that we don't do additional - // reference count. - let var_name = var_info - .name - .take() - .expect("Variable should not be poped from stack twice"); - if var_index == index { - name = var_name; - break; - } - // Anything here is in a loop which can traverse to the - // variable we are handling, so remove it from the map, it's invalid - // at computed-value time. - context.map.remove(&var_name); - in_loop = true; - } - if in_loop { - // This variable is in loop. Resolve to invalid. - context.map.remove(&name); - return None; - } - - // Now we have shown that this variable is not in a loop, and all of its - // dependencies should have been resolved. We can start substitution - // now. - let result = substitute_references_in_value(&value, &context.map, &context.device); - match result { - Ok(computed_value) => { - context.map.insert(name, computed_value); - }, - Err(..) => { - // This is invalid, reset it to the guaranteed-invalid value. - context.map.remove(&name); - }, - } - - // All resolved, so return the signal value. - None - } - - // Note that `seen` doesn't contain names inherited from our parent, but - // those can't have variable references (since we inherit the computed - // variables) so we don't want to spend cycles traversing them anyway. - for name in seen { - let mut context = Context { - count: 0, - index_map: PrecomputedHashMap::default(), - stack: SmallVec::new(), - var_info: SmallVec::new(), - map: custom_properties_map, - device, - }; - traverse(name, &mut context); - } -} - -/// Replace `var()` and `env()` functions in a pre-existing variable value. -fn substitute_references_in_value<'i>( - value: &'i VariableValue, - custom_properties: &CustomPropertiesMap, - device: &Device, -) -> Result, ParseError<'i>> { - debug_assert!(!value.references.is_empty() || value.references_environment); - - let mut input = ParserInput::new(&value.css); - let mut input = Parser::new(&mut input); - let mut position = (input.position(), value.first_token_type); - let mut computed_value = ComputedValue::empty(); - - let last_token_type = substitute_block( - &mut input, - &mut position, - &mut computed_value, - custom_properties, - device, - )?; - - computed_value.push_from(&input, position, last_token_type)?; - computed_value.css.shrink_to_fit(); - Ok(Arc::new(computed_value)) -} - -/// Replace `var()` functions in an arbitrary bit of input. -/// -/// If the variable has its initial value, the callback should return `Err(())` -/// and leave `partial_computed_value` unchanged. -/// -/// Otherwise, it should push the value of the variable (with its own `var()` functions replaced) -/// to `partial_computed_value` and return `Ok(last_token_type of what was pushed)` -/// -/// Return `Err(())` if `input` is invalid at computed-value time. -/// or `Ok(last_token_type that was pushed to partial_computed_value)` otherwise. -fn substitute_block<'i>( - input: &mut Parser<'i, '_>, - position: &mut (SourcePosition, TokenSerializationType), - partial_computed_value: &mut ComputedValue, - custom_properties: &CustomPropertiesMap, - device: &Device, -) -> Result> { - let mut last_token_type = TokenSerializationType::nothing(); - let mut set_position_at_next_iteration = false; - loop { - let before_this_token = input.position(); - let next = input.next_including_whitespace_and_comments(); - if set_position_at_next_iteration { - *position = ( - before_this_token, - match next { - Ok(token) => token.serialization_type(), - Err(_) => TokenSerializationType::nothing(), - }, - ); - set_position_at_next_iteration = false; - } - let token = match next { - Ok(token) => token, - Err(..) => break, - }; - match token { - Token::Function(ref name) - if name.eq_ignore_ascii_case("var") || name.eq_ignore_ascii_case("env") => - { - let is_env = name.eq_ignore_ascii_case("env"); - - partial_computed_value.push( - input, - input.slice(position.0..before_this_token), - position.1, - last_token_type, - )?; - input.parse_nested_block(|input| { - // parse_var_function() / parse_env_function() ensure neither .unwrap() will fail. - let name = { - let name = input.expect_ident().unwrap(); - if is_env { - Atom::from(&**name) - } else { - Atom::from(parse_name(&name).unwrap()) - } - }; - - let env_value; - let value = if is_env { - if let Some(v) = device.environment().get(&name, device) { - env_value = v; - Some(&env_value) - } else { - None - } - } else { - custom_properties.get(&name).map(|v| &**v) - }; - - if let Some(v) = value { - last_token_type = v.last_token_type; - partial_computed_value.push_variable(input, v)?; - // Skip over the fallback, as `parse_nested_block` would return `Err` - // if we don't consume all of `input`. - // FIXME: Add a specialized method to cssparser to do this with less work. - while input.next().is_ok() {} - } else { - input.expect_comma()?; - input.skip_whitespace(); - let after_comma = input.state(); - let first_token_type = input - .next_including_whitespace_and_comments() - .ok() - .map_or_else(TokenSerializationType::nothing, |t| { - t.serialization_type() - }); - input.reset(&after_comma); - let mut position = (after_comma.position(), first_token_type); - last_token_type = substitute_block( - input, - &mut position, - partial_computed_value, - custom_properties, - device, - )?; - partial_computed_value.push_from(input, position, last_token_type)?; - } - Ok(()) - })?; - set_position_at_next_iteration = true - }, - Token::Function(_) | - Token::ParenthesisBlock | - Token::CurlyBracketBlock | - Token::SquareBracketBlock => { - input.parse_nested_block(|input| { - substitute_block( - input, - position, - partial_computed_value, - custom_properties, - device, - ) - })?; - // It's the same type for CloseCurlyBracket and CloseSquareBracket. - last_token_type = Token::CloseParenthesis.serialization_type(); - }, - - _ => last_token_type = token.serialization_type(), - } - } - // FIXME: deal with things being implicitly closed at the end of the input. E.g. - // ```html - //
- //

- //
- // ``` - Ok(last_token_type) -} - -/// Replace `var()` and `env()` functions for a non-custom property. -/// -/// Return `Err(())` for invalid at computed time. -pub fn substitute<'i>( - input: &'i str, - first_token_type: TokenSerializationType, - computed_values_map: Option<&Arc>, - device: &Device, -) -> Result> { - let mut substituted = ComputedValue::empty(); - let mut input = ParserInput::new(input); - let mut input = Parser::new(&mut input); - let mut position = (input.position(), first_token_type); - let empty_map = CustomPropertiesMap::default(); - let custom_properties = match computed_values_map { - Some(m) => &**m, - None => &empty_map, - }; - let last_token_type = substitute_block( - &mut input, - &mut position, - &mut substituted, - &custom_properties, - device, - )?; - substituted.push_from(&input, position, last_token_type)?; - Ok(substituted.css) -} diff --git a/components/style/data.rs b/components/style/data.rs deleted file mode 100644 index 62dff225f8f..00000000000 --- a/components/style/data.rs +++ /dev/null @@ -1,545 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Per-node data used in style calculation. - -use crate::computed_value_flags::ComputedValueFlags; -use crate::context::{SharedStyleContext, StackLimitChecker}; -use crate::dom::TElement; -use crate::invalidation::element::invalidator::InvalidationResult; -use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::properties::ComputedValues; -use crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT}; -use crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle}; -#[cfg(feature = "gecko")] -use malloc_size_of::MallocSizeOfOps; -use selectors::NthIndexCache; -use servo_arc::Arc; -use std::fmt; -use std::mem; -use std::ops::{Deref, DerefMut}; - -bitflags! { - /// Various flags stored on ElementData. - #[derive(Default)] - pub struct ElementDataFlags: u8 { - /// Whether the styles changed for this restyle. - const WAS_RESTYLED = 1 << 0; - /// Whether the last traversal of this element did not do - /// any style computation. This is not true during the initial - /// styling pass, nor is it true when we restyle (in which case - /// WAS_RESTYLED is set). - /// - /// This bit always corresponds to the last time the element was - /// traversed, so each traversal simply updates it with the appropriate - /// value. - const TRAVERSED_WITHOUT_STYLING = 1 << 1; - - /// Whether the primary style of this element data was reused from - /// another element via a rule node comparison. This allows us to - /// differentiate between elements that shared styles because they met - /// all the criteria of the style sharing cache, compared to elements - /// that reused style structs via rule node identity. - /// - /// The former gives us stronger transitive guarantees that allows us to - /// apply the style sharing cache to cousins. - const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2; - } -} - -/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements. -/// -/// We use an Arc so that sharing these styles via the style sharing cache does -/// not require duplicate allocations. We leverage the copy-on-write semantics of -/// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations) -/// in servo_arc. -#[derive(Clone, Debug, Default)] -pub struct EagerPseudoStyles(Option>); - -#[derive(Default)] -struct EagerPseudoArray(EagerPseudoArrayInner); -type EagerPseudoArrayInner = [Option>; EAGER_PSEUDO_COUNT]; - -impl Deref for EagerPseudoArray { - type Target = EagerPseudoArrayInner; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for EagerPseudoArray { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -// Manually implement `Clone` here because the derived impl of `Clone` for -// array types assumes the value inside is `Copy`. -impl Clone for EagerPseudoArray { - fn clone(&self) -> Self { - let mut clone = Self::default(); - for i in 0..EAGER_PSEUDO_COUNT { - clone[i] = self.0[i].clone(); - } - clone - } -} - -// Override Debug to print which pseudos we have, and substitute the rule node -// for the much-more-verbose ComputedValues stringification. -impl fmt::Debug for EagerPseudoArray { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "EagerPseudoArray {{ ")?; - for i in 0..EAGER_PSEUDO_COUNT { - if let Some(ref values) = self[i] { - write!( - f, - "{:?}: {:?}, ", - PseudoElement::from_eager_index(i), - &values.rules - )?; - } - } - write!(f, "}}") - } -} - -// Can't use [None; EAGER_PSEUDO_COUNT] here because it complains -// about Copy not being implemented for our Arc type. -#[cfg(feature = "gecko")] -const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None]; -#[cfg(feature = "servo")] -const EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None]; - -impl EagerPseudoStyles { - /// Returns whether there are any pseudo styles. - pub fn is_empty(&self) -> bool { - self.0.is_none() - } - - /// Grabs a reference to the list of styles, if they exist. - pub fn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> { - match self.0 { - None => None, - Some(ref x) => Some(&x.0), - } - } - - /// Grabs a reference to the list of styles or a list of None if - /// there are no styles to be had. - pub fn as_array(&self) -> &EagerPseudoArrayInner { - self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY) - } - - /// Returns a reference to the style for a given eager pseudo, if it exists. - pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc> { - debug_assert!(pseudo.is_eager()); - self.0 - .as_ref() - .and_then(|p| p[pseudo.eager_index()].as_ref()) - } - - /// Sets the style for the eager pseudo. - pub fn set(&mut self, pseudo: &PseudoElement, value: Arc) { - if self.0.is_none() { - self.0 = Some(Arc::new(Default::default())); - } - let arr = Arc::make_mut(self.0.as_mut().unwrap()); - arr[pseudo.eager_index()] = Some(value); - } -} - -/// The styles associated with a node, including the styles for any -/// pseudo-elements. -#[derive(Clone, Default)] -pub struct ElementStyles { - /// The element's style. - pub primary: Option>, - /// A list of the styles for the element's eagerly-cascaded pseudo-elements. - pub pseudos: EagerPseudoStyles, -} - -// There's one of these per rendered elements so it better be small. -size_of_test!(ElementStyles, 16); - -/// Information on how this element uses viewport units. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum ViewportUnitUsage { - /// No viewport units are used. - None = 0, - /// There are viewport units used from regular style rules (which means we - /// should re-cascade). - FromDeclaration, - /// There are viewport units used from container queries (which means we - /// need to re-selector-match). - FromQuery, -} - -impl ElementStyles { - /// Returns the primary style. - pub fn get_primary(&self) -> Option<&Arc> { - self.primary.as_ref() - } - - /// Returns the primary style. Panic if no style available. - pub fn primary(&self) -> &Arc { - self.primary.as_ref().unwrap() - } - - /// Whether this element `display` value is `none`. - pub fn is_display_none(&self) -> bool { - self.primary().get_box().clone_display().is_none() - } - - /// Whether this element uses viewport units. - pub fn viewport_unit_usage(&self) -> ViewportUnitUsage { - fn usage_from_flags(flags: ComputedValueFlags) -> ViewportUnitUsage { - if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES) { - return ViewportUnitUsage::FromQuery; - } - if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) { - return ViewportUnitUsage::FromDeclaration; - } - ViewportUnitUsage::None - } - - let mut usage = usage_from_flags(self.primary().flags); - for pseudo_style in self.pseudos.as_array() { - if let Some(ref pseudo_style) = pseudo_style { - usage = std::cmp::max(usage, usage_from_flags(pseudo_style.flags)); - } - } - - usage - } - - #[cfg(feature = "gecko")] - fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize { - // As the method name suggests, we don't measures the ComputedValues - // here, because they are measured on the C++ side. - - // XXX: measure the EagerPseudoArray itself, but not the ComputedValues - // within it. - - 0 - } -} - -// We manually implement Debug for ElementStyles so that we can avoid the -// verbose stringification of every property in the ComputedValues. We -// substitute the rule node instead. -impl fmt::Debug for ElementStyles { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "ElementStyles {{ primary: {:?}, pseudos: {:?} }}", - self.primary.as_ref().map(|x| &x.rules), - self.pseudos - ) - } -} - -/// Style system data associated with an Element. -/// -/// In Gecko, this hangs directly off the Element. Servo, this is embedded -/// inside of layout data, which itself hangs directly off the Element. In -/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety. -#[derive(Debug, Default)] -pub struct ElementData { - /// The styles for the element and its pseudo-elements. - pub styles: ElementStyles, - - /// The restyle damage, indicating what kind of layout changes are required - /// afte restyling. - pub damage: RestyleDamage, - - /// The restyle hint, which indicates whether selectors need to be rematched - /// for this element, its children, and its descendants. - pub hint: RestyleHint, - - /// Flags. - pub flags: ElementDataFlags, -} - -// There's one of these per rendered elements so it better be small. -size_of_test!(ElementData, 24); - -/// The kind of restyle that a single element should do. -#[derive(Debug)] -pub enum RestyleKind { - /// We need to run selector matching plus re-cascade, that is, a full - /// restyle. - MatchAndCascade, - /// We need to recascade with some replacement rule, such as the style - /// attribute, or animation rules. - CascadeWithReplacements(RestyleHint), - /// We only need to recascade, for example, because only inherited - /// properties in the parent changed. - CascadeOnly, -} - -impl ElementData { - /// Invalidates style for this element, its descendants, and later siblings, - /// based on the snapshot of the element that we took when attributes or - /// state changed. - pub fn invalidate_style_if_needed<'a, E: TElement>( - &mut self, - element: E, - shared_context: &SharedStyleContext, - stack_limit_checker: Option<&StackLimitChecker>, - nth_index_cache: &mut NthIndexCache, - ) -> InvalidationResult { - // In animation-only restyle we shouldn't touch snapshot at all. - if shared_context.traversal_flags.for_animation_only() { - return InvalidationResult::empty(); - } - - use crate::invalidation::element::invalidator::TreeStyleInvalidator; - use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor; - - debug!( - "invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \ - handled_snapshot: {}, pseudo: {:?}", - element, - shared_context.traversal_flags, - element.has_snapshot(), - element.handled_snapshot(), - element.implemented_pseudo_element() - ); - - if !element.has_snapshot() || element.handled_snapshot() { - return InvalidationResult::empty(); - } - - let mut processor = - StateAndAttrInvalidationProcessor::new(shared_context, element, self, nth_index_cache); - - let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor); - - let result = invalidator.invalidate(); - - unsafe { element.set_handled_snapshot() } - debug_assert!(element.handled_snapshot()); - - result - } - - /// Returns true if this element has styles. - #[inline] - pub fn has_styles(&self) -> bool { - self.styles.primary.is_some() - } - - /// Returns this element's styles as resolved styles to use for sharing. - pub fn share_styles(&self) -> ResolvedElementStyles { - ResolvedElementStyles { - primary: self.share_primary_style(), - pseudos: self.styles.pseudos.clone(), - } - } - - /// Returns this element's primary style as a resolved style to use for sharing. - pub fn share_primary_style(&self) -> PrimaryStyle { - let reused_via_rule_node = self - .flags - .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE); - - PrimaryStyle { - style: ResolvedStyle(self.styles.primary().clone()), - reused_via_rule_node, - } - } - - /// Sets a new set of styles, returning the old ones. - pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles { - if new_styles.primary.reused_via_rule_node { - self.flags - .insert(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE); - } else { - self.flags - .remove(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE); - } - mem::replace(&mut self.styles, new_styles.into()) - } - - /// Returns the kind of restyling that we're going to need to do on this - /// element, based of the stored restyle hint. - pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option { - if shared_context.traversal_flags.for_animation_only() { - return self.restyle_kind_for_animation(shared_context); - } - - let style = match self.styles.primary { - Some(ref s) => s, - None => return Some(RestyleKind::MatchAndCascade), - }; - - let hint = self.hint; - if hint.is_empty() { - return None; - } - - let needs_to_match_self = hint.intersects(RestyleHint::RESTYLE_SELF) || - (hint.intersects(RestyleHint::RESTYLE_SELF_IF_PSEUDO) && style.is_pseudo_style()); - if needs_to_match_self { - return Some(RestyleKind::MatchAndCascade); - } - - if hint.has_replacements() { - debug_assert!( - !hint.has_animation_hint(), - "Animation only restyle hint should have already processed" - ); - return Some(RestyleKind::CascadeWithReplacements( - hint & RestyleHint::replacements(), - )); - } - - let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF) || - (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE) && - style - .flags - .contains(ComputedValueFlags::INHERITS_RESET_STYLE)); - if needs_to_recascade_self { - return Some(RestyleKind::CascadeOnly); - } - - None - } - - /// Returns the kind of restyling for animation-only restyle. - fn restyle_kind_for_animation( - &self, - shared_context: &SharedStyleContext, - ) -> Option { - debug_assert!(shared_context.traversal_flags.for_animation_only()); - debug_assert!( - self.has_styles(), - "animation traversal doesn't care about unstyled elements" - ); - - // FIXME: We should ideally restyle here, but it is a hack to work around our weird - // animation-only traversal stuff: If we're display: none and the rules we could - // match could change, we consider our style up-to-date. This is because re-cascading with - // and old style doesn't guarantee returning the correct animation style (that's - // bug 1393323). So if our display changed, and it changed from display: none, we would - // incorrectly forget about it and wouldn't be able to correctly style our descendants - // later. - // XXX Figure out if this still makes sense. - let hint = self.hint; - if self.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) { - return None; - } - - let style = self.styles.primary(); - // Return either CascadeWithReplacements or CascadeOnly in case of - // animation-only restyle. I.e. animation-only restyle never does - // selector matching. - if hint.has_animation_hint() { - return Some(RestyleKind::CascadeWithReplacements( - hint & RestyleHint::for_animations(), - )); - } - - let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF) || - (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE) && - style - .flags - .contains(ComputedValueFlags::INHERITS_RESET_STYLE)); - if needs_to_recascade_self { - return Some(RestyleKind::CascadeOnly); - } - return None; - } - - /// Drops any restyle state from the element. - /// - /// FIXME(bholley): The only caller of this should probably just assert that - /// the hint is empty and call clear_flags_and_damage(). - #[inline] - pub fn clear_restyle_state(&mut self) { - self.hint = RestyleHint::empty(); - self.clear_restyle_flags_and_damage(); - } - - /// Drops restyle flags and damage from the element. - #[inline] - pub fn clear_restyle_flags_and_damage(&mut self) { - self.damage = RestyleDamage::empty(); - self.flags.remove(ElementDataFlags::WAS_RESTYLED); - } - - /// Mark this element as restyled, which is useful to know whether we need - /// to do a post-traversal. - pub fn set_restyled(&mut self) { - self.flags.insert(ElementDataFlags::WAS_RESTYLED); - self.flags - .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING); - } - - /// Returns true if this element was restyled. - #[inline] - pub fn is_restyle(&self) -> bool { - self.flags.contains(ElementDataFlags::WAS_RESTYLED) - } - - /// Mark that we traversed this element without computing any style for it. - pub fn set_traversed_without_styling(&mut self) { - self.flags - .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING); - } - - /// Returns whether this element has been part of a restyle. - #[inline] - pub fn contains_restyle_data(&self) -> bool { - self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty() - } - - /// Returns whether it is safe to perform cousin sharing based on the ComputedValues - /// identity of the primary style in this ElementData. There are a few subtle things - /// to check. - /// - /// First, if a parent element was already styled and we traversed past it without - /// restyling it, that may be because our clever invalidation logic was able to prove - /// that the styles of that element would remain unchanged despite changes to the id - /// or class attributes. However, style sharing relies on the strong guarantee that all - /// the classes and ids up the respective parent chains are identical. As such, if we - /// skipped styling for one (or both) of the parents on this traversal, we can't share - /// styles across cousins. Note that this is a somewhat conservative check. We could - /// tighten it by having the invalidation logic explicitly flag elements for which it - /// ellided styling. - /// - /// Second, we want to only consider elements whose ComputedValues match due to a hit - /// in the style sharing cache, rather than due to the rule-node-based reuse that - /// happens later in the styling pipeline. The former gives us the stronger guarantees - /// we need for style sharing, the latter does not. - pub fn safe_for_cousin_sharing(&self) -> bool { - if self.flags.intersects( - ElementDataFlags::TRAVERSED_WITHOUT_STYLING | - ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE, - ) { - return false; - } - if !self - .styles - .primary() - .get_box() - .clone_container_type() - .is_normal() - { - return false; - } - true - } - - /// Measures memory usage. - #[cfg(feature = "gecko")] - pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize { - let n = self.styles.size_of_excluding_cvs(ops); - - // We may measure more fields in the future if DMD says it's worth it. - - n - } -} diff --git a/components/style/dom.rs b/components/style/dom.rs deleted file mode 100644 index 7fc29280e69..00000000000 --- a/components/style/dom.rs +++ /dev/null @@ -1,940 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Types and traits used to access the DOM from style calculation. - -#![allow(unsafe_code)] -#![deny(missing_docs)] - -use crate::applicable_declarations::ApplicableDeclarationBlock; -use crate::context::SharedStyleContext; -#[cfg(feature = "gecko")] -use crate::context::{PostAnimationTasks, UpdateAnimationsTasks}; -use crate::data::ElementData; -use crate::media_queries::Device; -use crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock}; -use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl}; -use crate::shared_lock::{Locked, SharedRwLock}; -use crate::stylist::CascadeData; -use crate::values::computed::Display; -use crate::values::AtomIdent; -use crate::{LocalName, WeakAtom}; -use atomic_refcell::{AtomicRef, AtomicRefMut}; -use selectors::matching::{QuirksMode, VisitedHandlingMode}; -use selectors::sink::Push; -use selectors::Element as SelectorsElement; -use servo_arc::{Arc, ArcBorrow}; -use std::fmt; -use std::fmt::Debug; -use std::hash::Hash; -use std::ops::Deref; -use style_traits::dom::ElementState; - -pub use style_traits::dom::OpaqueNode; - -/// Simple trait to provide basic information about the type of an element. -/// -/// We avoid exposing the full type id, since computing it in the general case -/// would be difficult for Gecko nodes. -pub trait NodeInfo { - /// Whether this node is an element. - fn is_element(&self) -> bool; - /// Whether this node is a text node. - fn is_text_node(&self) -> bool; -} - -/// A node iterator that only returns node that don't need layout. -pub struct LayoutIterator(pub T); - -impl Iterator for LayoutIterator -where - T: Iterator, - N: NodeInfo, -{ - type Item = N; - - fn next(&mut self) -> Option { - loop { - let n = self.0.next()?; - // Filter out nodes that layout should ignore. - if n.is_text_node() || n.is_element() { - return Some(n); - } - } - } -} - -/// An iterator over the DOM children of a node. -pub struct DomChildren(Option); -impl Iterator for DomChildren -where - N: TNode, -{ - type Item = N; - - fn next(&mut self) -> Option { - let n = self.0.take()?; - self.0 = n.next_sibling(); - Some(n) - } -} - -/// An iterator over the DOM descendants of a node in pre-order. -pub struct DomDescendants { - previous: Option, - scope: N, -} - -impl Iterator for DomDescendants -where - N: TNode, -{ - type Item = N; - - #[inline] - fn next(&mut self) -> Option { - let prev = self.previous.take()?; - self.previous = prev.next_in_preorder(self.scope); - self.previous - } -} - -/// The `TDocument` trait, to represent a document node. -pub trait TDocument: Sized + Copy + Clone { - /// The concrete `TNode` type. - type ConcreteNode: TNode; - - /// Get this document as a `TNode`. - fn as_node(&self) -> Self::ConcreteNode; - - /// Returns whether this document is an HTML document. - fn is_html_document(&self) -> bool; - - /// Returns the quirks mode of this document. - fn quirks_mode(&self) -> QuirksMode; - - /// Get a list of elements with a given ID in this document, sorted by - /// tree position. - /// - /// Can return an error to signal that this list is not available, or also - /// return an empty slice. - fn elements_with_id<'a>( - &self, - _id: &AtomIdent, - ) -> Result<&'a [::ConcreteElement], ()> - where - Self: 'a, - { - Err(()) - } - - /// This document's shared lock. - fn shared_lock(&self) -> &SharedRwLock; -} - -/// The `TNode` trait. This is the main generic trait over which the style -/// system can be implemented. -pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq { - /// The concrete `TElement` type. - type ConcreteElement: TElement; - - /// The concrete `TDocument` type. - type ConcreteDocument: TDocument; - - /// The concrete `TShadowRoot` type. - type ConcreteShadowRoot: TShadowRoot; - - /// Get this node's parent node. - fn parent_node(&self) -> Option; - - /// Get this node's first child. - fn first_child(&self) -> Option; - - /// Get this node's last child. - fn last_child(&self) -> Option; - - /// Get this node's previous sibling. - fn prev_sibling(&self) -> Option; - - /// Get this node's next sibling. - fn next_sibling(&self) -> Option; - - /// Get the owner document of this node. - fn owner_doc(&self) -> Self::ConcreteDocument; - - /// Iterate over the DOM children of a node. - #[inline(always)] - fn dom_children(&self) -> DomChildren { - DomChildren(self.first_child()) - } - - /// Returns whether the node is attached to a document. - fn is_in_document(&self) -> bool; - - /// Iterate over the DOM children of a node, in preorder. - #[inline(always)] - fn dom_descendants(&self) -> DomDescendants { - DomDescendants { - previous: Some(*self), - scope: *self, - } - } - - /// Returns the next node after this one, in a pre-order tree-traversal of - /// the subtree rooted at scoped_to. - #[inline] - fn next_in_preorder(&self, scoped_to: Self) -> Option { - if let Some(c) = self.first_child() { - return Some(c); - } - - let mut current = *self; - loop { - if current == scoped_to { - return None; - } - - if let Some(s) = current.next_sibling() { - return Some(s); - } - - debug_assert!( - current.parent_node().is_some(), - "Not a descendant of the scope?" - ); - current = current.parent_node()?; - } - } - - /// Get this node's parent element from the perspective of a restyle - /// traversal. - fn traversal_parent(&self) -> Option; - - /// Get this node's parent element if present. - fn parent_element(&self) -> Option { - self.parent_node().and_then(|n| n.as_element()) - } - - /// Get this node's parent element, or shadow host if it's a shadow root. - fn parent_element_or_host(&self) -> Option { - let parent = self.parent_node()?; - if let Some(e) = parent.as_element() { - return Some(e); - } - if let Some(root) = parent.as_shadow_root() { - return Some(root.host()); - } - None - } - - /// Converts self into an `OpaqueNode`. - fn opaque(&self) -> OpaqueNode; - - /// A debug id, only useful, mm... for debugging. - fn debug_id(self) -> usize; - - /// Get this node as an element, if it's one. - fn as_element(&self) -> Option; - - /// Get this node as a document, if it's one. - fn as_document(&self) -> Option; - - /// Get this node as a ShadowRoot, if it's one. - fn as_shadow_root(&self) -> Option; -} - -/// Wrapper to output the subtree rather than the single node when formatting -/// for Debug. -pub struct ShowSubtree(pub N); -impl Debug for ShowSubtree { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "DOM Subtree:")?; - fmt_subtree(f, &|f, n| write!(f, "{:?}", n), self.0, 1) - } -} - -/// Wrapper to output the subtree along with the ElementData when formatting -/// for Debug. -pub struct ShowSubtreeData(pub N); -impl Debug for ShowSubtreeData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "DOM Subtree:")?; - fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1) - } -} - -/// Wrapper to output the subtree along with the ElementData and primary -/// ComputedValues when formatting for Debug. This is extremely verbose. -#[cfg(feature = "servo")] -pub struct ShowSubtreeDataAndPrimaryValues(pub N); -#[cfg(feature = "servo")] -impl Debug for ShowSubtreeDataAndPrimaryValues { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "DOM Subtree:")?; - fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1) - } -} - -fn fmt_with_data(f: &mut fmt::Formatter, n: N) -> fmt::Result { - if let Some(el) = n.as_element() { - write!( - f, - "{:?} dd={} aodd={} data={:?}", - el, - el.has_dirty_descendants(), - el.has_animation_only_dirty_descendants(), - el.borrow_data(), - ) - } else { - write!(f, "{:?}", n) - } -} - -#[cfg(feature = "servo")] -fn fmt_with_data_and_primary_values(f: &mut fmt::Formatter, n: N) -> fmt::Result { - if let Some(el) = n.as_element() { - let dd = el.has_dirty_descendants(); - let aodd = el.has_animation_only_dirty_descendants(); - let data = el.borrow_data(); - let values = data.as_ref().and_then(|d| d.styles.get_primary()); - write!( - f, - "{:?} dd={} aodd={} data={:?} values={:?}", - el, dd, aodd, &data, values - ) - } else { - write!(f, "{:?}", n) - } -} - -fn fmt_subtree(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32) -> fmt::Result -where - F: Fn(&mut fmt::Formatter, N) -> fmt::Result, -{ - for _ in 0..indent { - write!(f, " ")?; - } - stringify(f, n)?; - if let Some(e) = n.as_element() { - for kid in e.traversal_children() { - writeln!(f, "")?; - fmt_subtree(f, stringify, kid, indent + 1)?; - } - } - - Ok(()) -} - -/// The ShadowRoot trait. -pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq { - /// The concrete node type. - type ConcreteNode: TNode; - - /// Get this ShadowRoot as a node. - fn as_node(&self) -> Self::ConcreteNode; - - /// Get the shadow host that hosts this ShadowRoot. - fn host(&self) -> ::ConcreteElement; - - /// Get the style data for this ShadowRoot. - fn style_data<'a>(&self) -> Option<&'a CascadeData> - where - Self: 'a; - - /// Get the list of shadow parts for this shadow root. - fn parts<'a>(&self) -> &[::ConcreteElement] - where - Self: 'a, - { - &[] - } - - /// Get a list of elements with a given ID in this shadow root, sorted by - /// tree position. - /// - /// Can return an error to signal that this list is not available, or also - /// return an empty slice. - fn elements_with_id<'a>( - &self, - _id: &AtomIdent, - ) -> Result<&'a [::ConcreteElement], ()> - where - Self: 'a, - { - Err(()) - } -} - -/// The element trait, the main abstraction the style crate acts over. -pub trait TElement: - Eq + PartialEq + Debug + Hash + Sized + Copy + Clone + SelectorsElement -{ - /// The concrete node type. - type ConcreteNode: TNode; - - /// A concrete children iterator type in order to iterate over the `Node`s. - /// - /// TODO(emilio): We should eventually replace this with the `impl Trait` - /// syntax. - type TraversalChildrenIterator: Iterator; - - /// Get this element as a node. - fn as_node(&self) -> Self::ConcreteNode; - - /// A debug-only check that the device's owner doc matches the actual doc - /// we're the root of. - /// - /// Otherwise we may set document-level state incorrectly, like the root - /// font-size used for rem units. - fn owner_doc_matches_for_testing(&self, _: &Device) -> bool { - true - } - - /// Whether this element should match user and content rules. - /// - /// We use this for Native Anonymous Content in Gecko. - fn matches_user_and_content_rules(&self) -> bool { - true - } - - /// Returns the depth of this element in the DOM. - fn depth(&self) -> usize { - let mut depth = 0; - let mut curr = *self; - while let Some(parent) = curr.traversal_parent() { - depth += 1; - curr = parent; - } - - depth - } - - /// Get this node's parent element from the perspective of a restyle - /// traversal. - fn traversal_parent(&self) -> Option { - self.as_node().traversal_parent() - } - - /// Get this node's children from the perspective of a restyle traversal. - fn traversal_children(&self) -> LayoutIterator; - - /// Returns the parent element we should inherit from. - /// - /// This is pretty much always the parent element itself, except in the case - /// of Gecko's Native Anonymous Content, which uses the traversal parent - /// (i.e. the flattened tree parent) and which also may need to find the - /// closest non-NAC ancestor. - fn inheritance_parent(&self) -> Option { - self.parent_element() - } - - /// The ::before pseudo-element of this element, if it exists. - fn before_pseudo_element(&self) -> Option { - None - } - - /// The ::after pseudo-element of this element, if it exists. - fn after_pseudo_element(&self) -> Option { - None - } - - /// The ::marker pseudo-element of this element, if it exists. - fn marker_pseudo_element(&self) -> Option { - None - } - - /// Execute `f` for each anonymous content child (apart from ::before and - /// ::after) whose originating element is `self`. - fn each_anonymous_content_child(&self, _f: F) - where - F: FnMut(Self), - { - } - - /// Return whether this element is an element in the HTML namespace. - fn is_html_element(&self) -> bool; - - /// Return whether this element is an element in the MathML namespace. - fn is_mathml_element(&self) -> bool; - - /// Return whether this element is an element in the SVG namespace. - fn is_svg_element(&self) -> bool; - - /// Return whether this element is an element in the XUL namespace. - fn is_xul_element(&self) -> bool { - false - } - - /// Return the list of slotted nodes of this node. - fn slotted_nodes(&self) -> &[Self::ConcreteNode] { - &[] - } - - /// Get this element's style attribute. - fn style_attribute(&self) -> Option>>; - - /// Unset the style attribute's dirty bit. - /// Servo doesn't need to manage ditry bit for style attribute. - fn unset_dirty_style_attribute(&self) {} - - /// Get this element's SMIL override declarations. - fn smil_override(&self) -> Option>> { - None - } - - /// Get the combined animation and transition rules. - /// - /// FIXME(emilio): Is this really useful? - fn animation_declarations(&self, context: &SharedStyleContext) -> AnimationDeclarations { - if !self.may_have_animations() { - return Default::default(); - } - - AnimationDeclarations { - animations: self.animation_rule(context), - transitions: self.transition_rule(context), - } - } - - /// Get this element's animation rule. - fn animation_rule( - &self, - _: &SharedStyleContext, - ) -> Option>>; - - /// Get this element's transition rule. - fn transition_rule( - &self, - context: &SharedStyleContext, - ) -> Option>>; - - /// Get this element's state, for non-tree-structural pseudos. - fn state(&self) -> ElementState; - - /// Returns whether this element has a `part` attribute. - fn has_part_attr(&self) -> bool; - - /// Returns whether this element exports any part from its shadow tree. - fn exports_any_part(&self) -> bool; - - /// The ID for this element. - fn id(&self) -> Option<&WeakAtom>; - - /// Internal iterator for the classes of this element. - fn each_class(&self, callback: F) - where - F: FnMut(&AtomIdent); - - /// Internal iterator for the part names of this element. - fn each_part(&self, _callback: F) - where - F: FnMut(&AtomIdent), - { - } - - /// Internal iterator for the attribute names of this element. - fn each_attr_name(&self, callback: F) - where - F: FnMut(&LocalName); - - /// Internal iterator for the part names that this element exports for a - /// given part name. - fn each_exported_part(&self, _name: &AtomIdent, _callback: F) - where - F: FnMut(&AtomIdent), - { - } - - /// Whether a given element may generate a pseudo-element. - /// - /// This is useful to avoid computing, for example, pseudo styles for - /// `::-first-line` or `::-first-letter`, when we know it won't affect us. - /// - /// TODO(emilio, bz): actually implement the logic for it. - fn may_generate_pseudo(&self, pseudo: &PseudoElement, _primary_style: &ComputedValues) -> bool { - // ::before/::after are always supported for now, though we could try to - // optimize out leaf elements. - - // ::first-letter and ::first-line are only supported for block-inside - // things, and only in Gecko, not Servo. Unfortunately, Gecko has - // block-inside things that might have any computed display value due to - // things like fieldsets, legends, etc. Need to figure out how this - // should work. - debug_assert!( - pseudo.is_eager(), - "Someone called may_generate_pseudo with a non-eager pseudo." - ); - true - } - - /// Returns true if this element may have a descendant needing style processing. - /// - /// Note that we cannot guarantee the existence of such an element, because - /// it may have been removed from the DOM between marking it for restyle and - /// the actual restyle traversal. - fn has_dirty_descendants(&self) -> bool; - - /// Returns whether state or attributes that may change style have changed - /// on the element, and thus whether the element has been snapshotted to do - /// restyle hint computation. - fn has_snapshot(&self) -> bool; - - /// Returns whether the current snapshot if present has been handled. - fn handled_snapshot(&self) -> bool; - - /// Flags this element as having handled already its snapshot. - unsafe fn set_handled_snapshot(&self); - - /// Returns whether the element's styles are up-to-date after traversal - /// (i.e. in post traversal). - fn has_current_styles(&self, data: &ElementData) -> bool { - if self.has_snapshot() && !self.handled_snapshot() { - return false; - } - - data.has_styles() && - // TODO(hiro): When an animating element moved into subtree of - // contenteditable element, there remains animation restyle hints in - // post traversal. It's generally harmless since the hints will be - // processed in a next styling but ideally it should be processed soon. - // - // Without this, we get failures in: - // layout/style/crashtests/1383319.html - // layout/style/crashtests/1383001.html - // - // https://bugzilla.mozilla.org/show_bug.cgi?id=1389675 tracks fixing - // this. - !data.hint.has_non_animation_invalidations() - } - - /// Flag that this element has a descendant for style processing. - /// - /// Only safe to call with exclusive access to the element. - unsafe fn set_dirty_descendants(&self); - - /// Flag that this element has no descendant for style processing. - /// - /// Only safe to call with exclusive access to the element. - unsafe fn unset_dirty_descendants(&self); - - /// Similar to the dirty_descendants but for representing a descendant of - /// the element needs to be updated in animation-only traversal. - fn has_animation_only_dirty_descendants(&self) -> bool { - false - } - - /// Flag that this element has a descendant for animation-only restyle - /// processing. - /// - /// Only safe to call with exclusive access to the element. - unsafe fn set_animation_only_dirty_descendants(&self) {} - - /// Flag that this element has no descendant for animation-only restyle processing. - /// - /// Only safe to call with exclusive access to the element. - unsafe fn unset_animation_only_dirty_descendants(&self) {} - - /// Clear all bits related describing the dirtiness of descendants. - /// - /// In Gecko, this corresponds to the regular dirty descendants bit, the - /// animation-only dirty descendants bit, and the lazy frame construction - /// descendants bit. - unsafe fn clear_descendant_bits(&self) { - self.unset_dirty_descendants(); - } - - /// Returns true if this element is a visited link. - /// - /// Servo doesn't support visited styles yet. - fn is_visited_link(&self) -> bool { - false - } - - /// Returns the pseudo-element implemented by this element, if any. - /// - /// Gecko traverses pseudo-elements during the style traversal, and we need - /// to know this so we can properly grab the pseudo-element style from the - /// parent element. - /// - /// Note that we still need to compute the pseudo-elements before-hand, - /// given otherwise we don't know if we need to create an element or not. - /// - /// Servo doesn't have to deal with this. - fn implemented_pseudo_element(&self) -> Option { - None - } - - /// Atomically stores the number of children of this node that we will - /// need to process during bottom-up traversal. - fn store_children_to_process(&self, n: isize); - - /// Atomically notes that a child has been processed during bottom-up - /// traversal. Returns the number of children left to process. - fn did_process_child(&self) -> isize; - - /// Gets a reference to the ElementData container, or creates one. - /// - /// Unsafe because it can race to allocate and leak if not used with - /// exclusive access to the element. - unsafe fn ensure_data(&self) -> AtomicRefMut; - - /// Clears the element data reference, if any. - /// - /// Unsafe following the same reasoning as ensure_data. - unsafe fn clear_data(&self); - - /// Whether there is an ElementData container. - fn has_data(&self) -> bool; - - /// Immutably borrows the ElementData. - fn borrow_data(&self) -> Option>; - - /// Mutably borrows the ElementData. - fn mutate_data(&self) -> Option>; - - /// Whether we should skip any root- or item-based display property - /// blockification on this element. (This function exists so that Gecko - /// native anonymous content can opt out of this style fixup.) - fn skip_item_display_fixup(&self) -> bool; - - /// In Gecko, element has a flag that represents the element may have - /// any type of animations or not to bail out animation stuff early. - /// Whereas Servo doesn't have such flag. - fn may_have_animations(&self) -> bool; - - /// Creates a task to update various animation state on a given (pseudo-)element. - #[cfg(feature = "gecko")] - fn update_animations( - &self, - before_change_style: Option>, - tasks: UpdateAnimationsTasks, - ); - - /// Creates a task to process post animation on a given element. - #[cfg(feature = "gecko")] - fn process_post_animation(&self, tasks: PostAnimationTasks); - - /// Returns true if the element has relevant animations. Relevant - /// animations are those animations that are affecting the element's style - /// or are scheduled to do so in the future. - fn has_animations(&self, context: &SharedStyleContext) -> bool; - - /// Returns true if the element has a CSS animation. The `context` and `pseudo_element` - /// arguments are only used by Servo, since it stores animations globally and pseudo-elements - /// are not in the DOM. - fn has_css_animations( - &self, - context: &SharedStyleContext, - pseudo_element: Option, - ) -> bool; - - /// Returns true if the element has a CSS transition (including running transitions and - /// completed transitions). The `context` and `pseudo_element` arguments are only used - /// by Servo, since it stores animations globally and pseudo-elements are not in the DOM. - fn has_css_transitions( - &self, - context: &SharedStyleContext, - pseudo_element: Option, - ) -> bool; - - /// Returns true if the element has animation restyle hints. - fn has_animation_restyle_hints(&self) -> bool { - let data = match self.borrow_data() { - Some(d) => d, - None => return false, - }; - return data.hint.has_animation_hint(); - } - - /// The shadow root this element is a host of. - fn shadow_root(&self) -> Option<::ConcreteShadowRoot>; - - /// The shadow root which roots the subtree this element is contained in. - fn containing_shadow(&self) -> Option<::ConcreteShadowRoot>; - - /// Return the element which we can use to look up rules in the selector - /// maps. - /// - /// This is always the element itself, except in the case where we are an - /// element-backed pseudo-element, in which case we return the originating - /// element. - fn rule_hash_target(&self) -> Self { - if self.is_pseudo_element() { - self.pseudo_element_originating_element() - .expect("Trying to collect rules for a detached pseudo-element") - } else { - *self - } - } - - /// Executes the callback for each applicable style rule data which isn't - /// the main document's data (which stores UA / author rules). - /// - /// The element passed to the callback is the containing shadow host for the - /// data if it comes from Shadow DOM. - /// - /// Returns whether normal document author rules should apply. - /// - /// TODO(emilio): We could separate the invalidation data for elements - /// matching in other scopes to avoid over-invalidation. - fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool - where - Self: 'a, - F: FnMut(&'a CascadeData, Self), - { - use crate::rule_collector::containing_shadow_ignoring_svg_use; - - let target = self.rule_hash_target(); - let matches_user_and_content_rules = target.matches_user_and_content_rules(); - let mut doc_rules_apply = matches_user_and_content_rules; - - // Use the same rules to look for the containing host as we do for rule - // collection. - if let Some(shadow) = containing_shadow_ignoring_svg_use(target) { - doc_rules_apply = false; - if let Some(data) = shadow.style_data() { - f(data, shadow.host()); - } - } - - if let Some(shadow) = target.shadow_root() { - if let Some(data) = shadow.style_data() { - f(data, shadow.host()); - } - } - - let mut current = target.assigned_slot(); - while let Some(slot) = current { - // Slots can only have assigned nodes when in a shadow tree. - let shadow = slot.containing_shadow().unwrap(); - if let Some(data) = shadow.style_data() { - if data.any_slotted_rule() { - f(data, shadow.host()); - } - } - current = slot.assigned_slot(); - } - - if target.has_part_attr() { - if let Some(mut inner_shadow) = target.containing_shadow() { - loop { - let inner_shadow_host = inner_shadow.host(); - match inner_shadow_host.containing_shadow() { - Some(shadow) => { - if let Some(data) = shadow.style_data() { - if data.any_part_rule() { - f(data, shadow.host()) - } - } - // TODO: Could be more granular. - if !inner_shadow_host.exports_any_part() { - break; - } - inner_shadow = shadow; - }, - None => { - // TODO(emilio): Should probably distinguish with - // MatchesDocumentRules::{No,Yes,IfPart} or something so that we could - // skip some work. - doc_rules_apply = matches_user_and_content_rules; - break; - }, - } - } - } - } - - doc_rules_apply - } - - /// Returns true if one of the transitions needs to be updated on this element. We check all - /// the transition properties to make sure that updating transitions is necessary. - /// This method should only be called if might_needs_transitions_update returns true when - /// passed the same parameters. - #[cfg(feature = "gecko")] - fn needs_transitions_update( - &self, - before_change_style: &ComputedValues, - after_change_style: &ComputedValues, - ) -> bool; - - /// Returns the value of the `xml:lang=""` attribute (or, if appropriate, - /// the `lang=""` attribute) on this element. - fn lang_attr(&self) -> Option; - - /// Returns whether this element's language matches the language tag - /// `value`. If `override_lang` is not `None`, it specifies the value - /// of the `xml:lang=""` or `lang=""` attribute to use in place of - /// looking at the element and its ancestors. (This argument is used - /// to implement matching of `:lang()` against snapshots.) - fn match_element_lang(&self, override_lang: Option>, value: &Lang) -> bool; - - /// Returns whether this element is the main body element of the HTML - /// document it is on. - fn is_html_document_body_element(&self) -> bool; - - /// Generate the proper applicable declarations due to presentational hints, - /// and insert them into `hints`. - fn synthesize_presentational_hints_for_legacy_attributes( - &self, - visited_handling: VisitedHandlingMode, - hints: &mut V, - ) where - V: Push; - - /// Returns element's local name. - fn local_name(&self) -> &::BorrowedLocalName; - - /// Returns element's namespace. - fn namespace(&self) - -> &::BorrowedNamespaceUrl; - - /// Returns the size of the element to be used in container size queries. - /// This will usually be the size of the content area of the primary box, - /// but can be None if there is no box or if some axis lacks size containment. - fn query_container_size( - &self, - display: &Display, - ) -> euclid::default::Size2D>; -} - -/// TNode and TElement aren't Send because we want to be careful and explicit -/// about our parallel traversal. However, there are certain situations -/// (including but not limited to the traversal) where we need to send DOM -/// objects to other threads. -/// -/// That's the reason why `SendNode` exists. -#[derive(Clone, Debug, PartialEq)] -pub struct SendNode(N); -unsafe impl Send for SendNode {} -impl SendNode { - /// Unsafely construct a SendNode. - pub unsafe fn new(node: N) -> Self { - SendNode(node) - } -} -impl Deref for SendNode { - type Target = N; - fn deref(&self) -> &N { - &self.0 - } -} - -/// Same reason as for the existence of SendNode, SendElement does the proper -/// things for a given `TElement`. -#[derive(Debug, Eq, Hash, PartialEq)] -pub struct SendElement(E); -unsafe impl Send for SendElement {} -impl SendElement { - /// Unsafely construct a SendElement. - pub unsafe fn new(el: E) -> Self { - SendElement(el) - } -} -impl Deref for SendElement { - type Target = E; - fn deref(&self) -> &E { - &self.0 - } -} diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs deleted file mode 100644 index da5dc387f73..00000000000 --- a/components/style/dom_apis.rs +++ /dev/null @@ -1,767 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Generic implementations of some DOM APIs so they can be shared between Servo -//! and Gecko. - -use crate::context::QuirksMode; -use crate::dom::{TDocument, TElement, TNode, TShadowRoot}; -use crate::invalidation::element::invalidation_map::Dependency; -use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation}; -use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector}; -use crate::selector_parser::SelectorImpl; -use crate::values::AtomIdent; -use selectors::attr::CaseSensitivity; -use selectors::matching::{self, MatchingContext, MatchingMode, NeedsSelectorFlags}; -use selectors::parser::{Combinator, Component, LocalName}; -use selectors::{Element, SelectorList}; -use smallvec::SmallVec; - -/// -pub fn element_matches( - element: &E, - selector_list: &SelectorList, - quirks_mode: QuirksMode, -) -> bool -where - E: Element, -{ - let mut nth_index_cache = Default::default(); - - let mut context = MatchingContext::new( - MatchingMode::Normal, - None, - &mut nth_index_cache, - quirks_mode, - NeedsSelectorFlags::No, - ); - context.scope_element = Some(element.opaque()); - context.current_host = element.containing_shadow_host().map(|e| e.opaque()); - matching::matches_selector_list(selector_list, element, &mut context) -} - -/// -pub fn element_closest( - element: E, - selector_list: &SelectorList, - quirks_mode: QuirksMode, -) -> Option -where - E: Element, -{ - let mut nth_index_cache = Default::default(); - - let mut context = MatchingContext::new( - MatchingMode::Normal, - None, - &mut nth_index_cache, - quirks_mode, - NeedsSelectorFlags::No, - ); - context.scope_element = Some(element.opaque()); - context.current_host = element.containing_shadow_host().map(|e| e.opaque()); - - let mut current = Some(element); - while let Some(element) = current.take() { - if matching::matches_selector_list(selector_list, &element, &mut context) { - return Some(element); - } - current = element.parent_element(); - } - - return None; -} - -/// A selector query abstraction, in order to be generic over QuerySelector and -/// QuerySelectorAll. -pub trait SelectorQuery { - /// The output of the query. - type Output; - - /// Whether the query should stop after the first element has been matched. - fn should_stop_after_first_match() -> bool; - - /// Append an element matching after the first query. - fn append_element(output: &mut Self::Output, element: E); - - /// Returns true if the output is empty. - fn is_empty(output: &Self::Output) -> bool; -} - -/// The result of a querySelectorAll call. -pub type QuerySelectorAllResult = SmallVec<[E; 128]>; - -/// A query for all the elements in a subtree. -pub struct QueryAll; - -impl SelectorQuery for QueryAll { - type Output = QuerySelectorAllResult; - - fn should_stop_after_first_match() -> bool { - false - } - - fn append_element(output: &mut Self::Output, element: E) { - output.push(element); - } - - fn is_empty(output: &Self::Output) -> bool { - output.is_empty() - } -} - -/// A query for the first in-tree match of all the elements in a subtree. -pub struct QueryFirst; - -impl SelectorQuery for QueryFirst { - type Output = Option; - - fn should_stop_after_first_match() -> bool { - true - } - - fn append_element(output: &mut Self::Output, element: E) { - if output.is_none() { - *output = Some(element) - } - } - - fn is_empty(output: &Self::Output) -> bool { - output.is_none() - } -} - -struct QuerySelectorProcessor<'a, E, Q> -where - E: TElement + 'a, - Q: SelectorQuery, - Q::Output: 'a, -{ - results: &'a mut Q::Output, - matching_context: MatchingContext<'a, E::Impl>, - dependencies: &'a [Dependency], -} - -impl<'a, E, Q> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E, Q> -where - E: TElement + 'a, - Q: SelectorQuery, - Q::Output: 'a, -{ - fn light_tree_only(&self) -> bool { - true - } - - fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool { - debug_assert!( - false, - "How? We should only have parent-less dependencies here!" - ); - true - } - - fn collect_invalidations( - &mut self, - element: E, - self_invalidations: &mut InvalidationVector<'a>, - descendant_invalidations: &mut DescendantInvalidationLists<'a>, - _sibling_invalidations: &mut InvalidationVector<'a>, - ) -> bool { - // TODO(emilio): If the element is not a root element, and - // selector_list has any descendant combinator, we need to do extra work - // in order to handle properly things like: - // - //
- //
- //
- //
- //
- // - // b.querySelector('#a div'); // Should return "c". - // - // For now, assert it's a root element. - debug_assert!(element.parent_element().is_none()); - - let target_vector = if self.matching_context.scope_element.is_some() { - &mut descendant_invalidations.dom_descendants - } else { - self_invalidations - }; - - for dependency in self.dependencies.iter() { - target_vector.push(Invalidation::new( - dependency, - self.matching_context.current_host.clone(), - )) - } - - false - } - - fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> { - &mut self.matching_context - } - - fn should_process_descendants(&mut self, _: E) -> bool { - if Q::should_stop_after_first_match() { - return Q::is_empty(&self.results); - } - - true - } - - fn invalidated_self(&mut self, e: E) { - Q::append_element(self.results, e); - } - - fn invalidated_sibling(&mut self, e: E, _of: E) { - Q::append_element(self.results, e); - } - - fn recursion_limit_exceeded(&mut self, _e: E) {} - fn invalidated_descendants(&mut self, _e: E, _child: E) {} -} - -fn collect_all_elements(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F) -where - E: TElement, - Q: SelectorQuery, - F: FnMut(E) -> bool, -{ - for node in root.dom_descendants() { - let element = match node.as_element() { - Some(e) => e, - None => continue, - }; - - if !filter(element) { - continue; - } - - Q::append_element(results, element); - if Q::should_stop_after_first_match() { - return; - } - } -} - -/// Returns whether a given element connected to `root` is descendant of `root`. -/// -/// NOTE(emilio): if root == element, this returns false. -fn connected_element_is_descendant_of(element: E, root: E::ConcreteNode) -> bool -where - E: TElement, -{ - // Optimize for when the root is a document or a shadow root and the element - // is connected to that root. - if root.as_document().is_some() { - debug_assert!(element.as_node().is_in_document(), "Not connected?"); - debug_assert_eq!( - root, - root.owner_doc().as_node(), - "Where did this element come from?", - ); - return true; - } - - if root.as_shadow_root().is_some() { - debug_assert_eq!( - element.containing_shadow().unwrap().as_node(), - root, - "Not connected?" - ); - return true; - } - - let mut current = element.as_node().parent_node(); - while let Some(n) = current.take() { - if n == root { - return true; - } - - current = n.parent_node(); - } - false -} - -/// Fast path for iterating over every element with a given id in the document -/// or shadow root that `root` is connected to. -fn fast_connected_elements_with_id<'a, N>( - root: N, - id: &AtomIdent, - case_sensitivity: CaseSensitivity, -) -> Result<&'a [N::ConcreteElement], ()> -where - N: TNode + 'a, -{ - if case_sensitivity != CaseSensitivity::CaseSensitive { - return Err(()); - } - - if root.is_in_document() { - return root.owner_doc().elements_with_id(id); - } - - if let Some(shadow) = root.as_shadow_root() { - return shadow.elements_with_id(id); - } - - if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) { - return shadow.elements_with_id(id); - } - - Err(()) -} - -/// Collects elements with a given id under `root`, that pass `filter`. -fn collect_elements_with_id( - root: E::ConcreteNode, - id: &AtomIdent, - results: &mut Q::Output, - class_and_id_case_sensitivity: CaseSensitivity, - mut filter: F, -) where - E: TElement, - Q: SelectorQuery, - F: FnMut(E) -> bool, -{ - let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) { - Ok(elements) => elements, - Err(()) => { - collect_all_elements::(root, results, |e| { - e.has_id(id, class_and_id_case_sensitivity) && filter(e) - }); - - return; - }, - }; - - for element in elements { - // If the element is not an actual descendant of the root, even though - // it's connected, we don't really care about it. - if !connected_element_is_descendant_of(*element, root) { - continue; - } - - if !filter(*element) { - continue; - } - - Q::append_element(results, *element); - if Q::should_stop_after_first_match() { - break; - } - } -} - -fn has_attr(element: E, local_name: &crate::LocalName) -> bool -where - E: TElement, -{ - let mut found = false; - element.each_attr_name(|name| found |= name == local_name); - found -} - -#[inline(always)] -fn local_name_matches(element: E, local_name: &LocalName) -> bool -where - E: TElement, -{ - let LocalName { - ref name, - ref lower_name, - } = *local_name; - - let chosen_name = if name == lower_name || element.is_html_element_in_html_document() { - lower_name - } else { - name - }; - - element.local_name() == &**chosen_name -} - -fn get_attr_name(component: &Component) -> Option<&crate::LocalName> { - let (name, name_lower) = match component { - Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name), - Component::AttributeInNoNamespaceExists { - ref local_name, - ref local_name_lower, - .. - } => (local_name, local_name_lower), - Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower), - _ => return None, - }; - if name != name_lower { - return None; // TODO: Maybe optimize this? - } - Some(name) -} - -fn get_id(component: &Component) -> Option<&AtomIdent> { - use selectors::attr::AttrSelectorOperator; - Some(match component { - Component::ID(ref id) => id, - Component::AttributeInNoNamespace { - ref operator, - ref local_name, - ref value, - .. - } => { - if *local_name != local_name!("id") { - return None; - } - if *operator != AttrSelectorOperator::Equal { - return None; - } - AtomIdent::cast(&value.0) - }, - _ => return None, - }) -} - -/// Fast paths for querySelector with a single simple selector. -fn query_selector_single_query( - root: E::ConcreteNode, - component: &Component, - results: &mut Q::Output, - class_and_id_case_sensitivity: CaseSensitivity, -) -> Result<(), ()> -where - E: TElement, - Q: SelectorQuery, -{ - // TODO: Maybe we could implement a fast path for [name=""]? - match *component { - Component::ExplicitUniversalType => { - collect_all_elements::(root, results, |_| true) - }, - Component::Class(ref class) => collect_all_elements::(root, results, |element| { - element.has_class(class, class_and_id_case_sensitivity) - }), - Component::LocalName(ref local_name) => { - collect_all_elements::(root, results, |element| { - local_name_matches(element, local_name) - }) - }, - ref other => { - let id = match get_id(other) { - Some(id) => id, - // TODO(emilio): More fast paths? - None => return Err(()), - }; - collect_elements_with_id::( - root, - id, - results, - class_and_id_case_sensitivity, - |_| true, - ); - }, - } - - Ok(()) -} - -enum SimpleFilter<'a> { - Class(&'a AtomIdent), - Attr(&'a crate::LocalName), - LocalName(&'a LocalName), -} - -/// Fast paths for a given selector query. -/// -/// When there's only one component, we go directly to -/// `query_selector_single_query`, otherwise, we try to optimize by looking just -/// at the subtrees rooted at ids in the selector, and otherwise we try to look -/// up by class name or local name in the rightmost compound. -/// -/// FIXME(emilio, nbp): This may very well be a good candidate for code to be -/// replaced by HolyJit :) -fn query_selector_fast( - root: E::ConcreteNode, - selector_list: &SelectorList, - results: &mut Q::Output, - matching_context: &mut MatchingContext, -) -> Result<(), ()> -where - E: TElement, - Q: SelectorQuery, -{ - // We need to return elements in document order, and reordering them - // afterwards is kinda silly. - if selector_list.0.len() > 1 { - return Err(()); - } - - let selector = &selector_list.0[0]; - let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity(); - // Let's just care about the easy cases for now. - if selector.len() == 1 { - if query_selector_single_query::( - root, - selector.iter().next().unwrap(), - results, - class_and_id_case_sensitivity, - ) - .is_ok() - { - return Ok(()); - } - } - - let mut iter = selector.iter(); - let mut combinator: Option = None; - - // We want to optimize some cases where there's no id involved whatsoever, - // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of - // that. - let mut simple_filter = None; - - 'selector_loop: loop { - debug_assert!(combinator.map_or(true, |c| !c.is_sibling())); - - 'component_loop: for component in &mut iter { - match *component { - Component::Class(ref class) => { - if combinator.is_none() { - simple_filter = Some(SimpleFilter::Class(class)); - } - }, - Component::LocalName(ref local_name) => { - if combinator.is_none() { - // Prefer to look at class rather than local-name if - // both are present. - if let Some(SimpleFilter::Class(..)) = simple_filter { - continue; - } - simple_filter = Some(SimpleFilter::LocalName(local_name)); - } - }, - ref other => { - if let Some(id) = get_id(other) { - if combinator.is_none() { - // In the rightmost compound, just find descendants of root that match - // the selector list with that id. - collect_elements_with_id::( - root, - id, - results, - class_and_id_case_sensitivity, - |e| { - matching::matches_selector_list( - selector_list, - &e, - matching_context, - ) - }, - ); - return Ok(()); - } - - let elements = fast_connected_elements_with_id( - root, - id, - class_and_id_case_sensitivity, - )?; - if elements.is_empty() { - return Ok(()); - } - - // Results need to be in document order. Let's not bother - // reordering or deduplicating nodes, which we would need to - // do if one element with the given id were a descendant of - // another element with that given id. - if !Q::should_stop_after_first_match() && elements.len() > 1 { - continue; - } - - for element in elements { - // If the element is not a descendant of the root, then - // it may have descendants that match our selector that - // _are_ descendants of the root, and other descendants - // that match our selector that are _not_. - // - // So we can't just walk over the element's descendants - // and match the selector against all of them, nor can - // we skip looking at this element's descendants. - // - // Give up on trying to optimize based on this id and - // keep walking our selector. - if !connected_element_is_descendant_of(*element, root) { - continue 'component_loop; - } - - query_selector_slow::( - element.as_node(), - selector_list, - results, - matching_context, - ); - - if Q::should_stop_after_first_match() && !Q::is_empty(&results) { - break; - } - } - - return Ok(()); - } - if combinator.is_none() && simple_filter.is_none() { - if let Some(attr_name) = get_attr_name(other) { - simple_filter = Some(SimpleFilter::Attr(attr_name)); - } - } - }, - } - } - - loop { - let next_combinator = match iter.next_sequence() { - None => break 'selector_loop, - Some(c) => c, - }; - - // We don't want to scan stuff affected by sibling combinators, - // given we scan the subtree of elements with a given id (and we - // don't want to care about scanning the siblings' subtrees). - if next_combinator.is_sibling() { - // Advance to the next combinator. - for _ in &mut iter {} - continue; - } - - combinator = Some(next_combinator); - break; - } - } - - // We got here without finding any ID or such that we could handle. Try to - // use one of the simple filters. - let simple_filter = match simple_filter { - Some(f) => f, - None => return Err(()), - }; - - match simple_filter { - SimpleFilter::Class(ref class) => { - collect_all_elements::(root, results, |element| { - element.has_class(class, class_and_id_case_sensitivity) && - matching::matches_selector_list(selector_list, &element, matching_context) - }); - }, - SimpleFilter::LocalName(ref local_name) => { - collect_all_elements::(root, results, |element| { - local_name_matches(element, local_name) && - matching::matches_selector_list(selector_list, &element, matching_context) - }); - }, - SimpleFilter::Attr(ref local_name) => { - collect_all_elements::(root, results, |element| { - has_attr(element, local_name) && - matching::matches_selector_list(selector_list, &element, matching_context) - }); - }, - } - - Ok(()) -} - -// Slow path for a given selector query. -fn query_selector_slow( - root: E::ConcreteNode, - selector_list: &SelectorList, - results: &mut Q::Output, - matching_context: &mut MatchingContext, -) where - E: TElement, - Q: SelectorQuery, -{ - collect_all_elements::(root, results, |element| { - matching::matches_selector_list(selector_list, &element, matching_context) - }); -} - -/// Whether the invalidation machinery should be used for this query. -#[derive(PartialEq)] -pub enum MayUseInvalidation { - /// We may use it if we deem it useful. - Yes, - /// Don't use it. - No, -} - -/// -pub fn query_selector( - root: E::ConcreteNode, - selector_list: &SelectorList, - results: &mut Q::Output, - may_use_invalidation: MayUseInvalidation, -) where - E: TElement, - Q: SelectorQuery, -{ - use crate::invalidation::element::invalidator::TreeStyleInvalidator; - - let mut nth_index_cache = Default::default(); - let quirks_mode = root.owner_doc().quirks_mode(); - - let mut matching_context = MatchingContext::new( - MatchingMode::Normal, - None, - &mut nth_index_cache, - quirks_mode, - NeedsSelectorFlags::No, - ); - let root_element = root.as_element(); - matching_context.scope_element = root_element.map(|e| e.opaque()); - matching_context.current_host = match root_element { - Some(root) => root.containing_shadow_host().map(|host| host.opaque()), - None => root.as_shadow_root().map(|root| root.host().opaque()), - }; - - let fast_result = - query_selector_fast::(root, selector_list, results, &mut matching_context); - - if fast_result.is_ok() { - return; - } - - // Slow path: Use the invalidation machinery if we're a root, and tree - // traversal otherwise. - // - // See the comment in collect_invalidations to see why only if we're a root. - // - // The invalidation mechanism is only useful in presence of combinators. - // - // We could do that check properly here, though checking the length of the - // selectors is a good heuristic. - // - // A selector with a combinator needs to have a length of at least 3: A - // simple selector, a combinator, and another simple selector. - let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes && - selector_list.0.iter().any(|s| s.len() > 2); - - if root_element.is_some() || !invalidation_may_be_useful { - query_selector_slow::(root, selector_list, results, &mut matching_context); - } else { - let dependencies = selector_list - .0 - .iter() - .map(|selector| Dependency::for_full_selector_invalidation(selector.clone())) - .collect::>(); - let mut processor = QuerySelectorProcessor:: { - results, - matching_context, - dependencies: &dependencies, - }; - - for node in root.dom_children() { - if let Some(e) = node.as_element() { - TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor) - .invalidate(); - } - } - } -} diff --git a/components/style/driver.rs b/components/style/driver.rs deleted file mode 100644 index cf48d831fdd..00000000000 --- a/components/style/driver.rs +++ /dev/null @@ -1,161 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Implements traversal over the DOM tree. The traversal starts in sequential -//! mode, and optionally parallelizes as it discovers work. - -#![deny(missing_docs)] - -use crate::context::{PerThreadTraversalStatistics, StyleContext}; -use crate::context::{ThreadLocalStyleContext, TraversalStatistics}; -use crate::dom::{SendNode, TElement, TNode}; -use crate::parallel; -use crate::scoped_tls::ScopedTLS; -use crate::traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken}; -use rayon; -use std::collections::VecDeque; -use std::mem; -use time; - -#[cfg(feature = "servo")] -fn should_report_statistics() -> bool { - false -} - -#[cfg(feature = "gecko")] -fn should_report_statistics() -> bool { - unsafe { crate::gecko_bindings::structs::ServoTraversalStatistics_sActive } -} - -#[cfg(feature = "servo")] -fn report_statistics(_stats: &PerThreadTraversalStatistics) { - unreachable!("Servo never report stats"); -} - -#[cfg(feature = "gecko")] -fn report_statistics(stats: &PerThreadTraversalStatistics) { - // This should only be called in the main thread, or it may be racy - // to update the statistics in a global variable. - debug_assert!(unsafe { crate::gecko_bindings::bindings::Gecko_IsMainThread() }); - let gecko_stats = - unsafe { &mut crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton }; - gecko_stats.mElementsTraversed += stats.elements_traversed; - gecko_stats.mElementsStyled += stats.elements_styled; - gecko_stats.mElementsMatched += stats.elements_matched; - gecko_stats.mStylesShared += stats.styles_shared; - gecko_stats.mStylesReused += stats.styles_reused; -} - -fn with_pool_in_place_scope<'scope, R>( - work_unit_max: usize, - pool: Option<&rayon::ThreadPool>, - closure: impl FnOnce(Option<&rayon::ScopeFifo<'scope>>) -> R, -) -> R { - if work_unit_max == 0 || pool.is_none() { - closure(None) - } else { - pool.unwrap().in_place_scope_fifo(|scope| { - closure(Some(scope)) - }) - } -} - -/// See documentation of the pref for performance characteristics. -fn work_unit_max() -> usize { - static_prefs::pref!("layout.css.stylo-work-unit-size") as usize -} - -/// Do a DOM traversal for top-down and (optionally) bottom-up processing, generic over `D`. -/// -/// We use an adaptive traversal strategy. We start out with simple sequential processing, until we -/// arrive at a wide enough level in the DOM that the parallel traversal would parallelize it. -/// If a thread pool is provided, we then transfer control over to the parallel traversal. -/// -/// Returns true if the traversal was parallel, and also returns the statistics object containing -/// information on nodes traversed (on nightly only). Not all of its fields will be initialized -/// since we don't call finish(). -pub fn traverse_dom( - traversal: &D, - token: PreTraverseToken, - pool: Option<&rayon::ThreadPool>, -) -> E -where - E: TElement, - D: DomTraversal, -{ - let root = token - .traversal_root() - .expect("Should've ensured we needed to traverse"); - - let report_stats = should_report_statistics(); - let dump_stats = traversal.shared_context().options.dump_style_statistics; - let start_time = if dump_stats { - Some(time::precise_time_s()) - } else { - None - }; - - // Declare the main-thread context, as well as the worker-thread contexts, - // which we may or may not instantiate. It's important to declare the worker- - // thread contexts first, so that they get dropped second. This matters because: - // * ThreadLocalContexts borrow AtomicRefCells in TLS. - // * Dropping a ThreadLocalContext can run SequentialTasks. - // * Sequential tasks may call into functions like - // Servo_StyleSet_GetBaseComputedValuesForElement, which instantiate a - // ThreadLocalStyleContext on the main thread. If the main thread - // ThreadLocalStyleContext has not released its TLS borrow by that point, - // we'll panic on double-borrow. - let mut scoped_tls = pool.map(ScopedTLS::>::new); - let mut tlc = ThreadLocalStyleContext::new(); - let mut context = StyleContext { - shared: traversal.shared_context(), - thread_local: &mut tlc, - }; - - // Process the nodes breadth-first. This helps keep similar traversal characteristics for the - // style sharing cache. - let work_unit_max = work_unit_max(); - with_pool_in_place_scope(work_unit_max, pool, |maybe_scope| { - let mut discovered = VecDeque::with_capacity(work_unit_max * 2); - discovered.push_back(unsafe { SendNode::new(root.as_node()) }); - parallel::style_trees( - &mut context, - discovered, - root.as_node().opaque(), - work_unit_max, - static_prefs::pref!("layout.css.stylo-local-work-queue.in-main-thread") as usize, - PerLevelTraversalData { current_dom_depth: root.depth() }, - maybe_scope, - traversal, - scoped_tls.as_ref(), - ); - }); - - // Collect statistics from thread-locals if requested. - if dump_stats || report_stats { - let mut aggregate = mem::replace(&mut context.thread_local.statistics, Default::default()); - let parallel = pool.is_some(); - if let Some(ref mut tls) = scoped_tls { - for slot in tls.slots() { - if let Some(cx) = slot.get_mut() { - aggregate += cx.statistics.clone(); - } - } - } - - if report_stats { - report_statistics(&aggregate); - } - // dump statistics to stdout if requested - if dump_stats { - let stats = - TraversalStatistics::new(aggregate, traversal, parallel, start_time.unwrap()); - if stats.is_large { - println!("{}", stats); - } - } - } - - root -} diff --git a/components/style/encoding_support.rs b/components/style/encoding_support.rs deleted file mode 100644 index c144ad0b3bc..00000000000 --- a/components/style/encoding_support.rs +++ /dev/null @@ -1,105 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Parsing stylesheets from bytes (not `&str`). - -use crate::context::QuirksMode; -use crate::error_reporting::ParseErrorReporter; -use crate::media_queries::MediaList; -use crate::shared_lock::SharedRwLock; -use crate::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetLoader, UrlExtraData}; -use cssparser::{stylesheet_encoding, EncodingSupport}; -use servo_arc::Arc; -use std::borrow::Cow; -use std::str; - -struct EncodingRs; - -impl EncodingSupport for EncodingRs { - type Encoding = &'static encoding_rs::Encoding; - - fn utf8() -> Self::Encoding { - encoding_rs::UTF_8 - } - - fn is_utf16_be_or_le(encoding: &Self::Encoding) -> bool { - *encoding == encoding_rs::UTF_16LE || *encoding == encoding_rs::UTF_16BE - } - - fn from_label(ascii_label: &[u8]) -> Option { - encoding_rs::Encoding::for_label(ascii_label) - } -} - -fn decode_stylesheet_bytes<'a>( - css: &'a [u8], - protocol_encoding_label: Option<&str>, - environment_encoding: Option<&'static encoding_rs::Encoding>, -) -> Cow<'a, str> { - let fallback_encoding = stylesheet_encoding::( - css, - protocol_encoding_label.map(str::as_bytes), - environment_encoding, - ); - let (result, _used_encoding, _) = fallback_encoding.decode(&css); - // FIXME record used encoding for environment encoding of @import - result -} - -impl Stylesheet { - /// Parse a stylesheet from a set of bytes, potentially received over the - /// network. - /// - /// Takes care of decoding the network bytes and forwards the resulting - /// string to `Stylesheet::from_str`. - pub fn from_bytes( - bytes: &[u8], - url_data: UrlExtraData, - protocol_encoding_label: Option<&str>, - environment_encoding: Option<&'static encoding_rs::Encoding>, - origin: Origin, - media: MediaList, - shared_lock: SharedRwLock, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - quirks_mode: QuirksMode, - ) -> Stylesheet { - let string = decode_stylesheet_bytes(bytes, protocol_encoding_label, environment_encoding); - Stylesheet::from_str( - &string, - url_data, - origin, - Arc::new(shared_lock.wrap(media)), - shared_lock, - stylesheet_loader, - error_reporter, - quirks_mode, - 0, - AllowImportRules::Yes, - ) - } - - /// Updates an empty stylesheet with a set of bytes that reached over the - /// network. - pub fn update_from_bytes( - existing: &Stylesheet, - bytes: &[u8], - protocol_encoding_label: Option<&str>, - environment_encoding: Option<&'static encoding_rs::Encoding>, - url_data: UrlExtraData, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - ) { - let string = decode_stylesheet_bytes(bytes, protocol_encoding_label, environment_encoding); - Self::update_from_str( - existing, - &string, - url_data, - stylesheet_loader, - error_reporter, - 0, - AllowImportRules::Yes, - ) - } -} diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs deleted file mode 100644 index 258c7c44ef4..00000000000 --- a/components/style/error_reporting.rs +++ /dev/null @@ -1,283 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Types used to report parsing errors. - -#![deny(missing_docs)] - -use crate::selector_parser::SelectorImpl; -use crate::stylesheets::UrlExtraData; -use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token}; -use selectors::SelectorList; -use std::fmt; -use style_traits::ParseError; - -/// Errors that can be encountered while parsing CSS. -#[derive(Debug)] -pub enum ContextualParseError<'a> { - /// A property declaration was not recognized. - UnsupportedPropertyDeclaration( - &'a str, - ParseError<'a>, - Option<&'a SelectorList>, - ), - /// A property descriptor was not recognized. - UnsupportedPropertyDescriptor(&'a str, ParseError<'a>), - /// A font face descriptor was not recognized. - UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>), - /// A font feature values descriptor was not recognized. - UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>), - /// A font palette values descriptor was not recognized. - UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>), - /// A keyframe rule was not valid. - InvalidKeyframeRule(&'a str, ParseError<'a>), - /// A font feature values rule was not valid. - InvalidFontFeatureValuesRule(&'a str, ParseError<'a>), - /// A keyframe property declaration was not recognized. - UnsupportedKeyframePropertyDeclaration(&'a str, ParseError<'a>), - /// A rule was invalid for some reason. - InvalidRule(&'a str, ParseError<'a>), - /// A rule was not recognized. - UnsupportedRule(&'a str, ParseError<'a>), - /// A viewport descriptor declaration was not recognized. - UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>), - /// A counter style descriptor declaration was not recognized. - UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>), - /// A counter style rule had no symbols. - InvalidCounterStyleWithoutSymbols(String), - /// A counter style rule had less than two symbols. - InvalidCounterStyleNotEnoughSymbols(String), - /// A counter style rule did not have additive-symbols. - InvalidCounterStyleWithoutAdditiveSymbols, - /// A counter style rule had extends with symbols. - InvalidCounterStyleExtendsWithSymbols, - /// A counter style rule had extends with additive-symbols. - InvalidCounterStyleExtendsWithAdditiveSymbols, - /// A media rule was invalid for some reason. - InvalidMediaRule(&'a str, ParseError<'a>), - /// A value was not recognized. - UnsupportedValue(&'a str, ParseError<'a>), - /// A never-matching `:host` selector was found. - NeverMatchingHostSelector(String), -} - -impl<'a> fmt::Display for ContextualParseError<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result { - match *t { - Token::Ident(ref i) => write!(f, "identifier {}", i), - Token::AtKeyword(ref kw) => write!(f, "keyword @{}", kw), - Token::Hash(ref h) => write!(f, "hash #{}", h), - Token::IDHash(ref h) => write!(f, "id selector #{}", h), - Token::QuotedString(ref s) => write!(f, "quoted string \"{}\"", s), - Token::UnquotedUrl(ref u) => write!(f, "url {}", u), - Token::Delim(ref d) => write!(f, "delimiter {}", d), - Token::Number { - int_value: Some(i), .. - } => write!(f, "number {}", i), - Token::Number { value, .. } => write!(f, "number {}", value), - Token::Percentage { - int_value: Some(i), .. - } => write!(f, "percentage {}", i), - Token::Percentage { unit_value, .. } => { - write!(f, "percentage {}", unit_value * 100.) - }, - Token::Dimension { - value, ref unit, .. - } => write!(f, "dimension {}{}", value, unit), - Token::WhiteSpace(_) => write!(f, "whitespace"), - Token::Comment(_) => write!(f, "comment"), - Token::Colon => write!(f, "colon (:)"), - Token::Semicolon => write!(f, "semicolon (;)"), - Token::Comma => write!(f, "comma (,)"), - Token::IncludeMatch => write!(f, "include match (~=)"), - Token::DashMatch => write!(f, "dash match (|=)"), - Token::PrefixMatch => write!(f, "prefix match (^=)"), - Token::SuffixMatch => write!(f, "suffix match ($=)"), - Token::SubstringMatch => write!(f, "substring match (*=)"), - Token::CDO => write!(f, "CDO ()"), - Token::Function(ref name) => write!(f, "function {}", name), - Token::ParenthesisBlock => write!(f, "parenthesis ("), - Token::SquareBracketBlock => write!(f, "square bracket ["), - Token::CurlyBracketBlock => write!(f, "curly bracket {{"), - Token::BadUrl(ref _u) => write!(f, "bad url parse error"), - Token::BadString(ref _s) => write!(f, "bad string parse error"), - Token::CloseParenthesis => write!(f, "unmatched close parenthesis"), - Token::CloseSquareBracket => write!(f, "unmatched close square bracket"), - Token::CloseCurlyBracket => write!(f, "unmatched close curly bracket"), - } - } - - fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result { - match err.kind { - ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => { - write!(f, "found unexpected ")?; - token_to_str(t, f) - }, - ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => { - write!(f, "unexpected end of input") - }, - ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => { - write!(f, "@ rule invalid: {}", i) - }, - ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => { - write!(f, "@ rule invalid") - }, - ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => { - write!(f, "qualified rule invalid") - }, - ParseErrorKind::Custom(ref err) => write!(f, "{:?}", err), - } - } - - match *self { - ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => { - write!(f, "Unsupported property declaration: '{}', ", decl)?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => { - write!( - f, - "Unsupported @property descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => { - write!( - f, - "Unsupported @font-face descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => { - write!( - f, - "Unsupported @font-feature-values descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => { - write!( - f, - "Unsupported @font-palette-values descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::InvalidKeyframeRule(rule, ref err) => { - write!(f, "Invalid keyframe rule: '{}', ", rule)?; - parse_error_to_str(err, f) - }, - ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => { - write!(f, "Invalid font feature value rule: '{}', ", rule)?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedKeyframePropertyDeclaration(decl, ref err) => { - write!(f, "Unsupported keyframe property declaration: '{}', ", decl)?; - parse_error_to_str(err, f) - }, - ContextualParseError::InvalidRule(rule, ref err) => { - write!(f, "Invalid rule: '{}', ", rule)?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedRule(rule, ref err) => { - write!(f, "Unsupported rule: '{}', ", rule)?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => { - write!( - f, - "Unsupported @viewport descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => { - write!( - f, - "Unsupported @counter-style descriptor declaration: '{}', ", - decl - )?; - parse_error_to_str(err, f) - }, - ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!( - f, - "Invalid @counter-style rule: 'system: {}' without 'symbols'", - system - ), - ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!( - f, - "Invalid @counter-style rule: 'system: {}' less than two 'symbols'", - system - ), - ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!( - f, - "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'" - ), - ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!( - f, - "Invalid @counter-style rule: 'system: extends …' with 'symbols'" - ), - ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!( - f, - "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'" - ), - ContextualParseError::InvalidMediaRule(media_rule, ref err) => { - write!(f, "Invalid media rule: {}, ", media_rule)?; - parse_error_to_str(err, f) - }, - ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f), - ContextualParseError::NeverMatchingHostSelector(ref selector) => { - write!(f, ":host selector is not featureless: {}", selector) - }, - } - } -} - -/// A generic trait for an error reporter. -pub trait ParseErrorReporter { - /// Called when the style engine detects an error. - /// - /// Returns the current input being parsed, the source location it was - /// reported from, and a message. - fn report_error( - &self, - url: &UrlExtraData, - location: SourceLocation, - error: ContextualParseError, - ); -} - -/// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log) -/// at `info` level. -/// -/// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info` -/// environment variable. -/// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).) -#[cfg(feature = "servo")] -pub struct RustLogReporter; - -#[cfg(feature = "servo")] -impl ParseErrorReporter for RustLogReporter { - fn report_error( - &self, - url: &UrlExtraData, - location: SourceLocation, - error: ContextualParseError, - ) { - if log_enabled!(log::Level::Info) { - info!( - "Url:\t{}\n{}:{} {}", - url.as_str(), - location.line, - location.column, - error - ) - } - } -} diff --git a/components/style/font_face.rs b/components/style/font_face.rs deleted file mode 100644 index 1e193d3eb79..00000000000 --- a/components/style/font_face.rs +++ /dev/null @@ -1,835 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The [`@font-face`][ff] at-rule. -//! -//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule - -use crate::error_reporting::ContextualParseError; -use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "gecko")] -use crate::properties::longhands::font_language_override; -use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::values::computed::font::{FamilyName, FontStretch}; -use crate::values::generics::font::FontStyle as GenericFontStyle; -#[cfg(feature = "gecko")] -use crate::values::specified::font::MetricsOverride; -use crate::values::specified::font::SpecifiedFontStyle; -use crate::values::specified::font::{AbsoluteFontWeight, FontStretch as SpecifiedFontStretch}; -#[cfg(feature = "gecko")] -use crate::values::specified::font::{FontFeatureSettings, FontVariationSettings}; -use crate::values::specified::url::SpecifiedUrl; -use crate::values::specified::Angle; -#[cfg(feature = "gecko")] -use crate::values::specified::NonNegativePercentage; -#[cfg(feature = "gecko")] -use cssparser::UnicodeRange; -use cssparser::{ - AtRuleParser, CowRcStr, DeclarationParser, Parser, QualifiedRuleParser, RuleBodyItemParser, - RuleBodyParser, SourceLocation, -}; -use selectors::parser::SelectorParseErrorKind; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError}; -use style_traits::{StyleParseErrorKind, ToCss}; - -/// A source for a font-face rule. -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] -pub enum Source { - /// A `url()` source. - Url(UrlSource), - /// A `local()` source. - #[css(function)] - Local(FamilyName), -} - -/// A list of sources for the font-face src descriptor. -#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] -#[css(comma)] -pub struct SourceList(#[css(iterable)] pub Vec); - -// We can't just use OneOrMoreSeparated to derive Parse for the Source list, -// because we want to filter out components that parsed as None, then fail if no -// valid components remain. So we provide our own implementation here. -impl Parse for SourceList { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - // Parse the comma-separated list, then let filter_map discard any None items. - let list = input - .parse_comma_separated(|input| { - let s = input.parse_entirely(|input| Source::parse(context, input)); - while input.next().is_ok() {} - Ok(s.ok()) - })? - .into_iter() - .filter_map(|s| s) - .collect::>(); - if list.is_empty() { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } else { - Ok(SourceList(list)) - } - } -} - -/// Keywords for the font-face src descriptor's format() function. -/// ('None' and 'Unknown' are for internal use in gfx, not exposed to CSS.) -#[derive(Clone, Copy, Debug, Eq, Parse, PartialEq, ToCss, ToShmem)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[repr(u8)] -#[allow(missing_docs)] -pub enum FontFaceSourceFormatKeyword { - #[css(skip)] - None, - Collection, - EmbeddedOpentype, - Opentype, - Svg, - Truetype, - Woff, - Woff2, - #[css(skip)] - Unknown, -} - -bitflags! { - /// Flags for the @font-face tech() function, indicating font technologies - /// required by the resource. - #[derive(ToShmem)] - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] - #[repr(C)] - pub struct FontFaceSourceTechFlags: u16 { - /// Font requires OpenType feature support. - const FEATURES_OPENTYPE = 1 << 0; - /// Font requires Apple Advanced Typography support. - const FEATURES_AAT = 1 << 1; - /// Font requires Graphite shaping support. - const FEATURES_GRAPHITE = 1 << 2; - /// Font requires COLRv0 rendering support (simple list of colored layers). - const COLOR_COLRV0 = 1 << 3; - /// Font requires COLRv1 rendering support (graph of paint operations). - const COLOR_COLRV1 = 1 << 4; - /// Font requires SVG glyph rendering support. - const COLOR_SVG = 1 << 5; - /// Font has bitmap glyphs in 'sbix' format. - const COLOR_SBIX = 1 << 6; - /// Font has bitmap glyphs in 'CBDT' format. - const COLOR_CBDT = 1 << 7; - /// Font requires OpenType Variations support. - const VARIATIONS = 1 << 8; - /// Font requires CPAL palette selection support. - const PALETTES = 1 << 9; - /// Font requires support for incremental downloading. - const INCREMENTAL = 1 << 10; - } -} - -impl FontFaceSourceTechFlags { - /// Parse a single font-technology keyword and return its flag. - pub fn parse_one<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - Ok(try_match_ident_ignore_ascii_case! { input, - "features-opentype" => Self::FEATURES_OPENTYPE, - "features-aat" => Self::FEATURES_AAT, - "features-graphite" => Self::FEATURES_GRAPHITE, - "color-colrv0" => Self::COLOR_COLRV0, - "color-colrv1" => Self::COLOR_COLRV1, - "color-svg" => Self::COLOR_SVG, - "color-sbix" => Self::COLOR_SBIX, - "color-cbdt" => Self::COLOR_CBDT, - "variations" => Self::VARIATIONS, - "palettes" => Self::PALETTES, - "incremental" => Self::INCREMENTAL, - }) - } -} - -impl Parse for FontFaceSourceTechFlags { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let location = input.current_source_location(); - // We don't actually care about the return value of parse_comma_separated, - // because we insert the flags into result as we go. - let mut result = Self::empty(); - input.parse_comma_separated(|input| { - let flag = Self::parse_one(input)?; - result.insert(flag); - Ok(()) - })?; - if !result.is_empty() { - Ok(result) - } else { - Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } -} - -#[allow(unused_assignments)] -impl ToCss for FontFaceSourceTechFlags { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write, - { - let mut first = true; - - macro_rules! write_if_flag { - ($s:expr => $f:ident) => { - if self.contains(Self::$f) { - if first { - first = false; - } else { - dest.write_str(", ")?; - } - dest.write_str($s)?; - } - }; - } - - write_if_flag!("features-opentype" => FEATURES_OPENTYPE); - write_if_flag!("features-aat" => FEATURES_AAT); - write_if_flag!("features-graphite" => FEATURES_GRAPHITE); - write_if_flag!("color-colrv0" => COLOR_COLRV0); - write_if_flag!("color-colrv1" => COLOR_COLRV1); - write_if_flag!("color-svg" => COLOR_SVG); - write_if_flag!("color-sbix" => COLOR_SBIX); - write_if_flag!("color-cbdt" => COLOR_CBDT); - write_if_flag!("variations" => VARIATIONS); - write_if_flag!("palettes" => PALETTES); - write_if_flag!("incremental" => INCREMENTAL); - - Ok(()) - } -} - -/// A POD representation for Gecko. All pointers here are non-owned and as such -/// can't outlive the rule they came from, but we can't enforce that via C++. -/// -/// All the strings are of course utf8. -#[cfg(feature = "gecko")] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum FontFaceSourceListComponent { - Url(*const crate::gecko::url::CssUrl), - Local(*mut crate::gecko_bindings::structs::nsAtom), - FormatHintKeyword(FontFaceSourceFormatKeyword), - FormatHintString { - length: usize, - utf8_bytes: *const u8, - }, - TechFlags(FontFaceSourceTechFlags), -} - -#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[repr(u8)] -#[allow(missing_docs)] -pub enum FontFaceSourceFormat { - Keyword(FontFaceSourceFormatKeyword), - String(String), -} - -/// A `UrlSource` represents a font-face source that has been specified with a -/// `url()` function. -/// -/// -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Clone, Debug, Eq, PartialEq, ToShmem)] -pub struct UrlSource { - /// The specified url. - pub url: SpecifiedUrl, - /// The format hint specified with the `format()` function, if present. - pub format_hint: Option, - /// The font technology flags specified with the `tech()` function, if any. - pub tech_flags: FontFaceSourceTechFlags, -} - -impl ToCss for UrlSource { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write, - { - self.url.to_css(dest)?; - if let Some(hint) = &self.format_hint { - dest.write_str(" format(")?; - hint.to_css(dest)?; - dest.write_char(')')?; - } - if !self.tech_flags.is_empty() { - dest.write_str(" tech(")?; - self.tech_flags.to_css(dest)?; - dest.write_char(')')?; - } - Ok(()) - } -} - -/// A font-display value for a @font-face rule. -/// The font-display descriptor determines how a font face is displayed based -/// on whether and when it is downloaded and ready to use. -#[allow(missing_docs)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive( - Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss, ToShmem, -)] -#[repr(u8)] -pub enum FontDisplay { - Auto, - Block, - Swap, - Fallback, - Optional, -} - -macro_rules! impl_range { - ($range:ident, $component:ident) => { - impl Parse for $range { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let first = $component::parse(context, input)?; - let second = input - .try_parse(|input| $component::parse(context, input)) - .unwrap_or_else(|_| first.clone()); - Ok($range(first, second)) - } - } - impl ToCss for $range { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write, - { - self.0.to_css(dest)?; - if self.0 != self.1 { - dest.write_char(' ')?; - self.1.to_css(dest)?; - } - Ok(()) - } - } - }; -} - -/// The font-weight descriptor: -/// -/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight -#[derive(Clone, Debug, PartialEq, ToShmem)] -pub struct FontWeightRange(pub AbsoluteFontWeight, pub AbsoluteFontWeight); -impl_range!(FontWeightRange, AbsoluteFontWeight); - -/// The computed representation of the above so Gecko can read them easily. -/// -/// This one is needed because cbindgen doesn't know how to generate -/// specified::Number. -#[repr(C)] -#[allow(missing_docs)] -pub struct ComputedFontWeightRange(f32, f32); - -#[inline] -fn sort_range(a: T, b: T) -> (T, T) { - if a > b { - (b, a) - } else { - (a, b) - } -} - -impl FontWeightRange { - /// Returns a computed font-stretch range. - pub fn compute(&self) -> ComputedFontWeightRange { - let (min, max) = sort_range(self.0.compute().value(), self.1.compute().value()); - ComputedFontWeightRange(min, max) - } -} - -/// The font-stretch descriptor: -/// -/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch -#[derive(Clone, Debug, PartialEq, ToShmem)] -pub struct FontStretchRange(pub SpecifiedFontStretch, pub SpecifiedFontStretch); -impl_range!(FontStretchRange, SpecifiedFontStretch); - -/// The computed representation of the above, so that Gecko can read them -/// easily. -#[repr(C)] -#[allow(missing_docs)] -pub struct ComputedFontStretchRange(FontStretch, FontStretch); - -impl FontStretchRange { - /// Returns a computed font-stretch range. - pub fn compute(&self) -> ComputedFontStretchRange { - fn compute_stretch(s: &SpecifiedFontStretch) -> FontStretch { - match *s { - SpecifiedFontStretch::Keyword(ref kw) => kw.compute(), - SpecifiedFontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()), - SpecifiedFontStretch::System(..) => unreachable!(), - } - } - - let (min, max) = sort_range(compute_stretch(&self.0), compute_stretch(&self.1)); - ComputedFontStretchRange(min, max) - } -} - -/// The font-style descriptor: -/// -/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-style -#[derive(Clone, Debug, PartialEq, ToShmem)] -#[allow(missing_docs)] -pub enum FontStyle { - Normal, - Italic, - Oblique(Angle, Angle), -} - -/// The computed representation of the above, with angles in degrees, so that -/// Gecko can read them easily. -#[repr(u8)] -#[allow(missing_docs)] -pub enum ComputedFontStyleDescriptor { - Normal, - Italic, - Oblique(f32, f32), -} - -impl Parse for FontStyle { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let style = SpecifiedFontStyle::parse(context, input)?; - Ok(match style { - GenericFontStyle::Normal => FontStyle::Normal, - GenericFontStyle::Italic => FontStyle::Italic, - GenericFontStyle::Oblique(angle) => { - let second_angle = input - .try_parse(|input| SpecifiedFontStyle::parse_angle(context, input)) - .unwrap_or_else(|_| angle.clone()); - - FontStyle::Oblique(angle, second_angle) - }, - }) - } -} - -impl ToCss for FontStyle { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write, - { - match *self { - FontStyle::Normal => dest.write_str("normal"), - FontStyle::Italic => dest.write_str("italic"), - FontStyle::Oblique(ref first, ref second) => { - dest.write_str("oblique")?; - if *first != SpecifiedFontStyle::default_angle() || first != second { - dest.write_char(' ')?; - first.to_css(dest)?; - } - if first != second { - dest.write_char(' ')?; - second.to_css(dest)?; - } - Ok(()) - }, - } - } -} - -impl FontStyle { - /// Returns a computed font-style descriptor. - pub fn compute(&self) -> ComputedFontStyleDescriptor { - match *self { - FontStyle::Normal => ComputedFontStyleDescriptor::Normal, - FontStyle::Italic => ComputedFontStyleDescriptor::Italic, - FontStyle::Oblique(ref first, ref second) => { - let (min, max) = sort_range( - SpecifiedFontStyle::compute_angle_degrees(first), - SpecifiedFontStyle::compute_angle_degrees(second), - ); - ComputedFontStyleDescriptor::Oblique(min, max) - }, - } - } -} - -/// Parse the block inside a `@font-face` rule. -/// -/// Note that the prelude parsing code lives in the `stylesheets` module. -pub fn parse_font_face_block( - context: &ParserContext, - input: &mut Parser, - location: SourceLocation, -) -> FontFaceRuleData { - let mut rule = FontFaceRuleData::empty(location); - { - let mut parser = FontFaceRuleParser { - context, - rule: &mut rule, - }; - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(declaration) = iter.next() { - if let Err((error, slice)) = declaration { - let location = error.location; - let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error); - context.log_css_error(location, error) - } - } - } - rule -} - -/// A @font-face rule that is known to have font-family and src declarations. -#[cfg(feature = "servo")] -pub struct FontFace<'a>(&'a FontFaceRuleData); - -/// A list of effective sources that we send over through IPC to the font cache. -#[cfg(feature = "servo")] -#[derive(Clone, Debug)] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -pub struct EffectiveSources(Vec); - -#[cfg(feature = "servo")] -impl<'a> FontFace<'a> { - /// Returns the list of effective sources for that font-face, that is the - /// sources which don't list any format hint, or the ones which list at - /// least "truetype" or "opentype". - pub fn effective_sources(&self) -> EffectiveSources { - EffectiveSources( - self.sources() - .0 - .iter() - .rev() - .filter(|source| { - if let Source::Url(ref url_source) = **source { - // We support only opentype fonts and truetype is an alias for - // that format. Sources without format hints need to be - // downloaded in case we support them. - url_source - .format_hint - .as_ref() - .map_or(true, |hint| match hint { - FontFaceSourceFormat::Keyword( - FontFaceSourceFormatKeyword::Truetype - | FontFaceSourceFormatKeyword::Opentype - | FontFaceSourceFormatKeyword::Woff, - ) => true, - FontFaceSourceFormat::String(s) => { - s == "truetype" || s == "opentype" || s == "woff" - } - _ => false, - }) - } else { - true - } - }) - .cloned() - .collect(), - ) - } -} - -#[cfg(feature = "servo")] -impl Iterator for EffectiveSources { - type Item = Source; - fn next(&mut self) -> Option { - self.0.pop() - } - - fn size_hint(&self) -> (usize, Option) { - (self.0.len(), Some(self.0.len())) - } -} - -struct FontFaceRuleParser<'a, 'b: 'a> { - context: &'a ParserContext<'b>, - rule: &'a mut FontFaceRuleData, -} - -/// Default methods reject all at rules. -impl<'a, 'b, 'i> AtRuleParser<'i> for FontFaceRuleParser<'a, 'b> { - type Prelude = (); - type AtRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for FontFaceRuleParser<'a, 'b> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for FontFaceRuleParser<'a, 'b> -{ - fn parse_qualified(&self) -> bool { - false - } - fn parse_declarations(&self) -> bool { - true - } -} - -impl Parse for Source { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - if input - .try_parse(|input| input.expect_function_matching("local")) - .is_ok() - { - return input - .parse_nested_block(|input| FamilyName::parse(context, input)) - .map(Source::Local); - } - - let url = SpecifiedUrl::parse(context, input)?; - - // Parsing optional format() - let format_hint = if input - .try_parse(|input| input.expect_function_matching("format")) - .is_ok() - { - input.parse_nested_block(|input| { - if let Ok(kw) = input.try_parse(FontFaceSourceFormatKeyword::parse) { - Ok(Some(FontFaceSourceFormat::Keyword(kw))) - } else { - let s = input.expect_string()?.as_ref().to_owned(); - Ok(Some(FontFaceSourceFormat::String(s))) - } - })? - } else { - None - }; - - // Parse optional tech() - let tech_flags = if static_prefs::pref!("layout.css.font-tech.enabled") && - input - .try_parse(|input| input.expect_function_matching("tech")) - .is_ok() - { - input.parse_nested_block(|input| FontFaceSourceTechFlags::parse(context, input))? - } else { - FontFaceSourceTechFlags::empty() - }; - - Ok(Source::Url(UrlSource { - url, - format_hint, - tech_flags, - })) - } -} - -macro_rules! is_descriptor_enabled { - ("font-display") => { - static_prefs::pref!("layout.css.font-display.enabled") - }; - ("font-variation-settings") => { - static_prefs::pref!("layout.css.font-variations.enabled") - }; - ("ascent-override") => { - static_prefs::pref!("layout.css.font-metrics-overrides.enabled") - }; - ("descent-override") => { - static_prefs::pref!("layout.css.font-metrics-overrides.enabled") - }; - ("line-gap-override") => { - static_prefs::pref!("layout.css.font-metrics-overrides.enabled") - }; - ("size-adjust") => { - static_prefs::pref!("layout.css.size-adjust.enabled") - }; - ($name:tt) => { - true - }; -} - -macro_rules! font_face_descriptors_common { - ( - $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty, )* - ) => { - /// Data inside a `@font-face` rule. - /// - /// - #[derive(Clone, Debug, PartialEq, ToShmem)] - pub struct FontFaceRuleData { - $( - #[$doc] - pub $ident: Option<$ty>, - )* - /// Line and column of the @font-face rule source code. - pub source_location: SourceLocation, - } - - impl FontFaceRuleData { - /// Create an empty font-face rule - pub fn empty(location: SourceLocation) -> Self { - FontFaceRuleData { - $( - $ident: None, - )* - source_location: location, - } - } - - /// Serialization of declarations in the FontFaceRule - pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { - $( - if let Some(ref value) = self.$ident { - dest.write_str(concat!($name, ": "))?; - value.to_css(&mut CssWriter::new(dest))?; - dest.write_str("; ")?; - } - )* - Ok(()) - } - } - - impl<'a, 'b, 'i> DeclarationParser<'i> for FontFaceRuleParser<'a, 'b> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - match_ignore_ascii_case! { &*name, - $( - $name if is_descriptor_enabled!($name) => { - // DeclarationParser also calls parse_entirely - // so we’d normally not need to, - // but in this case we do because we set the value as a side effect - // rather than returning it. - let value = input.parse_entirely(|i| Parse::parse(self.context, i))?; - self.rule.$ident = Some(value) - }, - )* - _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), - } - Ok(()) - } - } - } -} - -impl ToCssWithGuard for FontFaceRuleData { - // Serialization of FontFaceRule is not specced. - fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@font-face { ")?; - self.decl_to_css(dest)?; - dest.write_char('}') - } -} - -macro_rules! font_face_descriptors { - ( - mandatory descriptors = [ - $( #[$m_doc: meta] $m_name: tt $m_ident: ident / $m_gecko_ident: ident: $m_ty: ty, )* - ] - optional descriptors = [ - $( #[$o_doc: meta] $o_name: tt $o_ident: ident / $o_gecko_ident: ident: $o_ty: ty, )* - ] - ) => { - font_face_descriptors_common! { - $( #[$m_doc] $m_name $m_ident / $m_gecko_ident: $m_ty, )* - $( #[$o_doc] $o_name $o_ident / $o_gecko_ident: $o_ty, )* - } - - impl FontFaceRuleData { - /// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule - /// is valid as far as the CSS parser is concerned even if it doesn’t have - /// a font-family or src declaration. - /// - /// However both are required for the rule to represent an actual font face. - #[cfg(feature = "servo")] - pub fn font_face(&self) -> Option { - if $( self.$m_ident.is_some() )&&* { - Some(FontFace(self)) - } else { - None - } - } - } - - #[cfg(feature = "servo")] - impl<'a> FontFace<'a> { - $( - #[$m_doc] - pub fn $m_ident(&self) -> &$m_ty { - self.0 .$m_ident.as_ref().unwrap() - } - )* - } - } -} - -#[cfg(feature = "gecko")] -font_face_descriptors! { - mandatory descriptors = [ - /// The name of this font face - "font-family" family / mFamily: FamilyName, - - /// The alternative sources for this font face. - "src" sources / mSrc: SourceList, - ] - optional descriptors = [ - /// The style of this font face. - "font-style" style / mStyle: FontStyle, - - /// The weight of this font face. - "font-weight" weight / mWeight: FontWeightRange, - - /// The stretch of this font face. - "font-stretch" stretch / mStretch: FontStretchRange, - - /// The display of this font face. - "font-display" display / mDisplay: FontDisplay, - - /// The ranges of code points outside of which this font face should not be used. - "unicode-range" unicode_range / mUnicodeRange: Vec, - - /// The feature settings of this font face. - "font-feature-settings" feature_settings / mFontFeatureSettings: FontFeatureSettings, - - /// The variation settings of this font face. - "font-variation-settings" variation_settings / mFontVariationSettings: FontVariationSettings, - - /// The language override of this font face. - "font-language-override" language_override / mFontLanguageOverride: font_language_override::SpecifiedValue, - - /// The ascent override for this font face. - "ascent-override" ascent_override / mAscentOverride: MetricsOverride, - - /// The descent override for this font face. - "descent-override" descent_override / mDescentOverride: MetricsOverride, - - /// The line-gap override for this font face. - "line-gap-override" line_gap_override / mLineGapOverride: MetricsOverride, - - /// The size adjustment for this font face. - "size-adjust" size_adjust / mSizeAdjust: NonNegativePercentage, - ] -} - -#[cfg(feature = "servo")] -font_face_descriptors! { - mandatory descriptors = [ - /// The name of this font face - "font-family" family / mFamily: FamilyName, - - /// The alternative sources for this font face. - "src" sources / mSrc: SourceList, - ] - optional descriptors = [ - ] -} diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs deleted file mode 100644 index 391d3653ee6..00000000000 --- a/components/style/font_metrics.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Access to font metrics from the style system. - -#![deny(missing_docs)] - -use crate::values::computed::Length; - -/// Represents the font metrics that style needs from a font to compute the -/// value of certain CSS units like `ex`. -#[derive(Clone, Debug, PartialEq)] -pub struct FontMetrics { - /// The x-height of the font. - pub x_height: Option, - /// The zero advance. This is usually writing mode dependent - pub zero_advance_measure: Option, - /// The cap-height of the font. - pub cap_height: Option, - /// The ideographic-width of the font. - pub ic_width: Option, - /// The ascent of the font (a value is always available for this). - pub ascent: Length, - /// Script scale down factor for math-depth 1. - /// https://w3c.github.io/mathml-core/#dfn-scriptpercentscaledown - pub script_percent_scale_down: Option, - /// Script scale down factor for math-depth 2. - /// https://w3c.github.io/mathml-core/#dfn-scriptscriptpercentscaledown - pub script_script_percent_scale_down: Option, -} - -impl Default for FontMetrics { - fn default() -> Self { - FontMetrics { - x_height: None, - zero_advance_measure: None, - cap_height: None, - ic_width: None, - ascent: Length::new(0.0), - script_percent_scale_down: None, - script_script_percent_scale_down: None, - } - } -} - -/// Type of font metrics to retrieve. -#[derive(Clone, Debug, PartialEq)] -pub enum FontMetricsOrientation { - /// Get metrics for horizontal or vertical according to the Context's - /// writing mode, using horizontal metrics for vertical/mixed - MatchContextPreferHorizontal, - /// Get metrics for horizontal or vertical according to the Context's - /// writing mode, using vertical metrics for vertical/mixed - MatchContextPreferVertical, - /// Force getting horizontal metrics. - Horizontal, -} diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs deleted file mode 100644 index 24bf22d69a7..00000000000 --- a/components/style/gecko/arc_types.rs +++ /dev/null @@ -1,171 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! This file lists all arc FFI types and defines corresponding addref and release functions. This -//! list loosely corresponds to ServoLockedArcTypeList.h file in Gecko. - -#![allow(non_snake_case, missing_docs)] - -use crate::gecko::url::CssUrlData; -use crate::media_queries::MediaList; -use crate::properties::animated_properties::AnimationValue; -use crate::properties::{ComputedValues, PropertyDeclarationBlock}; -use crate::shared_lock::Locked; -use crate::stylesheets::keyframes_rule::Keyframe; -use crate::stylesheets::{ - ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule, - FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule, - MediaRule, NamespaceRule, PageRule, PropertyRule, StyleRule, StylesheetContents, SupportsRule, -}; -use servo_arc::Arc; - -macro_rules! impl_simple_arc_ffi { - ($ty:ty, $addref:ident, $release:ident) => { - #[no_mangle] - pub unsafe extern "C" fn $addref(obj: *const $ty) { - std::mem::forget(Arc::from_raw_addrefed(obj)); - } - - #[no_mangle] - pub unsafe extern "C" fn $release(obj: *const $ty) { - let _ = Arc::from_raw(obj); - } - }; -} - -macro_rules! impl_locked_arc_ffi { - ($servo_type:ty, $alias:ident, $addref:ident, $release:ident) => { - /// A simple alias for a locked type. - pub type $alias = Locked<$servo_type>; - impl_simple_arc_ffi!($alias, $addref, $release); - }; -} - -impl_locked_arc_ffi!( - CssRules, - LockedCssRules, - Servo_CssRules_AddRef, - Servo_CssRules_Release -); -impl_locked_arc_ffi!( - PropertyDeclarationBlock, - LockedDeclarationBlock, - Servo_DeclarationBlock_AddRef, - Servo_DeclarationBlock_Release -); -impl_locked_arc_ffi!( - StyleRule, - LockedStyleRule, - Servo_StyleRule_AddRef, - Servo_StyleRule_Release -); -impl_locked_arc_ffi!( - ImportRule, - LockedImportRule, - Servo_ImportRule_AddRef, - Servo_ImportRule_Release -); -impl_locked_arc_ffi!( - Keyframe, - LockedKeyframe, - Servo_Keyframe_AddRef, - Servo_Keyframe_Release -); -impl_locked_arc_ffi!( - KeyframesRule, - LockedKeyframesRule, - Servo_KeyframesRule_AddRef, - Servo_KeyframesRule_Release -); -impl_simple_arc_ffi!( - LayerBlockRule, - Servo_LayerBlockRule_AddRef, - Servo_LayerBlockRule_Release -); -impl_simple_arc_ffi!( - LayerStatementRule, - Servo_LayerStatementRule_AddRef, - Servo_LayerStatementRule_Release -); -impl_locked_arc_ffi!( - MediaList, - LockedMediaList, - Servo_MediaList_AddRef, - Servo_MediaList_Release -); -impl_simple_arc_ffi!(MediaRule, Servo_MediaRule_AddRef, Servo_MediaRule_Release); -impl_simple_arc_ffi!( - NamespaceRule, - Servo_NamespaceRule_AddRef, - Servo_NamespaceRule_Release -); -impl_locked_arc_ffi!( - PageRule, - LockedPageRule, - Servo_PageRule_AddRef, - Servo_PageRule_Release -); -impl_simple_arc_ffi!( - PropertyRule, - Servo_PropertyRule_AddRef, - Servo_PropertyRule_Release -); -impl_simple_arc_ffi!( - SupportsRule, - Servo_SupportsRule_AddRef, - Servo_SupportsRule_Release -); -impl_simple_arc_ffi!( - ContainerRule, - Servo_ContainerRule_AddRef, - Servo_ContainerRule_Release -); -impl_simple_arc_ffi!( - DocumentRule, - Servo_DocumentRule_AddRef, - Servo_DocumentRule_Release -); -impl_simple_arc_ffi!( - FontFeatureValuesRule, - Servo_FontFeatureValuesRule_AddRef, - Servo_FontFeatureValuesRule_Release -); -impl_simple_arc_ffi!( - FontPaletteValuesRule, - Servo_FontPaletteValuesRule_AddRef, - Servo_FontPaletteValuesRule_Release -); -impl_locked_arc_ffi!( - FontFaceRule, - LockedFontFaceRule, - Servo_FontFaceRule_AddRef, - Servo_FontFaceRule_Release -); -impl_locked_arc_ffi!( - CounterStyleRule, - LockedCounterStyleRule, - Servo_CounterStyleRule_AddRef, - Servo_CounterStyleRule_Release -); - -impl_simple_arc_ffi!( - StylesheetContents, - Servo_StyleSheetContents_AddRef, - Servo_StyleSheetContents_Release -); -impl_simple_arc_ffi!( - CssUrlData, - Servo_CssUrlData_AddRef, - Servo_CssUrlData_Release -); -impl_simple_arc_ffi!( - ComputedValues, - Servo_ComputedStyle_AddRef, - Servo_ComputedStyle_Release -); -impl_simple_arc_ffi!( - AnimationValue, - Servo_AnimationValue_AddRef, - Servo_AnimationValue_Release -); diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs deleted file mode 100644 index ea3700a3235..00000000000 --- a/components/style/gecko/conversions.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! This module contains conversion helpers between Servo and Gecko types -//! Ideally, it would be in geckolib itself, but coherence -//! forces us to keep the traits and implementations here -//! -//! FIXME(emilio): This file should generally just die. - -#![allow(unsafe_code)] - -use crate::gecko_bindings::structs::{nsresult, Matrix4x4Components}; -use crate::stylesheets::RulesMutateError; -use crate::values::computed::transform::Matrix3D; - -impl From for nsresult { - fn from(other: RulesMutateError) -> Self { - match other { - RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR, - RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR, - RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, - RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR, - } - } -} - -impl<'a> From<&'a Matrix4x4Components> for Matrix3D { - fn from(m: &'a Matrix4x4Components) -> Matrix3D { - Matrix3D { - m11: m[0], - m12: m[1], - m13: m[2], - m14: m[3], - m21: m[4], - m22: m[5], - m23: m[6], - m24: m[7], - m31: m[8], - m32: m[9], - m33: m[10], - m34: m[11], - m41: m[12], - m42: m[13], - m43: m[14], - m44: m[15], - } - } -} - -impl From for Matrix4x4Components { - fn from(matrix: Matrix3D) -> Self { - [ - matrix.m11, matrix.m12, matrix.m13, matrix.m14, matrix.m21, matrix.m22, matrix.m23, - matrix.m24, matrix.m31, matrix.m32, matrix.m33, matrix.m34, matrix.m41, matrix.m42, - matrix.m43, matrix.m44, - ] - } -} diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs deleted file mode 100644 index c4a5554c5e5..00000000000 --- a/components/style/gecko/data.rs +++ /dev/null @@ -1,198 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Data needed to style a Gecko document. - -use crate::dom::TElement; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::{ - self, ServoStyleSetSizes, StyleSheet as DomStyleSheet, StyleSheetInfo, -}; -use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey}; -use crate::media_queries::{Device, MediaList}; -use crate::properties::ComputedValues; -use crate::selector_parser::SnapshotMap; -use crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards}; -use crate::stylesheets::{StylesheetContents, StylesheetInDocument}; -use crate::stylist::Stylist; -use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; -use malloc_size_of::MallocSizeOfOps; -use servo_arc::Arc; -use std::fmt; - -/// Little wrapper to a Gecko style sheet. -#[derive(Eq, PartialEq)] -pub struct GeckoStyleSheet(*const DomStyleSheet); - -// NOTE(emilio): These are kind of a lie. We allow to make these Send + Sync so that other data -// structures can also be Send and Sync, but Gecko's stylesheets are main-thread-reference-counted. -// -// We assert that we reference-count in the right thread (in the Addref/Release implementations). -// Sending these to a different thread can't really happen (it could theoretically really happen if -// we allowed @import rules inside a nested style rule, but that can't happen per spec and would be -// a parser bug, caught by the asserts). -unsafe impl Send for GeckoStyleSheet {} -unsafe impl Sync for GeckoStyleSheet {} - -impl fmt::Debug for GeckoStyleSheet { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let contents = self.contents(); - formatter - .debug_struct("GeckoStyleSheet") - .field("origin", &contents.origin) - .field("url_data", &*contents.url_data.read()) - .finish() - } -} - -impl ToMediaListKey for crate::gecko::data::GeckoStyleSheet { - fn to_media_list_key(&self) -> MediaListKey { - use std::mem; - unsafe { MediaListKey::from_raw(mem::transmute(self.0)) } - } -} - -impl GeckoStyleSheet { - /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer. - #[inline] - pub unsafe fn new(s: *const DomStyleSheet) -> Self { - debug_assert!(!s.is_null()); - bindings::Gecko_StyleSheet_AddRef(s); - Self::from_addrefed(s) - } - - /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer that - /// already holds a strong reference. - #[inline] - pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self { - assert!(!s.is_null()); - GeckoStyleSheet(s) - } - - /// HACK(emilio): This is so that we can avoid crashing release due to - /// bug 1719963 and can hopefully get a useful report from fuzzers. - #[inline] - pub fn hack_is_null(&self) -> bool { - self.0.is_null() - } - - /// Get the raw `StyleSheet` that we're wrapping. - pub fn raw(&self) -> &DomStyleSheet { - unsafe { &*self.0 } - } - - fn inner(&self) -> &StyleSheetInfo { - unsafe { &*(self.raw().mInner as *const StyleSheetInfo) } - } -} - -impl Drop for GeckoStyleSheet { - fn drop(&mut self) { - unsafe { bindings::Gecko_StyleSheet_Release(self.0) }; - } -} - -impl Clone for GeckoStyleSheet { - fn clone(&self) -> Self { - unsafe { bindings::Gecko_StyleSheet_AddRef(self.0) }; - GeckoStyleSheet(self.0) - } -} - -impl StylesheetInDocument for GeckoStyleSheet { - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList; - unsafe { - let dom_media_list = self.raw().mMedia.mRawPtr as *const DomMediaList; - if dom_media_list.is_null() { - return None; - } - let list = &*(*dom_media_list).mRawList.mRawPtr; - Some(list.read_with(guard)) - } - } - - // All the stylesheets Servo knows about are enabled, because that state is - // handled externally by Gecko. - #[inline] - fn enabled(&self) -> bool { - true - } - - #[inline] - fn contents(&self) -> &StylesheetContents { - debug_assert!(!self.inner().mContents.mRawPtr.is_null()); - unsafe { &*self.inner().mContents.mRawPtr } - } -} - -/// The container for data that a Servo-backed Gecko document needs to style -/// itself. -pub struct PerDocumentStyleDataImpl { - /// Rule processor. - pub stylist: Stylist, - - /// A cache from element to resolved style. - pub undisplayed_style_cache: crate::traversal::UndisplayedStyleCache, - - /// The generation for which our cache is valid. - pub undisplayed_style_cache_generation: u64, -} - -/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics -/// and unexpected races while trying to mutate it. -pub struct PerDocumentStyleData(AtomicRefCell); - -impl PerDocumentStyleData { - /// Create a `PerDocumentStyleData`. - pub fn new(document: *const structs::Document) -> Self { - let device = Device::new(document); - let quirks_mode = device.document().mCompatMode; - - PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl { - stylist: Stylist::new(device, quirks_mode.into()), - undisplayed_style_cache: Default::default(), - undisplayed_style_cache_generation: 0, - })) - } - - /// Get an immutable reference to this style data. - pub fn borrow(&self) -> AtomicRef { - self.0.borrow() - } - - /// Get an mutable reference to this style data. - pub fn borrow_mut(&self) -> AtomicRefMut { - self.0.borrow_mut() - } -} - -impl PerDocumentStyleDataImpl { - /// Recreate the style data if the stylesheets have changed. - pub fn flush_stylesheets( - &mut self, - guard: &SharedRwLockReadGuard, - document_element: Option, - snapshots: Option<&SnapshotMap>, - ) -> bool - where - E: TElement, - { - self.stylist - .flush(&StylesheetGuards::same(guard), document_element, snapshots) - } - - /// Get the default computed values for this document. - pub fn default_computed_values(&self) -> &Arc { - self.stylist.device().default_computed_values_arc() - } - - /// Measure heap usage. - pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { - self.stylist.add_size_of(ops, sizes); - } -} - -/// The gecko-specific AuthorStyles instantiation. -pub type AuthorStyles = crate::author_styles::AuthorStyles; diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs deleted file mode 100644 index 4ca746ea84d..00000000000 --- a/components/style/gecko/media_features.rs +++ /dev/null @@ -1,1028 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko's media feature list and evaluator. - -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs; -use crate::gecko_bindings::structs::ScreenColorGamut; -use crate::media_queries::{Device, MediaType}; -use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::Orientation; -use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; -use app_units::Au; -use euclid::default::Size2D; - -fn device_size(device: &Device) -> Size2D { - let mut width = 0; - let mut height = 0; - unsafe { - bindings::Gecko_MediaFeatures_GetDeviceSize(device.document(), &mut width, &mut height); - } - Size2D::new(Au(width), Au(height)) -} - -/// https://drafts.csswg.org/mediaqueries-4/#width -fn eval_width(context: &Context) -> CSSPixelLength { - CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px()) -} - -/// https://drafts.csswg.org/mediaqueries-4/#device-width -fn eval_device_width(context: &Context) -> CSSPixelLength { - CSSPixelLength::new(device_size(context.device()).width.to_f32_px()) -} - -/// https://drafts.csswg.org/mediaqueries-4/#height -fn eval_height(context: &Context) -> CSSPixelLength { - CSSPixelLength::new(context.device().au_viewport_size().height.to_f32_px()) -} - -/// https://drafts.csswg.org/mediaqueries-4/#device-height -fn eval_device_height(context: &Context) -> CSSPixelLength { - CSSPixelLength::new(device_size(context.device()).height.to_f32_px()) -} - -fn eval_aspect_ratio_for(context: &Context, get_size: F) -> Ratio -where - F: FnOnce(&Device) -> Size2D, -{ - let size = get_size(context.device()); - Ratio::new(size.width.0 as f32, size.height.0 as f32) -} - -/// https://drafts.csswg.org/mediaqueries-4/#aspect-ratio -fn eval_aspect_ratio(context: &Context) -> Ratio { - eval_aspect_ratio_for(context, Device::au_viewport_size) -} - -/// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio -fn eval_device_aspect_ratio(context: &Context) -> Ratio { - eval_aspect_ratio_for(context, device_size) -} - -/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio -fn eval_device_pixel_ratio(context: &Context) -> f32 { - eval_resolution(context).dppx() -} - -/// https://drafts.csswg.org/mediaqueries-4/#orientation -fn eval_orientation(context: &Context, value: Option) -> bool { - Orientation::eval(context.device().au_viewport_size(), value) -} - -/// FIXME: There's no spec for `-moz-device-orientation`. -fn eval_device_orientation(context: &Context, value: Option) -> bool { - Orientation::eval(device_size(context.device()), value) -} - -/// Values for the display-mode media feature. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum DisplayMode { - Browser = 0, - MinimalUi, - Standalone, - Fullscreen, -} - -/// https://w3c.github.io/manifest/#the-display-mode-media-feature -fn eval_display_mode(context: &Context, query_value: Option) -> bool { - match query_value { - Some(v) => { - v == unsafe { - bindings::Gecko_MediaFeatures_GetDisplayMode(context.device().document()) - } - }, - None => true, - } -} - -/// https://drafts.csswg.org/mediaqueries-4/#grid -fn eval_grid(_: &Context) -> bool { - // Gecko doesn't support grid devices (e.g., ttys), so the 'grid' feature - // is always 0. - false -} - -/// https://compat.spec.whatwg.org/#css-media-queries-webkit-transform-3d -fn eval_transform_3d(_: &Context) -> bool { - true -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum Scan { - Progressive, - Interlace, -} - -/// https://drafts.csswg.org/mediaqueries-4/#scan -fn eval_scan(_: &Context, _: Option) -> bool { - // Since Gecko doesn't support the 'tv' media type, the 'scan' feature never - // matches. - false -} - -/// https://drafts.csswg.org/mediaqueries-4/#color -fn eval_color(context: &Context) -> i32 { - unsafe { bindings::Gecko_MediaFeatures_GetColorDepth(context.device().document()) } -} - -/// https://drafts.csswg.org/mediaqueries-4/#color-index -fn eval_color_index(_: &Context) -> i32 { - // We should return zero if the device does not use a color lookup table. - 0 -} - -/// https://drafts.csswg.org/mediaqueries-4/#monochrome -fn eval_monochrome(context: &Context) -> i32 { - // For color devices we should return 0. - unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(context.device().document()) } -} - -/// Values for the color-gamut media feature. -/// This implements PartialOrd so that lower values will correctly match -/// higher capabilities. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)] -#[repr(u8)] -enum ColorGamut { - /// The sRGB gamut. - Srgb, - /// The gamut specified by the Display P3 Color Space. - P3, - /// The gamut specified by the ITU-R Recommendation BT.2020 Color Space. - Rec2020, -} - -/// https://drafts.csswg.org/mediaqueries-4/#color-gamut -fn eval_color_gamut(context: &Context, query_value: Option) -> bool { - let query_value = match query_value { - Some(v) => v, - None => return false, - }; - let color_gamut = - unsafe { bindings::Gecko_MediaFeatures_ColorGamut(context.device().document()) }; - // Match if our color gamut is at least as wide as the query value - query_value <= - match color_gamut { - // EndGuard_ is not a valid color gamut, so the default color-gamut is used. - ScreenColorGamut::Srgb | ScreenColorGamut::EndGuard_ => ColorGamut::Srgb, - ScreenColorGamut::P3 => ColorGamut::P3, - ScreenColorGamut::Rec2020 => ColorGamut::Rec2020, - } -} - -/// https://drafts.csswg.org/mediaqueries-4/#resolution -fn eval_resolution(context: &Context) -> Resolution { - let resolution_dppx = - unsafe { bindings::Gecko_MediaFeatures_GetResolution(context.device().document()) }; - Resolution::from_dppx(resolution_dppx) -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum PrefersReducedMotion { - NoPreference, - Reduce, -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum PrefersReducedTransparency { - NoPreference, - Reduce, -} - -/// Values for the prefers-color-scheme media feature. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum PrefersColorScheme { - Light, - Dark, -} - -/// Values for the dynamic-range and video-dynamic-range media features. -/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range -/// This implements PartialOrd so that lower values will correctly match -/// higher capabilities. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum DynamicRange { - Standard, - High, -} - -/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion -fn eval_prefers_reduced_motion( - context: &Context, - query_value: Option, -) -> bool { - let prefers_reduced = - unsafe { bindings::Gecko_MediaFeatures_PrefersReducedMotion(context.device().document()) }; - let query_value = match query_value { - Some(v) => v, - None => return prefers_reduced, - }; - - match query_value { - PrefersReducedMotion::NoPreference => !prefers_reduced, - PrefersReducedMotion::Reduce => prefers_reduced, - } -} - -/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency -fn eval_prefers_reduced_transparency( - context: &Context, - query_value: Option, -) -> bool { - let prefers_reduced = unsafe { - bindings::Gecko_MediaFeatures_PrefersReducedTransparency(context.device().document()) - }; - let query_value = match query_value { - Some(v) => v, - None => return prefers_reduced, - }; - - match query_value { - PrefersReducedTransparency::NoPreference => !prefers_reduced, - PrefersReducedTransparency::Reduce => prefers_reduced, - } -} - -/// Possible values for prefers-contrast media query. -/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -pub enum PrefersContrast { - /// More contrast is preferred. - More, - /// Low contrast is preferred. - Less, - /// Custom (not more, not less). - Custom, - /// The default value if neither high or low contrast is enabled. - NoPreference, -} - -/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast -fn eval_prefers_contrast(context: &Context, query_value: Option) -> bool { - let prefers_contrast = - unsafe { bindings::Gecko_MediaFeatures_PrefersContrast(context.device().document()) }; - match query_value { - Some(v) => v == prefers_contrast, - None => prefers_contrast != PrefersContrast::NoPreference, - } -} - -/// Possible values for the forced-colors media query. -/// https://drafts.csswg.org/mediaqueries-5/#forced-colors -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -pub enum ForcedColors { - /// Page colors are not being forced. - None, - /// Page colors are being forced. - Active, -} - -/// https://drafts.csswg.org/mediaqueries-5/#forced-colors -fn eval_forced_colors(context: &Context, query_value: Option) -> bool { - let forced = !context.device().use_document_colors(); - match query_value { - Some(query_value) => forced == (query_value == ForcedColors::Active), - None => forced, - } -} - -/// Possible values for the inverted-colors media query. -/// https://drafts.csswg.org/mediaqueries-5/#inverted -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum InvertedColors { - /// Colors are displayed normally. - None, - /// All pixels within the displayed area have been inverted. - Inverted, -} - -/// https://drafts.csswg.org/mediaqueries-5/#inverted -fn eval_inverted_colors(context: &Context, query_value: Option) -> bool { - let inverted_colors = - unsafe { bindings::Gecko_MediaFeatures_InvertedColors(context.device().document()) }; - let query_value = match query_value { - Some(v) => v, - None => return inverted_colors, - }; - - match query_value { - InvertedColors::None => !inverted_colors, - InvertedColors::Inverted => inverted_colors, - } -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum OverflowBlock { - None, - Scroll, - Paged, -} - -/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-block -fn eval_overflow_block(context: &Context, query_value: Option) -> bool { - // For the time being, assume that printing (including previews) - // is the only time when we paginate, and we are otherwise always - // scrolling. This is true at the moment in Firefox, but may need - // updating in the future (e.g., ebook readers built with Stylo, a - // billboard mode that doesn't support overflow at all). - // - // If this ever changes, don't forget to change eval_overflow_inline too. - let scrolling = context.device().media_type() != MediaType::print(); - let query_value = match query_value { - Some(v) => v, - None => return true, - }; - - match query_value { - OverflowBlock::None => false, - OverflowBlock::Scroll => scrolling, - OverflowBlock::Paged => !scrolling, - } -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum OverflowInline { - None, - Scroll, -} - -/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-inline -fn eval_overflow_inline(context: &Context, query_value: Option) -> bool { - // See the note in eval_overflow_block. - let scrolling = context.device().media_type() != MediaType::print(); - let query_value = match query_value { - Some(v) => v, - None => return scrolling, - }; - - match query_value { - OverflowInline::None => !scrolling, - OverflowInline::Scroll => scrolling, - } -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum Update { - None, - Slow, - Fast, -} - -/// https://drafts.csswg.org/mediaqueries-4/#update -fn eval_update(context: &Context, query_value: Option) -> bool { - // This has similar caveats to those described in eval_overflow_block. - // For now, we report that print (incl. print media simulation, - // which can in fact update but is limited to the developer tools) - // is `update: none` and that all other contexts are `update: fast`, - // which may not be true for future platforms, like e-ink devices. - let can_update = context.device().media_type() != MediaType::print(); - let query_value = match query_value { - Some(v) => v, - None => return can_update, - }; - - match query_value { - Update::None => !can_update, - Update::Slow => false, - Update::Fast => can_update, - } -} - -fn do_eval_prefers_color_scheme( - context: &Context, - use_content: bool, - query_value: Option, -) -> bool { - let prefers_color_scheme = unsafe { - bindings::Gecko_MediaFeatures_PrefersColorScheme(context.device().document(), use_content) - }; - match query_value { - Some(v) => prefers_color_scheme == v, - None => true, - } -} - -/// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme -fn eval_prefers_color_scheme(context: &Context, query_value: Option) -> bool { - do_eval_prefers_color_scheme(context, /* use_content = */ false, query_value) -} - -fn eval_content_prefers_color_scheme( - context: &Context, - query_value: Option, -) -> bool { - do_eval_prefers_color_scheme(context, /* use_content = */ true, query_value) -} - -/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range -fn eval_dynamic_range(context: &Context, query_value: Option) -> bool { - let dynamic_range = - unsafe { bindings::Gecko_MediaFeatures_DynamicRange(context.device().document()) }; - match query_value { - Some(v) => dynamic_range >= v, - None => false, - } -} -/// https://drafts.csswg.org/mediaqueries-5/#video-dynamic-range -fn eval_video_dynamic_range(context: &Context, query_value: Option) -> bool { - let dynamic_range = - unsafe { bindings::Gecko_MediaFeatures_VideoDynamicRange(context.device().document()) }; - match query_value { - Some(v) => dynamic_range >= v, - None => false, - } -} - -bitflags! { - /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction - struct PointerCapabilities: u8 { - const COARSE = structs::PointerCapabilities_Coarse; - const FINE = structs::PointerCapabilities_Fine; - const HOVER = structs::PointerCapabilities_Hover; - } -} - -fn primary_pointer_capabilities(context: &Context) -> PointerCapabilities { - PointerCapabilities::from_bits_truncate(unsafe { - bindings::Gecko_MediaFeatures_PrimaryPointerCapabilities(context.device().document()) - }) -} - -fn all_pointer_capabilities(context: &Context) -> PointerCapabilities { - PointerCapabilities::from_bits_truncate(unsafe { - bindings::Gecko_MediaFeatures_AllPointerCapabilities(context.device().document()) - }) -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum Pointer { - None, - Coarse, - Fine, -} - -fn eval_pointer_capabilities( - query_value: Option, - pointer_capabilities: PointerCapabilities, -) -> bool { - let query_value = match query_value { - Some(v) => v, - None => return !pointer_capabilities.is_empty(), - }; - - match query_value { - Pointer::None => pointer_capabilities.is_empty(), - Pointer::Coarse => pointer_capabilities.intersects(PointerCapabilities::COARSE), - Pointer::Fine => pointer_capabilities.intersects(PointerCapabilities::FINE), - } -} - -/// https://drafts.csswg.org/mediaqueries-4/#pointer -fn eval_pointer(context: &Context, query_value: Option) -> bool { - eval_pointer_capabilities(query_value, primary_pointer_capabilities(context)) -} - -/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-pointer -fn eval_any_pointer(context: &Context, query_value: Option) -> bool { - eval_pointer_capabilities(query_value, all_pointer_capabilities(context)) -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum Hover { - None, - Hover, -} - -fn eval_hover_capabilities( - query_value: Option, - pointer_capabilities: PointerCapabilities, -) -> bool { - let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER); - let query_value = match query_value { - Some(v) => v, - None => return can_hover, - }; - - match query_value { - Hover::None => !can_hover, - Hover::Hover => can_hover, - } -} - -/// https://drafts.csswg.org/mediaqueries-4/#hover -fn eval_hover(context: &Context, query_value: Option) -> bool { - eval_hover_capabilities(query_value, primary_pointer_capabilities(context)) -} - -/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-hover -fn eval_any_hover(context: &Context, query_value: Option) -> bool { - eval_hover_capabilities(query_value, all_pointer_capabilities(context)) -} - -fn eval_moz_is_glyph(context: &Context) -> bool { - context.device().document().mIsSVGGlyphsDocument() -} - -fn eval_moz_print_preview(context: &Context) -> bool { - let is_print_preview = context.device().is_print_preview(); - if is_print_preview { - debug_assert_eq!(context.device().media_type(), MediaType::print()); - } - is_print_preview -} - -fn eval_moz_non_native_content_theme(context: &Context) -> bool { - unsafe { bindings::Gecko_MediaFeatures_ShouldAvoidNativeTheme(context.device().document()) } -} - -fn eval_moz_is_resource_document(context: &Context) -> bool { - unsafe { bindings::Gecko_MediaFeatures_IsResourceDocument(context.device().document()) } -} - -/// Allows front-end CSS to discern platform via media queries. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -pub enum Platform { - /// Matches any Android version. - Android, - /// For our purposes here, "linux" is just "gtk" (so unix-but-not-mac). - /// There's no need for our front-end code to differentiate between those - /// platforms and they already use the "linux" string elsewhere (e.g., - /// toolkit/themes/linux). - Linux, - /// Matches any macOS version. - Macos, - /// Matches any Windows version. - Windows, - /// Matches only Windows 7. - WindowsWin7, - /// Matches only Windows 8. - WindowsWin8, - /// Matches windows 10 and actually matches windows 11 too, as of right now. - WindowsWin10, -} - -fn eval_moz_platform(_: &Context, query_value: Option) -> bool { - let query_value = match query_value { - Some(v) => v, - None => return false, - }; - - unsafe { bindings::Gecko_MediaFeatures_MatchesPlatform(query_value) } -} - -/// Values for the scripting media feature. -/// https://drafts.csswg.org/mediaqueries-5/#scripting -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -pub enum Scripting { - /// Scripting is not supported or not enabled - None, - /// Scripting is supported and enabled, but only for initial page load - /// We will never match this value as it is intended for non-browser user agents, - /// but it is part of the spec so we should still parse it. - /// See: https://github.com/w3c/csswg-drafts/issues/8621 - InitialOnly, - /// Scripting is supported and enabled - Enabled, -} - -/// https://drafts.csswg.org/mediaqueries-5/#scripting -fn eval_scripting(context: &Context, query_value: Option) -> bool { - let scripting = unsafe { bindings::Gecko_MediaFeatures_Scripting(context.device().document()) }; - match query_value { - Some(v) => v == scripting, - None => scripting != Scripting::None, - } -} - -fn eval_moz_windows_non_native_menus(context: &Context) -> bool { - unsafe { bindings::Gecko_MediaFeatures_WindowsNonNativeMenus(context.device().document()) } -} - -fn eval_moz_overlay_scrollbars(context: &Context) -> bool { - unsafe { bindings::Gecko_MediaFeatures_UseOverlayScrollbars(context.device().document()) } -} - -fn get_lnf_int(int_id: i32) -> i32 { - unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) } -} - -fn get_lnf_int_as_bool(int_id: i32) -> bool { - get_lnf_int(int_id) != 0 -} - -fn get_scrollbar_start_backward(int_id: i32) -> bool { - (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartBackward as i32) != 0 -} - -fn get_scrollbar_start_forward(int_id: i32) -> bool { - (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_StartForward as i32) != 0 -} - -fn get_scrollbar_end_backward(int_id: i32) -> bool { - (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndBackward as i32) != 0 -} - -fn get_scrollbar_end_forward(int_id: i32) -> bool { - (get_lnf_int(int_id) & bindings::LookAndFeel_eScrollArrow_EndForward as i32) != 0 -} - -macro_rules! lnf_int_feature { - ($feature_name:expr, $int_id:ident, $get_value:ident) => {{ - fn __eval(_: &Context) -> bool { - $get_value(bindings::LookAndFeel_IntID::$int_id as i32) - } - - feature!( - $feature_name, - AllowsRanges::No, - Evaluator::BoolInteger(__eval), - FeatureFlags::CHROME_AND_UA_ONLY, - ) - }}; - ($feature_name:expr, $int_id:ident) => {{ - lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool) - }}; -} - -/// bool pref-based features are an slightly less convenient to start using -/// version of @supports -moz-bool-pref, but with some benefits, mainly that -/// they can support dynamic changes, and don't require a pref lookup every time -/// they're used. -/// -/// In order to use them you need to make sure that the pref defined as a static -/// pref, with `rust: true`. The feature name needs to be defined in -/// `StaticAtoms.py` just like the others. In order to support dynamic changes, -/// you also need to add them to kMediaQueryPrefs in nsXPLookAndFeel.cpp -#[allow(unused)] -macro_rules! bool_pref_feature { - ($feature_name:expr, $pref:tt) => {{ - fn __eval(_: &Context) -> bool { - static_prefs::pref!($pref) - } - - feature!( - $feature_name, - AllowsRanges::No, - Evaluator::BoolInteger(__eval), - FeatureFlags::CHROME_AND_UA_ONLY, - ) - }}; -} - -/// Adding new media features requires (1) adding the new feature to this -/// array, with appropriate entries (and potentially any new code needed -/// to support new types in these entries and (2) ensuring that either -/// nsPresContext::MediaFeatureValuesChanged is called when the value that -/// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [QueryFeatureDescription; 67] = [ - feature!( - atom!("width"), - AllowsRanges::Yes, - Evaluator::Length(eval_width), - FeatureFlags::VIEWPORT_DEPENDENT, - ), - feature!( - atom!("height"), - AllowsRanges::Yes, - Evaluator::Length(eval_height), - FeatureFlags::VIEWPORT_DEPENDENT, - ), - feature!( - atom!("aspect-ratio"), - AllowsRanges::Yes, - Evaluator::NumberRatio(eval_aspect_ratio), - FeatureFlags::VIEWPORT_DEPENDENT, - ), - feature!( - atom!("orientation"), - AllowsRanges::No, - keyword_evaluator!(eval_orientation, Orientation), - FeatureFlags::VIEWPORT_DEPENDENT, - ), - feature!( - atom!("device-width"), - AllowsRanges::Yes, - Evaluator::Length(eval_device_width), - FeatureFlags::empty(), - ), - feature!( - atom!("device-height"), - AllowsRanges::Yes, - Evaluator::Length(eval_device_height), - FeatureFlags::empty(), - ), - feature!( - atom!("device-aspect-ratio"), - AllowsRanges::Yes, - Evaluator::NumberRatio(eval_device_aspect_ratio), - FeatureFlags::empty(), - ), - feature!( - atom!("-moz-device-orientation"), - AllowsRanges::No, - keyword_evaluator!(eval_device_orientation, Orientation), - FeatureFlags::empty(), - ), - // Webkit extensions that we support for de-facto web compatibility. - // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref): - feature!( - atom!("device-pixel-ratio"), - AllowsRanges::Yes, - Evaluator::Float(eval_device_pixel_ratio), - FeatureFlags::WEBKIT_PREFIX, - ), - // -webkit-transform-3d. - feature!( - atom!("transform-3d"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_transform_3d), - FeatureFlags::WEBKIT_PREFIX, - ), - feature!( - atom!("-moz-device-pixel-ratio"), - AllowsRanges::Yes, - Evaluator::Float(eval_device_pixel_ratio), - FeatureFlags::empty(), - ), - feature!( - atom!("resolution"), - AllowsRanges::Yes, - Evaluator::Resolution(eval_resolution), - FeatureFlags::empty(), - ), - feature!( - atom!("display-mode"), - AllowsRanges::No, - keyword_evaluator!(eval_display_mode, DisplayMode), - FeatureFlags::empty(), - ), - feature!( - atom!("grid"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_grid), - FeatureFlags::empty(), - ), - feature!( - atom!("scan"), - AllowsRanges::No, - keyword_evaluator!(eval_scan, Scan), - FeatureFlags::empty(), - ), - feature!( - atom!("color"), - AllowsRanges::Yes, - Evaluator::Integer(eval_color), - FeatureFlags::empty(), - ), - feature!( - atom!("color-index"), - AllowsRanges::Yes, - Evaluator::Integer(eval_color_index), - FeatureFlags::empty(), - ), - feature!( - atom!("monochrome"), - AllowsRanges::Yes, - Evaluator::Integer(eval_monochrome), - FeatureFlags::empty(), - ), - feature!( - atom!("color-gamut"), - AllowsRanges::No, - keyword_evaluator!(eval_color_gamut, ColorGamut), - FeatureFlags::empty(), - ), - feature!( - atom!("prefers-reduced-motion"), - AllowsRanges::No, - keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion), - FeatureFlags::empty(), - ), - feature!( - atom!("prefers-reduced-transparency"), - AllowsRanges::No, - keyword_evaluator!( - eval_prefers_reduced_transparency, - PrefersReducedTransparency - ), - FeatureFlags::empty(), - ), - feature!( - atom!("prefers-contrast"), - AllowsRanges::No, - keyword_evaluator!(eval_prefers_contrast, PrefersContrast), - // Note: by default this is only enabled in browser chrome and - // ua. It can be enabled on the web via the - // layout.css.prefers-contrast.enabled preference. See - // disabed_by_pref in media_feature_expression.rs for how that - // is done. - FeatureFlags::empty(), - ), - feature!( - atom!("forced-colors"), - AllowsRanges::No, - keyword_evaluator!(eval_forced_colors, ForcedColors), - FeatureFlags::empty(), - ), - feature!( - atom!("inverted-colors"), - AllowsRanges::No, - keyword_evaluator!(eval_inverted_colors, InvertedColors), - FeatureFlags::empty(), - ), - feature!( - atom!("overflow-block"), - AllowsRanges::No, - keyword_evaluator!(eval_overflow_block, OverflowBlock), - FeatureFlags::empty(), - ), - feature!( - atom!("overflow-inline"), - AllowsRanges::No, - keyword_evaluator!(eval_overflow_inline, OverflowInline), - FeatureFlags::empty(), - ), - feature!( - atom!("update"), - AllowsRanges::No, - keyword_evaluator!(eval_update, Update), - FeatureFlags::empty(), - ), - feature!( - atom!("prefers-color-scheme"), - AllowsRanges::No, - keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), - FeatureFlags::empty(), - ), - feature!( - atom!("dynamic-range"), - AllowsRanges::No, - keyword_evaluator!(eval_dynamic_range, DynamicRange), - FeatureFlags::empty(), - ), - feature!( - atom!("video-dynamic-range"), - AllowsRanges::No, - keyword_evaluator!(eval_video_dynamic_range, DynamicRange), - FeatureFlags::empty(), - ), - feature!( - atom!("scripting"), - AllowsRanges::No, - keyword_evaluator!(eval_scripting, Scripting), - FeatureFlags::empty(), - ), - // Evaluates to the preferred color scheme for content. Only useful in - // chrome context, where the chrome color-scheme and the content - // color-scheme might differ. - feature!( - atom!("-moz-content-prefers-color-scheme"), - AllowsRanges::No, - keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("pointer"), - AllowsRanges::No, - keyword_evaluator!(eval_pointer, Pointer), - FeatureFlags::empty(), - ), - feature!( - atom!("any-pointer"), - AllowsRanges::No, - keyword_evaluator!(eval_any_pointer, Pointer), - FeatureFlags::empty(), - ), - feature!( - atom!("hover"), - AllowsRanges::No, - keyword_evaluator!(eval_hover, Hover), - FeatureFlags::empty(), - ), - feature!( - atom!("any-hover"), - AllowsRanges::No, - keyword_evaluator!(eval_any_hover, Hover), - FeatureFlags::empty(), - ), - // Internal -moz-is-glyph media feature: applies only inside SVG glyphs. - // Internal because it is really only useful in the user agent anyway - // and therefore not worth standardizing. - feature!( - atom!("-moz-is-glyph"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_is_glyph), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-is-resource-document"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_is_resource_document), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-platform"), - AllowsRanges::No, - keyword_evaluator!(eval_moz_platform, Platform), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-print-preview"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_print_preview), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-non-native-content-theme"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_non_native_content_theme), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-windows-non-native-menus"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_windows_non_native_menus), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - feature!( - atom!("-moz-overlay-scrollbars"), - AllowsRanges::No, - Evaluator::BoolInteger(eval_moz_overlay_scrollbars), - FeatureFlags::CHROME_AND_UA_ONLY, - ), - lnf_int_feature!( - atom!("-moz-scrollbar-start-backward"), - ScrollArrowStyle, - get_scrollbar_start_backward - ), - lnf_int_feature!( - atom!("-moz-scrollbar-start-forward"), - ScrollArrowStyle, - get_scrollbar_start_forward - ), - lnf_int_feature!( - atom!("-moz-scrollbar-end-backward"), - ScrollArrowStyle, - get_scrollbar_end_backward - ), - lnf_int_feature!( - atom!("-moz-scrollbar-end-forward"), - ScrollArrowStyle, - get_scrollbar_end_forward - ), - lnf_int_feature!(atom!("-moz-menubar-drag"), MenuBarDrag), - lnf_int_feature!(atom!("-moz-windows-default-theme"), WindowsDefaultTheme), - lnf_int_feature!(atom!("-moz-mac-graphite-theme"), MacGraphiteTheme), - lnf_int_feature!(atom!("-moz-mac-big-sur-theme"), MacBigSurTheme), - lnf_int_feature!(atom!("-moz-mac-rtl"), MacRTL), - lnf_int_feature!( - atom!("-moz-windows-accent-color-in-titlebar"), - WindowsAccentColorInTitlebar - ), - lnf_int_feature!(atom!("-moz-windows-compositor"), DWMCompositor), - lnf_int_feature!(atom!("-moz-windows-classic"), WindowsClassic), - lnf_int_feature!(atom!("-moz-windows-glass"), WindowsGlass), - lnf_int_feature!(atom!("-moz-swipe-animation-enabled"), SwipeAnimationEnabled), - lnf_int_feature!(atom!("-moz-gtk-csd-available"), GTKCSDAvailable), - lnf_int_feature!(atom!("-moz-gtk-csd-minimize-button"), GTKCSDMinimizeButton), - lnf_int_feature!(atom!("-moz-gtk-csd-maximize-button"), GTKCSDMaximizeButton), - lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton), - lnf_int_feature!( - atom!("-moz-gtk-csd-reversed-placement"), - GTKCSDReversedPlacement - ), - lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme), - lnf_int_feature!(atom!("-moz-panel-animations"), PanelAnimations), - // media query for MathML Core's implementation of maction/semantics - bool_pref_feature!( - atom!("-moz-mathml-core-maction-and-semantics"), - "mathml.legacy_maction_and_semantics_implementations.disabled" - ), - // media query for MathML Core's implementation of ms - bool_pref_feature!( - atom!("-moz-mathml-core-ms"), - "mathml.ms_lquote_rquote_attributes.disabled" - ), - // media query for popover attribute - bool_pref_feature!(atom!("-moz-popover-enabled"), "dom.element.popover.enabled"), -]; diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs deleted file mode 100644 index 2ea4229133a..00000000000 --- a/components/style/gecko/media_queries.rs +++ /dev/null @@ -1,560 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko's media-query device and expression representation. - -use crate::color::AbsoluteColor; -use crate::context::QuirksMode; -use crate::custom_properties::CssEnvironment; -use crate::font_metrics::FontMetrics; -use crate::gecko::values::{convert_absolute_color_to_nscolor, convert_nscolor_to_absolute_color}; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs; -use crate::media_queries::MediaType; -use crate::properties::ComputedValues; -use crate::string_cache::Atom; -use crate::values::computed::font::GenericFontFamily; -use crate::values::computed::{ColorScheme, Length, NonNegativeLength}; -use crate::values::specified::color::SystemColor; -use crate::values::specified::font::FONT_MEDIUM_PX; -use crate::values::specified::ViewportVariant; -use crate::values::{CustomIdent, KeyframesName}; -use app_units::{Au, AU_PER_PX}; -use euclid::default::Size2D; -use euclid::{Scale, SideOffsets2D}; -use servo_arc::Arc; -use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; -use std::{cmp, fmt}; -use style_traits::{CSSPixel, DevicePixel}; - -/// The `Device` in Gecko wraps a pres context, has a default values computed, -/// and contains all the viewport rule state. -pub struct Device { - /// NB: The document owns the styleset, who owns the stylist, and thus the - /// `Device`, so having a raw document pointer here is fine. - document: *const structs::Document, - default_values: Arc, - /// The font size of the root element. - /// - /// This is set when computing the style of the root element, and used for - /// rem units in other elements. - /// - /// When computing the style of the root element, there can't be any other - /// style being computed at the same time, given we need the style of the - /// parent to compute everything else. So it is correct to just use a - /// relaxed atomic here. - root_font_size: AtomicU32, - /// The body text color, stored as an `nscolor`, used for the "tables - /// inherit from body" quirk. - /// - /// - body_text_color: AtomicUsize, - /// Whether any styles computed in the document relied on the root font-size - /// by using rem units. - used_root_font_size: AtomicBool, - /// Whether any styles computed in the document relied on font metrics. - used_font_metrics: AtomicBool, - /// Whether any styles computed in the document relied on the viewport size - /// by using vw/vh/vmin/vmax units. - used_viewport_size: AtomicBool, - /// Whether any styles computed in the document relied on the viewport size - /// by using dvw/dvh/dvmin/dvmax units. - used_dynamic_viewport_size: AtomicBool, - /// The CssEnvironment object responsible of getting CSS environment - /// variables. - environment: CssEnvironment, -} - -impl fmt::Debug for Device { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use nsstring::nsCString; - - let mut doc_uri = nsCString::new(); - unsafe { - bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri) - }; - - f.debug_struct("Device") - .field("document_url", &doc_uri) - .finish() - } -} - -unsafe impl Sync for Device {} -unsafe impl Send for Device {} - -impl Device { - /// Trivially constructs a new `Device`. - pub fn new(document: *const structs::Document) -> Self { - assert!(!document.is_null()); - let doc = unsafe { &*document }; - let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) }; - Device { - document, - default_values: ComputedValues::default_values(doc), - root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), - // This gets updated when we see the , so it doesn't really - // matter which color-scheme we look at here. - body_text_color: AtomicUsize::new(prefs.mLightColors.mDefault as usize), - used_root_font_size: AtomicBool::new(false), - used_font_metrics: AtomicBool::new(false), - used_viewport_size: AtomicBool::new(false), - used_dynamic_viewport_size: AtomicBool::new(false), - environment: CssEnvironment, - } - } - - /// Get the relevant environment to resolve `env()` functions. - #[inline] - pub fn environment(&self) -> &CssEnvironment { - &self.environment - } - - /// Returns the computed line-height for the font in a given computed values instance. - /// - /// If you pass down an element, then the used line-height is returned. - pub fn calc_line_height( - &self, - line_height: &crate::values::computed::LineHeight, - vertical: bool, - font: &crate::properties::style_structs::Font, - element: Option, - ) -> NonNegativeLength { - let pres_context = self.pres_context(); - let au = Au(unsafe { - bindings::Gecko_CalcLineHeight( - line_height, - pres_context.map_or(std::ptr::null(), |pc| pc), - vertical, - &**font, - element.map_or(std::ptr::null(), |e| e.0), - ) - }); - NonNegativeLength::new(au.to_f32_px()) - } - - /// Whether any animation name may be referenced from the style of any - /// element. - pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return false, - }; - - unsafe { - bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr()) - } - } - - /// Returns the default computed values as a reference, in order to match - /// Servo. - pub fn default_computed_values(&self) -> &ComputedValues { - &self.default_values - } - - /// Returns the default computed values as an `Arc`. - pub fn default_computed_values_arc(&self) -> &Arc { - &self.default_values - } - - /// Get the font size of the root element (for rem) - pub fn root_font_size(&self) -> Length { - self.used_root_font_size.store(true, Ordering::Relaxed); - Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) - } - - /// Set the font size of the root element (for rem) - pub fn set_root_font_size(&self, size: Length) { - self.root_font_size - .store(size.px().to_bits(), Ordering::Relaxed) - } - - /// The quirks mode of the document. - pub fn quirks_mode(&self) -> QuirksMode { - self.document().mCompatMode.into() - } - - /// Sets the body text color for the "inherit color from body" quirk. - /// - /// - pub fn set_body_text_color(&self, color: AbsoluteColor) { - self.body_text_color.store( - convert_absolute_color_to_nscolor(&color) as usize, - Ordering::Relaxed, - ) - } - - /// Gets the base size given a generic font family and a language. - pub fn base_size_for_generic(&self, language: &Atom, generic: GenericFontFamily) -> Length { - unsafe { bindings::Gecko_GetBaseSize(self.document(), language.as_ptr(), generic) } - } - - /// Gets the size of the scrollbar in CSS pixels. - pub fn scrollbar_inline_size(&self) -> Length { - let pc = match self.pres_context() { - Some(pc) => pc, - // XXX: we could have a more reasonable default perhaps. - None => return Length::new(0.0), - }; - Length::new(unsafe { bindings::Gecko_GetScrollbarInlineSize(pc) }) - } - - /// Queries font metrics - pub fn query_font_metrics( - &self, - vertical: bool, - font: &crate::properties::style_structs::Font, - base_size: Length, - in_media_query: bool, - retrieve_math_scales: bool, - ) -> FontMetrics { - self.used_font_metrics.store(true, Ordering::Relaxed); - let pc = match self.pres_context() { - Some(pc) => pc, - None => return Default::default(), - }; - let gecko_metrics = unsafe { - bindings::Gecko_GetFontMetrics( - pc, - vertical, - &**font, - base_size, - // we don't use the user font set in a media query - !in_media_query, - retrieve_math_scales, - ) - }; - FontMetrics { - x_height: Some(gecko_metrics.mXSize), - zero_advance_measure: if gecko_metrics.mChSize.px() >= 0. { - Some(gecko_metrics.mChSize) - } else { - None - }, - cap_height: if gecko_metrics.mCapHeight.px() >= 0. { - Some(gecko_metrics.mCapHeight) - } else { - None - }, - ic_width: if gecko_metrics.mIcWidth.px() >= 0. { - Some(gecko_metrics.mIcWidth) - } else { - None - }, - ascent: gecko_metrics.mAscent, - script_percent_scale_down: if gecko_metrics.mScriptPercentScaleDown > 0. { - Some(gecko_metrics.mScriptPercentScaleDown) - } else { - None - }, - script_script_percent_scale_down: if gecko_metrics.mScriptScriptPercentScaleDown > 0. { - Some(gecko_metrics.mScriptScriptPercentScaleDown) - } else { - None - }, - } - } - - /// Returns the body text color. - pub fn body_text_color(&self) -> AbsoluteColor { - convert_nscolor_to_absolute_color(self.body_text_color.load(Ordering::Relaxed) as u32) - } - - /// Gets the document pointer. - #[inline] - pub fn document(&self) -> &structs::Document { - unsafe { &*self.document } - } - - /// Gets the pres context associated with this document. - #[inline] - pub fn pres_context(&self) -> Option<&structs::nsPresContext> { - unsafe { - self.document() - .mPresShell - .as_ref()? - .mPresContext - .mRawPtr - .as_ref() - } - } - - /// Gets the preference stylesheet prefs for our document. - #[inline] - pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs { - unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) } - } - - /// Recreates the default computed values. - pub fn reset_computed_values(&mut self) { - self.default_values = ComputedValues::default_values(self.document()); - } - - /// Rebuild all the cached data. - pub fn rebuild_cached_data(&mut self) { - self.reset_computed_values(); - self.used_root_font_size.store(false, Ordering::Relaxed); - self.used_font_metrics.store(false, Ordering::Relaxed); - self.used_viewport_size.store(false, Ordering::Relaxed); - self.used_dynamic_viewport_size - .store(false, Ordering::Relaxed); - } - - /// Returns whether we ever looked up the root font size of the Device. - pub fn used_root_font_size(&self) -> bool { - self.used_root_font_size.load(Ordering::Relaxed) - } - - /// Recreates all the temporary state that the `Device` stores. - /// - /// This includes the viewport override from `@viewport` rules, and also the - /// default computed values. - pub fn reset(&mut self) { - self.reset_computed_values(); - } - - /// Returns whether this document is in print preview. - pub fn is_print_preview(&self) -> bool { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return false, - }; - pc.mType == structs::nsPresContext_nsPresContextType_eContext_PrintPreview - } - - /// Returns the current media type of the device. - pub fn media_type(&self) -> MediaType { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return MediaType::screen(), - }; - - // Gecko allows emulating random media with mMediaEmulationData.mMedium. - let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() { - pc.mMediaEmulationData.mMedium.mRawPtr - } else { - pc.mMedium as *const structs::nsAtom as *mut _ - }; - - MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) })) - } - - // It may make sense to account for @page rule margins here somehow, however - // it's not clear how that'd work, see: - // https://github.com/w3c/csswg-drafts/issues/5437 - fn page_size_minus_default_margin(&self, pc: &structs::nsPresContext) -> Size2D { - debug_assert!(pc.mIsRootPaginatedDocument() != 0); - let area = &pc.mPageSize; - let margin = &pc.mDefaultPageMargin; - let width = area.width - margin.left - margin.right; - let height = area.height - margin.top - margin.bottom; - Size2D::new(Au(cmp::max(width, 0)), Au(cmp::max(height, 0))) - } - - /// Returns the current viewport size in app units. - pub fn au_viewport_size(&self) -> Size2D { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return Size2D::new(Au(0), Au(0)), - }; - - if pc.mIsRootPaginatedDocument() != 0 { - return self.page_size_minus_default_margin(pc); - } - - let area = &pc.mVisibleArea; - Size2D::new(Au(area.width), Au(area.height)) - } - - /// Returns the current viewport size in app units, recording that it's been - /// used for viewport unit resolution. - pub fn au_viewport_size_for_viewport_unit_resolution( - &self, - variant: ViewportVariant, - ) -> Size2D { - self.used_viewport_size.store(true, Ordering::Relaxed); - let pc = match self.pres_context() { - Some(pc) => pc, - None => return Size2D::new(Au(0), Au(0)), - }; - - if pc.mIsRootPaginatedDocument() != 0 { - return self.page_size_minus_default_margin(pc); - } - - match variant { - ViewportVariant::UADefault => { - let size = &pc.mSizeForViewportUnits; - Size2D::new(Au(size.width), Au(size.height)) - }, - ViewportVariant::Small => { - let size = &pc.mVisibleArea; - Size2D::new(Au(size.width), Au(size.height)) - }, - ViewportVariant::Large => { - let size = &pc.mVisibleArea; - // Looks like IntCoordTyped is treated as if it's u32 in Rust. - debug_assert!( - /* pc.mDynamicToolbarMaxHeight >=0 && */ - pc.mDynamicToolbarMaxHeight < i32::MAX as u32 - ); - Size2D::new( - Au(size.width), - Au(size.height + - pc.mDynamicToolbarMaxHeight as i32 * pc.mCurAppUnitsPerDevPixel), - ) - }, - ViewportVariant::Dynamic => { - self.used_dynamic_viewport_size - .store(true, Ordering::Relaxed); - let size = &pc.mVisibleArea; - // Looks like IntCoordTyped is treated as if it's u32 in Rust. - debug_assert!( - /* pc.mDynamicToolbarHeight >=0 && */ - pc.mDynamicToolbarHeight < i32::MAX as u32 - ); - Size2D::new( - Au(size.width), - Au(size.height + - (pc.mDynamicToolbarMaxHeight - pc.mDynamicToolbarHeight) as i32 * - pc.mCurAppUnitsPerDevPixel), - ) - }, - } - } - - /// Returns whether we ever looked up the viewport size of the Device. - pub fn used_viewport_size(&self) -> bool { - self.used_viewport_size.load(Ordering::Relaxed) - } - - /// Returns whether we ever looked up the dynamic viewport size of the Device. - pub fn used_dynamic_viewport_size(&self) -> bool { - self.used_dynamic_viewport_size.load(Ordering::Relaxed) - } - - /// Returns whether font metrics have been queried. - pub fn used_font_metrics(&self) -> bool { - self.used_font_metrics.load(Ordering::Relaxed) - } - - /// Returns whether visited styles are enabled. - pub fn visited_styles_enabled(&self) -> bool { - unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) } - } - - /// Returns the number of app units per device pixel we're using currently. - pub fn app_units_per_device_pixel(&self) -> i32 { - match self.pres_context() { - Some(pc) => pc.mCurAppUnitsPerDevPixel, - None => AU_PER_PX, - } - } - - /// Returns the device pixel ratio. - pub fn device_pixel_ratio(&self) -> Scale { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return Scale::new(1.), - }; - - if pc.mMediaEmulationData.mDPPX > 0.0 { - return Scale::new(pc.mMediaEmulationData.mDPPX); - } - - let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32; - let au_per_px = AU_PER_PX as f32; - Scale::new(au_per_px / au_per_dpx) - } - - /// Returns whether document colors are enabled. - #[inline] - pub fn use_document_colors(&self) -> bool { - let doc = self.document(); - if doc.mIsBeingUsedAsImage() { - return true; - } - self.pref_sheet_prefs().mUseDocumentColors - } - - /// Computes a system color and returns it as an nscolor. - pub(crate) fn system_nscolor( - &self, - system_color: SystemColor, - color_scheme: &ColorScheme, - ) -> u32 { - unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), color_scheme) } - } - - /// Returns the default background color. - /// - /// This is only for forced-colors/high-contrast, so looking at light colors - /// is ok. - pub fn default_background_color(&self) -> AbsoluteColor { - let normal = ColorScheme::normal(); - convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvas, &normal)) - } - - /// Returns the default foreground color. - /// - /// See above for looking at light colors only. - pub fn default_color(&self) -> AbsoluteColor { - let normal = ColorScheme::normal(); - convert_nscolor_to_absolute_color(self.system_nscolor(SystemColor::Canvastext, &normal)) - } - - /// Returns the current effective text zoom. - #[inline] - fn text_zoom(&self) -> f32 { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return 1., - }; - pc.mTextZoom - } - - /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText). - #[inline] - pub fn zoom_text(&self, size: Length) -> Length { - size.scale_by(self.text_zoom()) - } - - /// Un-apply text zoom. - #[inline] - pub fn unzoom_text(&self, size: Length) -> Length { - size.scale_by(1. / self.text_zoom()) - } - - /// Returns safe area insets - pub fn safe_area_insets(&self) -> SideOffsets2D { - let pc = match self.pres_context() { - Some(pc) => pc, - None => return SideOffsets2D::zero(), - }; - let mut top = 0.0; - let mut right = 0.0; - let mut bottom = 0.0; - let mut left = 0.0; - unsafe { - bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left) - }; - SideOffsets2D::new(top, right, bottom, left) - } - - /// Returns true if the given MIME type is supported - pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { - unsafe { - bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32) - } - } - - /// Return whether the document is a chrome document. - /// - /// This check is consistent with how we enable chrome rules for chrome:// and resource:// - /// stylesheets (and thus chrome:// documents). - #[inline] - pub fn chrome_rules_enabled_for_document(&self) -> bool { - self.document().mChromeRulesEnabled() - } -} diff --git a/components/style/gecko/mod.rs b/components/style/gecko/mod.rs deleted file mode 100644 index c32ded14f33..00000000000 --- a/components/style/gecko/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko-specific style-system bits. - -#[macro_use] -mod non_ts_pseudo_class_list; - -pub mod arc_types; -pub mod conversions; -pub mod data; -pub mod media_features; -pub mod media_queries; -pub mod pseudo_element; -pub mod restyle_damage; -pub mod selector_parser; -pub mod snapshot; -pub mod snapshot_helpers; -pub mod traversal; -pub mod url; -pub mod values; -pub mod wrapper; diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs deleted file mode 100644 index e494082047f..00000000000 --- a/components/style/gecko/non_ts_pseudo_class_list.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -/* - * This file contains a helper macro includes all supported non-tree-structural - * pseudo-classes. - * - * FIXME: Find a way to autogenerate this file. - * - * Expected usage is as follows: - * ``` - * macro_rules! pseudo_class_macro{ - * ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { - * // do stuff - * } - * } - * apply_non_ts_list!(pseudo_class_macro) - * ``` - * - * $gecko_type can be either "_" or an ident in Gecko's CSSPseudoClassType. - * $state can be either "_" or an expression of type ElementState. If present, - * the semantics are that the pseudo-class matches if any of the bits in - * $state are set on the element. - * $flags can be either "_" or an expression of type NonTSPseudoClassFlag, - * see selector_parser.rs for more details. - */ - -macro_rules! apply_non_ts_list { - ($apply_macro:ident) => { - $apply_macro! { - [ - ("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-select-list-box", MozSelectListBox, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("link", Link, UNVISITED, _), - ("any-link", AnyLink, VISITED_OR_UNVISITED, _), - ("visited", Visited, VISITED, _), - ("active", Active, ACTIVE, _), - ("autofill", Autofill, AUTOFILL, _), - ("checked", Checked, CHECKED, _), - ("defined", Defined, DEFINED, _), - ("disabled", Disabled, DISABLED, _), - ("enabled", Enabled, ENABLED, _), - ("focus", Focus, FOCUS, _), - ("focus-within", FocusWithin, FOCUS_WITHIN, _), - ("focus-visible", FocusVisible, FOCUSRING, _), - ("hover", Hover, HOVER, _), - ("-moz-drag-over", MozDragOver, DRAGOVER, _), - ("target", Target, URLTARGET, _), - ("indeterminate", Indeterminate, INDETERMINATE, _), - ("-moz-inert", MozInert, INERT, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-devtools-highlighted", MozDevtoolsHighlighted, DEVTOOLS_HIGHLIGHTED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, STYLEEDITOR_TRANSITIONING, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("fullscreen", Fullscreen, FULLSCREEN, _), - ("modal", Modal, MODAL, _), - ("-moz-topmost-modal", MozTopmostModal, TOPMOST_MODAL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-broken", MozBroken, BROKEN, _), - ("-moz-loading", MozLoading, LOADING, _), - ("-moz-has-dir-attr", MozHasDirAttr, HAS_DIR_ATTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-ltr", MozDirAttrLTR, HAS_DIR_ATTR_LTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-rtl", MozDirAttrRTL, HAS_DIR_ATTR_RTL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, HAS_DIR_ATTR_LIKE_AUTO, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - - ("-moz-autofill-preview", MozAutofillPreview, AUTOFILL_PREVIEW, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-value-empty", MozValueEmpty, VALUE_EMPTY, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-revealed", MozRevealed, REVEALED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - - ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, INCREMENT_SCRIPT_LEVEL, _), - - ("required", Required, REQUIRED, _), - ("popover-open", PopoverOpen, POPOVER_OPEN, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("optional", Optional, OPTIONAL_, _), - ("valid", Valid, VALID, _), - ("invalid", Invalid, INVALID, _), - ("in-range", InRange, INRANGE, _), - ("out-of-range", OutOfRange, OUTOFRANGE, _), - ("default", Default, DEFAULT, _), - ("placeholder-shown", PlaceholderShown, PLACEHOLDER_SHOWN, _), - ("read-only", ReadOnly, READONLY, _), - ("read-write", ReadWrite, READWRITE, _), - ("user-valid", UserValid, USER_VALID, _), - ("user-invalid", UserInvalid, USER_INVALID, _), - ("-moz-meter-optimum", MozMeterOptimum, OPTIMUM, _), - ("-moz-meter-sub-optimum", MozMeterSubOptimum, SUB_OPTIMUM, _), - ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, SUB_SUB_OPTIMUM, _), - - ("-moz-first-node", MozFirstNode, _, _), - ("-moz-last-node", MozLastNode, _, _), - ("-moz-only-whitespace", MozOnlyWhitespace, _, _), - ("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), - ("-moz-is-html", MozIsHTML, _, _), - ("-moz-placeholder", MozPlaceholder, _, _), - ("-moz-lwtheme", MozLWTheme, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - ("-moz-window-inactive", MozWindowInactive, _, _), - ] - } - } -} diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs deleted file mode 100644 index d0c47d51a41..00000000000 --- a/components/style/gecko/pseudo_element.rs +++ /dev/null @@ -1,237 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko's definition of a pseudo-element. -//! -//! Note that a few autogenerated bits of this live in -//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably -//! need to update the checked-in files for Servo. - -use crate::gecko_bindings::structs::{self, PseudoStyleType}; -use crate::properties::longhands::display::computed_value::T as Display; -use crate::properties::{ComputedValues, PropertyFlags}; -use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl}; -use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; -use crate::string_cache::Atom; -use crate::values::serialize_atom_identifier; -use crate::values::AtomIdent; -use cssparser::ToCss; -use static_prefs::pref; -use std::fmt; - -include!(concat!( - env!("OUT_DIR"), - "/gecko/pseudo_element_definition.rs" -)); - -impl ::selectors::parser::PseudoElement for PseudoElement { - type Impl = SelectorImpl; - - // ::slotted() should support all tree-abiding pseudo-elements, see - // https://drafts.csswg.org/css-scoping/#slotted-pseudo - // https://drafts.csswg.org/css-pseudo-4/#treelike - #[inline] - fn valid_after_slotted(&self) -> bool { - matches!( - *self, - PseudoElement::Before | - PseudoElement::After | - PseudoElement::Marker | - PseudoElement::Placeholder | - PseudoElement::FileSelectorButton - ) - } - - #[inline] - fn accepts_state_pseudo_classes(&self) -> bool { - self.supports_user_action_state() - } -} - -impl PseudoElement { - /// Returns the kind of cascade type that a given pseudo is going to use. - /// - /// In Gecko we only compute ::before and ::after eagerly. We save the rules - /// for anonymous boxes separately, so we resolve them as precomputed - /// pseudos. - /// - /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`. - pub fn cascade_type(&self) -> PseudoElementCascadeType { - if self.is_eager() { - debug_assert!(!self.is_anon_box()); - return PseudoElementCascadeType::Eager; - } - - if self.is_precomputed() { - return PseudoElementCascadeType::Precomputed; - } - - PseudoElementCascadeType::Lazy - } - - /// Whether the pseudo-element should inherit from the default computed - /// values instead of from the parent element. - /// - /// This is not the common thing, but there are some pseudos (namely: - /// ::backdrop), that shouldn't inherit from the parent element. - pub fn inherits_from_default_values(&self) -> bool { - matches!(*self, PseudoElement::Backdrop) - } - - /// Gets the canonical index of this eagerly-cascaded pseudo-element. - #[inline] - pub fn eager_index(&self) -> usize { - EAGER_PSEUDOS - .iter() - .position(|p| p == self) - .expect("Not an eager pseudo") - } - - /// Creates a pseudo-element from an eager index. - #[inline] - pub fn from_eager_index(i: usize) -> Self { - EAGER_PSEUDOS[i].clone() - } - - /// Whether animations for the current pseudo element are stored in the - /// parent element. - #[inline] - pub fn animations_stored_in_parent(&self) -> bool { - matches!(*self, Self::Before | Self::After | Self::Marker) - } - - /// Whether the current pseudo element is ::before or ::after. - #[inline] - pub fn is_before_or_after(&self) -> bool { - self.is_before() || self.is_after() - } - - /// Whether this pseudo-element is the ::before pseudo. - #[inline] - pub fn is_before(&self) -> bool { - *self == PseudoElement::Before - } - - /// Whether this pseudo-element is the ::after pseudo. - #[inline] - pub fn is_after(&self) -> bool { - *self == PseudoElement::After - } - - /// Whether this pseudo-element is the ::marker pseudo. - #[inline] - pub fn is_marker(&self) -> bool { - *self == PseudoElement::Marker - } - - /// Whether this pseudo-element is the ::selection pseudo. - #[inline] - pub fn is_selection(&self) -> bool { - *self == PseudoElement::Selection - } - - /// Whether this pseudo-element is ::first-letter. - #[inline] - pub fn is_first_letter(&self) -> bool { - *self == PseudoElement::FirstLetter - } - - /// Whether this pseudo-element is ::first-line. - #[inline] - pub fn is_first_line(&self) -> bool { - *self == PseudoElement::FirstLine - } - - /// Whether this pseudo-element is the ::-moz-color-swatch pseudo. - #[inline] - pub fn is_color_swatch(&self) -> bool { - *self == PseudoElement::MozColorSwatch - } - - /// Whether this pseudo-element is lazily-cascaded. - #[inline] - pub fn is_lazy(&self) -> bool { - !self.is_eager() && !self.is_precomputed() - } - - /// The identifier of the highlight this pseudo-element represents. - pub fn highlight_name(&self) -> Option<&AtomIdent> { - match &*self { - PseudoElement::Highlight(name) => Some(&name), - _ => None, - } - } - - /// Whether this pseudo-element is the ::highlight pseudo. - pub fn is_highlight(&self) -> bool { - matches!(*self, PseudoElement::Highlight(_)) - } - - /// Whether this pseudo-element supports user action selectors. - pub fn supports_user_action_state(&self) -> bool { - (self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0 - } - - /// Whether this pseudo-element is enabled for all content. - pub fn enabled_in_content(&self) -> bool { - if self.is_highlight() && !pref!("dom.customHighlightAPI.enabled") { - return false; - } - return self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0; - } - - /// Whether this pseudo is enabled explicitly in UA sheets. - pub fn enabled_in_ua_sheets(&self) -> bool { - (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS) != 0 - } - - /// Whether this pseudo is enabled explicitly in chrome sheets. - pub fn enabled_in_chrome(&self) -> bool { - (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_CHROME) != 0 - } - - /// Whether this pseudo-element skips flex/grid container display-based - /// fixup. - #[inline] - pub fn skip_item_display_fixup(&self) -> bool { - (self.flags() & structs::CSS_PSEUDO_ELEMENT_IS_FLEX_OR_GRID_ITEM) == 0 - } - - /// Whether this pseudo-element is precomputed. - #[inline] - pub fn is_precomputed(&self) -> bool { - self.is_anon_box() && !self.is_tree_pseudo_element() - } - - /// Property flag that properties must have to apply to this pseudo-element. - #[inline] - pub fn property_restriction(&self) -> Option { - Some(match *self { - PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER, - PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE, - PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER, - PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE, - PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => { - PropertyFlags::APPLIES_TO_MARKER - }, - _ => return None, - }) - } - - /// Whether this pseudo-element should actually exist if it has - /// the given styles. - pub fn should_exist(&self, style: &ComputedValues) -> bool { - debug_assert!(self.is_eager()); - - if style.get_box().clone_display() == Display::None { - return false; - } - - if self.is_before_or_after() && style.ineffective_content_property() { - return false; - } - - true - } -} diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs deleted file mode 100644 index 73e7893c998..00000000000 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -/// Gecko's pseudo-element definition. -/// -/// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the -/// size of PseudoElement (and thus selector components) small. -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] -pub enum PseudoElement { - % for pseudo in PSEUDOS: - /// ${pseudo.value} - % if pseudo.is_tree_pseudo_element(): - ${pseudo.capitalized_pseudo()}(thin_vec::ThinVec), - % elif pseudo.pseudo_ident == "highlight": - ${pseudo.capitalized_pseudo()}(AtomIdent), - % else: - ${pseudo.capitalized_pseudo()}, - % endif - % endfor - /// ::-webkit-* that we don't recognize - /// https://github.com/whatwg/compat/issues/103 - UnknownWebkit(Atom), -} - -/// Important: If you change this, you should also update Gecko's -/// nsCSSPseudoElements::IsEagerlyCascadedInServo. -<% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %> -<% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %> -<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_simple_pseudo_element()] %> - -/// The number of eager pseudo-elements. -pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)}; - -/// The number of non-functional pseudo-elements. -pub const SIMPLE_PSEUDO_COUNT: usize = ${len(SIMPLE_PSEUDOS)}; - -/// The number of tree pseudo-elements. -pub const TREE_PSEUDO_COUNT: usize = ${len(TREE_PSEUDOS)}; - -/// The number of all pseudo-elements. -pub const PSEUDO_COUNT: usize = ${len(PSEUDOS)}; - -/// The list of eager pseudos. -pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ - % for eager_pseudo_name in EAGER_PSEUDOS: - PseudoElement::${eager_pseudo_name}, - % endfor -]; - -<%def name="pseudo_element_variant(pseudo, tree_arg='..')">\ -PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if not pseudo.is_simple_pseudo_element() else ""}\ - - -impl PseudoElement { - /// Returns an index of the pseudo-element. - #[inline] - pub fn index(&self) -> usize { - match *self { - % for i, pseudo in enumerate(PSEUDOS): - ${pseudo_element_variant(pseudo)} => ${i}, - % endfor - PseudoElement::UnknownWebkit(..) => unreachable!(), - } - } - - /// Returns an array of `None` values. - /// - /// FIXME(emilio): Integer generics can't come soon enough. - pub fn pseudo_none_array() -> [Option; PSEUDO_COUNT] { - [ - ${",\n ".join(["None" for pseudo in PSEUDOS])} - ] - } - - /// Whether this pseudo-element is an anonymous box. - #[inline] - pub fn is_anon_box(&self) -> bool { - match *self { - % for pseudo in PSEUDOS: - % if pseudo.is_anon_box(): - ${pseudo_element_variant(pseudo)} => true, - % endif - % endfor - _ => false, - } - } - - /// Whether this pseudo-element is eagerly-cascaded. - #[inline] - pub fn is_eager(&self) -> bool { - matches!(*self, - ${" | ".join(map(lambda name: "PseudoElement::{}".format(name), EAGER_PSEUDOS))}) - } - - /// Whether this pseudo-element is tree pseudo-element. - #[inline] - pub fn is_tree_pseudo_element(&self) -> bool { - match *self { - % for pseudo in TREE_PSEUDOS: - ${pseudo_element_variant(pseudo)} => true, - % endfor - _ => false, - } - } - - /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element. - #[inline] - pub fn is_unknown_webkit_pseudo_element(&self) -> bool { - matches!(*self, PseudoElement::UnknownWebkit(..)) - } - - /// Gets the flags associated to this pseudo-element, or 0 if it's an - /// anonymous box. - pub fn flags(&self) -> u32 { - match *self { - % for pseudo in PSEUDOS: - ${pseudo_element_variant(pseudo)} => - % if pseudo.is_tree_pseudo_element(): - structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME, - % elif pseudo.is_anon_box(): - structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS, - % else: - structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident}, - % endif - % endfor - PseudoElement::UnknownWebkit(..) => 0, - } - } - - /// Construct a pseudo-element from a `PseudoStyleType`. - #[inline] - pub fn from_pseudo_type(type_: PseudoStyleType) -> Option { - match type_ { - % for pseudo in PSEUDOS: - % if pseudo.is_simple_pseudo_element(): - PseudoStyleType::${pseudo.pseudo_ident} => { - Some(${pseudo_element_variant(pseudo)}) - }, - % endif - % endfor - _ => None, - } - } - - /// Construct a `PseudoStyleType` from a pseudo-element - #[inline] - pub fn pseudo_type(&self) -> PseudoStyleType { - match *self { - % for pseudo in PSEUDOS: - % if pseudo.is_tree_pseudo_element(): - PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::XULTree, - % elif pseudo.pseudo_ident == "highlight": - PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::${pseudo.pseudo_ident}, - % else: - PseudoElement::${pseudo.capitalized_pseudo()} => PseudoStyleType::${pseudo.pseudo_ident}, - % endif - % endfor - PseudoElement::UnknownWebkit(..) => unreachable!(), - } - } - - /// Get the argument list of a tree pseudo-element. - #[inline] - pub fn tree_pseudo_args(&self) -> Option<<&[Atom]> { - match *self { - % for pseudo in TREE_PSEUDOS: - PseudoElement::${pseudo.capitalized_pseudo()}(ref args) => Some(args), - % endfor - _ => None, - } - } - - /// Construct a tree pseudo-element from atom and args. - #[inline] - pub fn from_tree_pseudo_atom(atom: &Atom, args: Box<[Atom]>) -> Option { - % for pseudo in PSEUDOS: - % if pseudo.is_tree_pseudo_element(): - if atom == &atom!("${pseudo.value}") { - return Some(PseudoElement::${pseudo.capitalized_pseudo()}(args.into())); - } - % endif - % endfor - None - } - - /// Constructs a pseudo-element from a string of text. - /// - /// Returns `None` if the pseudo-element is not recognised. - #[inline] - pub fn from_slice(name: &str, allow_unkown_webkit: bool) -> Option { - // We don't need to support tree pseudos because functional - // pseudo-elements needs arguments, and thus should be created - // via other methods. - match_ignore_ascii_case! { name, - % for pseudo in SIMPLE_PSEUDOS: - "${pseudo.value[1:]}" => { - return Some(${pseudo_element_variant(pseudo)}) - }, - % endfor - // Alias some legacy prefixed pseudos to their standardized name at parse time: - "-moz-selection" => { - return Some(PseudoElement::Selection); - }, - "-moz-placeholder" => { - return Some(PseudoElement::Placeholder); - }, - "-moz-list-bullet" | "-moz-list-number" => { - return Some(PseudoElement::Marker); - }, - _ => { - if starts_with_ignore_ascii_case(name, "-moz-tree-") { - return PseudoElement::tree_pseudo_element(name, Default::default()) - } - const WEBKIT_PREFIX: &str = "-webkit-"; - if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) { - let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]); - return Some(PseudoElement::UnknownWebkit(part.into())); - } - } - } - - None - } - - /// Constructs a tree pseudo-element from the given name and arguments. - /// "name" must start with "-moz-tree-". - /// - /// Returns `None` if the pseudo-element is not recognized. - #[inline] - pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec) -> Option { - debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-")); - let tree_part = &name[10..]; - % for pseudo in TREE_PSEUDOS: - if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") { - return Some(${pseudo_element_variant(pseudo, "args")}); - } - % endfor - None - } -} - -impl ToCss for PseudoElement { - fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { - dest.write_char(':')?; - match *self { - % for pseudo in (p for p in PSEUDOS if p.pseudo_ident != "highlight"): - ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?, - % endfor - PseudoElement::Highlight(ref name) => { - dest.write_str(":highlight(")?; - serialize_atom_identifier(name, dest)?; - dest.write_char(')')?; - } - PseudoElement::UnknownWebkit(ref atom) => { - dest.write_str(":-webkit-")?; - serialize_atom_identifier(atom, dest)?; - } - } - if let Some(args) = self.tree_pseudo_args() { - if !args.is_empty() { - dest.write_char('(')?; - let mut iter = args.iter(); - if let Some(first) = iter.next() { - serialize_atom_identifier(&first, dest)?; - for item in iter { - dest.write_str(", ")?; - serialize_atom_identifier(item, dest)?; - } - } - dest.write_char(')')?; - } - } - Ok(()) - } -} diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py deleted file mode 100755 index 61f2fc4c635..00000000000 --- a/components/style/gecko/regen_atoms.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/env python - -# 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/. - -import re -import os -import sys - -from io import BytesIO - -GECKO_DIR = os.path.dirname(__file__.replace("\\", "/")) -sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties")) - -import build - - -# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`. -PATTERN = re.compile( - '^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)', - re.MULTILINE, -) -FILE = "include/nsGkAtomList.h" - - -def map_atom(ident): - if ident in { - "box", - "loop", - "match", - "mod", - "ref", - "self", - "type", - "use", - "where", - "in", - }: - return ident + "_" - return ident - - -class Atom: - def __init__(self, ident, value, hash, ty, atom_type): - self.ident = "nsGkAtoms_{}".format(ident) - self.original_ident = ident - self.value = value - self.hash = hash - # The Gecko type: "nsStaticAtom", "nsCSSPseudoElementStaticAtom", or - # "nsAnonBoxPseudoStaticAtom". - self.ty = ty - # The type of atom: "Atom", "PseudoElement", "NonInheritingAnonBox", - # or "InheritingAnonBox". - self.atom_type = atom_type - - if ( - self.is_pseudo_element() - or self.is_anon_box() - or self.is_tree_pseudo_element() - ): - self.pseudo_ident = (ident.split("_", 1))[1] - - if self.is_anon_box(): - assert self.is_inheriting_anon_box() or self.is_non_inheriting_anon_box() - - def type(self): - return self.ty - - def capitalized_pseudo(self): - return self.pseudo_ident[0].upper() + self.pseudo_ident[1:] - - def is_pseudo_element(self): - return self.atom_type == "PseudoElementAtom" - - def is_anon_box(self): - if self.is_tree_pseudo_element(): - return False - return self.is_non_inheriting_anon_box() or self.is_inheriting_anon_box() - - def is_non_inheriting_anon_box(self): - assert not self.is_tree_pseudo_element() - return self.atom_type == "NonInheritingAnonBoxAtom" - - def is_inheriting_anon_box(self): - if self.is_tree_pseudo_element(): - return False - return self.atom_type == "InheritingAnonBoxAtom" - - def is_tree_pseudo_element(self): - return self.value.startswith(":-moz-tree-") - - def is_simple_pseudo_element(self) -> bool: - return not (self.is_tree_pseudo_element() or self.pseudo_ident == "highlight") - - -def collect_atoms(objdir): - atoms = [] - path = os.path.abspath(os.path.join(objdir, FILE)) - print("cargo:rerun-if-changed={}".format(path)) - with open(path) as f: - content = f.read() - for result in PATTERN.finditer(content): - atoms.append( - Atom( - result.group(1), - result.group(2), - result.group(3), - result.group(4), - result.group(5), - ) - ) - return atoms - - -class FileAvoidWrite(BytesIO): - """File-like object that buffers output and only writes if content changed.""" - - def __init__(self, filename): - BytesIO.__init__(self) - self.name = filename - - def write(self, buf): - if isinstance(buf, str): - buf = buf.encode("utf-8") - BytesIO.write(self, buf) - - def close(self): - buf = self.getvalue() - BytesIO.close(self) - try: - with open(self.name, "rb") as f: - old_content = f.read() - if old_content == buf: - print("{} is not changed, skip".format(self.name)) - return - except IOError: - pass - with open(self.name, "wb") as f: - f.write(buf) - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - if not self.closed: - self.close() - - -PRELUDE = """ -/* 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/. */ - -// Autogenerated file created by components/style/gecko/regen_atoms.py. -// DO NOT EDIT DIRECTLY -"""[ - 1: -] - -RULE_TEMPLATE = """ - ("{atom}") => {{{{ - #[allow(unsafe_code)] #[allow(unused_unsafe)] - unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }} - }}}}; -"""[ - 1: -] - -MACRO_TEMPLATE = """ -/// Returns a static atom by passing the literal string it represents. -#[macro_export] -macro_rules! atom {{ -{body}\ -}} -""" - - -def write_atom_macro(atoms, file_name): - with FileAvoidWrite(file_name) as f: - f.write(PRELUDE) - macro_rules = [ - RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i) - for (i, atom) in enumerate(atoms) - ] - f.write(MACRO_TEMPLATE.format(body="".join(macro_rules))) - - -def write_pseudo_elements(atoms, target_filename): - pseudos = [] - for atom in atoms: - if ( - atom.type() == "nsCSSPseudoElementStaticAtom" - or atom.type() == "nsCSSAnonBoxPseudoStaticAtom" - ): - pseudos.append(atom) - - pseudo_definition_template = os.path.join( - GECKO_DIR, "pseudo_element_definition.mako.rs" - ) - print("cargo:rerun-if-changed={}".format(pseudo_definition_template)) - contents = build.render(pseudo_definition_template, PSEUDOS=pseudos) - - with FileAvoidWrite(target_filename) as f: - f.write(contents) - - -def generate_atoms(dist, out): - atoms = collect_atoms(dist) - write_atom_macro(atoms, os.path.join(out, "atom_macro.rs")) - write_pseudo_elements(atoms, os.path.join(out, "pseudo_element_definition.rs")) - - -if __name__ == "__main__": - if len(sys.argv) != 3: - print("Usage: {} dist out".format(sys.argv[0])) - exit(2) - generate_atoms(sys.argv[1], sys.argv[2]) diff --git a/components/style/gecko/restyle_damage.rs b/components/style/gecko/restyle_damage.rs deleted file mode 100644 index 4749daea183..00000000000 --- a/components/style/gecko/restyle_damage.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko's restyle damage computation (aka change hints, aka `nsChangeHint`). - -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs; -use crate::gecko_bindings::structs::nsChangeHint; -use crate::matching::{StyleChange, StyleDifference}; -use crate::properties::ComputedValues; -use std::ops::{BitAnd, BitOr, BitOrAssign, Not}; - -/// The representation of Gecko's restyle damage is just a wrapper over -/// `nsChangeHint`. -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct GeckoRestyleDamage(nsChangeHint); - -impl GeckoRestyleDamage { - /// Trivially construct a new `GeckoRestyleDamage`. - #[inline] - pub fn new(raw: nsChangeHint) -> Self { - GeckoRestyleDamage(raw) - } - - /// Get the inner change hint for this damage. - #[inline] - pub fn as_change_hint(&self) -> nsChangeHint { - self.0 - } - - /// Get an empty change hint, that is (`nsChangeHint(0)`). - #[inline] - pub fn empty() -> Self { - GeckoRestyleDamage(nsChangeHint(0)) - } - - /// Returns whether this restyle damage represents the empty damage. - #[inline] - pub fn is_empty(&self) -> bool { - self.0 == nsChangeHint(0) - } - - /// Computes the `StyleDifference` (including the appropriate change hint) - /// given an old and a new style. - pub fn compute_style_difference( - old_style: &ComputedValues, - new_style: &ComputedValues, - ) -> StyleDifference { - let mut any_style_changed = false; - let mut reset_only = false; - let hint = unsafe { - bindings::Gecko_CalcStyleDifference( - old_style.as_gecko_computed_style(), - new_style.as_gecko_computed_style(), - &mut any_style_changed, - &mut reset_only, - ) - }; - if reset_only && !old_style.custom_properties_equal(new_style) { - // The Gecko_CalcStyleDifference call only checks the non-custom - // property structs, so we check the custom properties here. Since - // they generate no damage themselves, we can skip this check if we - // already know we had some inherited (regular) property - // differences. - any_style_changed = true; - reset_only = false; - } - let change = if any_style_changed { - StyleChange::Changed { reset_only } - } else { - StyleChange::Unchanged - }; - let damage = GeckoRestyleDamage(nsChangeHint(hint)); - StyleDifference { damage, change } - } - - /// Returns true if this restyle damage contains all the damage of |other|. - pub fn contains(self, other: Self) -> bool { - self & other == other - } - - /// Gets restyle damage to reconstruct the entire frame, subsuming all - /// other damage. - pub fn reconstruct() -> Self { - GeckoRestyleDamage(structs::nsChangeHint::nsChangeHint_ReconstructFrame) - } -} - -impl Default for GeckoRestyleDamage { - fn default() -> Self { - Self::empty() - } -} - -impl BitOr for GeckoRestyleDamage { - type Output = Self; - fn bitor(self, other: Self) -> Self { - GeckoRestyleDamage(self.0 | other.0) - } -} - -impl BitOrAssign for GeckoRestyleDamage { - fn bitor_assign(&mut self, other: Self) { - *self = *self | other; - } -} - -impl BitAnd for GeckoRestyleDamage { - type Output = Self; - fn bitand(self, other: Self) -> Self { - GeckoRestyleDamage(nsChangeHint((self.0).0 & (other.0).0)) - } -} - -impl Not for GeckoRestyleDamage { - type Output = Self; - fn not(self) -> Self { - GeckoRestyleDamage(nsChangeHint(!(self.0).0)) - } -} diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs deleted file mode 100644 index 52549f9ecbe..00000000000 --- a/components/style/gecko/selector_parser.rs +++ /dev/null @@ -1,497 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko-specific bits for selector-parsing. - -use crate::computed_value_flags::ComputedValueFlags; -use crate::invalidation::element::document_state::InvalidationMatchingData; -use crate::properties::ComputedValues; -use crate::selector_parser::{Direction, HorizontalDirection, SelectorParser}; -use crate::str::starts_with_ignore_ascii_case; -use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; -use crate::values::{AtomIdent, AtomString}; -use cssparser::{BasicParseError, BasicParseErrorKind, Parser}; -use cssparser::{CowRcStr, SourceLocation, ToCss, Token}; -use dom::{DocumentState, ElementState}; -use selectors::parser::SelectorParseErrorKind; -use std::fmt; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; -use thin_vec::ThinVec; - -pub use crate::gecko::pseudo_element::{ - PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT, -}; -pub use crate::gecko::snapshot::SnapshotMap; - -bitflags! { - // See NonTSPseudoClass::is_enabled_in() - struct NonTSPseudoClassFlag: u8 { - const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0; - const PSEUDO_CLASS_ENABLED_IN_CHROME = 1 << 1; - const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME = - NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS.bits | - NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME.bits; - } -} - -/// The type used to store the language argument to the `:lang` pseudo-class. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)] -#[css(comma)] -pub struct Lang(#[css(iterable)] pub ThinVec); - -macro_rules! pseudo_class_name { - ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { - /// Our representation of a non tree-structural pseudo-class. - #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] - pub enum NonTSPseudoClass { - $( - #[doc = $css] - $name, - )* - /// The `:lang` pseudo-class. - Lang(Lang), - /// The `:dir` pseudo-class. - Dir(Direction), - /// The non-standard `:-moz-locale-dir` pseudo-class. - MozLocaleDir(Direction), - } - } -} -apply_non_ts_list!(pseudo_class_name); - -impl ToCss for NonTSPseudoClass { - fn to_css(&self, dest: &mut W) -> fmt::Result - where - W: fmt::Write, - { - macro_rules! pseudo_class_serialize { - ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { - match *self { - $(NonTSPseudoClass::$name => concat!(":", $css),)* - NonTSPseudoClass::Lang(ref lang) => { - dest.write_str(":lang(")?; - lang.to_css(&mut CssWriter::new(dest))?; - return dest.write_char(')'); - }, - NonTSPseudoClass::MozLocaleDir(ref dir) => { - dest.write_str(":-moz-locale-dir(")?; - dir.to_css(&mut CssWriter::new(dest))?; - return dest.write_char(')') - }, - NonTSPseudoClass::Dir(ref dir) => { - dest.write_str(":dir(")?; - dir.to_css(&mut CssWriter::new(dest))?; - return dest.write_char(')') - }, - } - } - } - let ser = apply_non_ts_list!(pseudo_class_serialize); - dest.write_str(ser) - } -} - -impl NonTSPseudoClass { - /// Parses the name and returns a non-ts-pseudo-class if succeeds. - /// None otherwise. It doesn't check whether the pseudo-class is enabled - /// in a particular state. - pub fn parse_non_functional(name: &str) -> Option { - macro_rules! pseudo_class_parse { - ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { - match_ignore_ascii_case! { &name, - $($css => Some(NonTSPseudoClass::$name),)* - "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen), - "-moz-read-only" => Some(NonTSPseudoClass::ReadOnly), - "-moz-read-write" => Some(NonTSPseudoClass::ReadWrite), - "-moz-focusring" => Some(NonTSPseudoClass::FocusVisible), - "-moz-ui-valid" => Some(NonTSPseudoClass::UserValid), - "-moz-ui-invalid" => Some(NonTSPseudoClass::UserInvalid), - "-webkit-autofill" => Some(NonTSPseudoClass::Autofill), - _ => None, - } - } - } - apply_non_ts_list!(pseudo_class_parse) - } - - /// Returns true if this pseudo-class has any of the given flags set. - fn has_any_flag(&self, flags: NonTSPseudoClassFlag) -> bool { - macro_rules! check_flag { - (_) => { - false - }; - ($flags:ident) => { - NonTSPseudoClassFlag::$flags.intersects(flags) - }; - } - macro_rules! pseudo_class_check_is_enabled_in { - ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { - match *self { - $(NonTSPseudoClass::$name => check_flag!($flags),)* - NonTSPseudoClass::MozLocaleDir(_) => check_flag!(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), - NonTSPseudoClass::Lang(_) | - NonTSPseudoClass::Dir(_) => false, - } - } - } - apply_non_ts_list!(pseudo_class_check_is_enabled_in) - } - - /// Returns whether the pseudo-class is enabled in content sheets. - #[inline] - fn is_enabled_in_content(&self) -> bool { - if matches!(*self, Self::PopoverOpen) { - return static_prefs::pref!("dom.element.popover.enabled"); - } - !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME) - } - - /// Get the state flag associated with a pseudo-class, if any. - pub fn state_flag(&self) -> ElementState { - macro_rules! flag { - (_) => { - ElementState::empty() - }; - ($state:ident) => { - ElementState::$state - }; - } - macro_rules! pseudo_class_state { - ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { - match *self { - $(NonTSPseudoClass::$name => flag!($state),)* - NonTSPseudoClass::Dir(ref dir) => dir.element_state(), - NonTSPseudoClass::MozLocaleDir(..) | - NonTSPseudoClass::Lang(..) => ElementState::empty(), - } - } - } - apply_non_ts_list!(pseudo_class_state) - } - - /// Get the document state flag associated with a pseudo-class, if any. - pub fn document_state_flag(&self) -> DocumentState { - match *self { - NonTSPseudoClass::MozLocaleDir(ref dir) => match dir.as_horizontal_direction() { - Some(HorizontalDirection::Ltr) => DocumentState::LTR_LOCALE, - Some(HorizontalDirection::Rtl) => DocumentState::RTL_LOCALE, - None => DocumentState::empty(), - }, - NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE, - NonTSPseudoClass::MozLWTheme => DocumentState::LWTHEME, - _ => DocumentState::empty(), - } - } - - /// Returns true if the given pseudoclass should trigger style sharing cache - /// revalidation. - pub fn needs_cache_revalidation(&self) -> bool { - self.state_flag().is_empty() && - !matches!( - *self, - // :dir() depends on state only, but may have an empty - // state_flag for invalid arguments. - NonTSPseudoClass::Dir(_) | - // :-moz-is-html only depends on the state of the document and - // the namespace of the element; the former is invariant - // across all the elements involved and the latter is already - // checked for by our caching precondtions. - NonTSPseudoClass::MozIsHTML | - // We prevent style sharing for NAC. - NonTSPseudoClass::MozNativeAnonymous | - // :-moz-placeholder is parsed but never matches. - NonTSPseudoClass::MozPlaceholder | - // :-moz-lwtheme, :-moz-locale-dir and - // :-moz-window-inactive depend only on the state of the - // document, which is invariant across all the elements - // involved in a given style cache. - NonTSPseudoClass::MozLWTheme | - NonTSPseudoClass::MozLocaleDir(_) | - NonTSPseudoClass::MozWindowInactive - ) - } -} - -impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass { - type Impl = SelectorImpl; - - #[inline] - fn is_active_or_hover(&self) -> bool { - matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover) - } - - /// We intentionally skip the link-related ones. - #[inline] - fn is_user_action_state(&self) -> bool { - matches!( - *self, - NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus - ) - } -} - -/// The dummy struct we use to implement our selector parsing. -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SelectorImpl; - -/// A set of extra data to carry along with the matching context, either for -/// selector-matching or invalidation. -#[derive(Default)] -pub struct ExtraMatchingData<'a> { - /// The invalidation data to invalidate doc-state pseudo-classes correctly. - pub invalidation_data: InvalidationMatchingData, - - /// The invalidation bits from matching container queries. These are here - /// just for convenience mostly. - pub cascade_input_flags: ComputedValueFlags, - - /// The style of the originating element in order to evaluate @container - /// size queries affecting pseudo-elements. - pub originating_element_style: Option<&'a ComputedValues>, -} - -impl ::selectors::SelectorImpl for SelectorImpl { - type ExtraMatchingData<'a> = ExtraMatchingData<'a>; - type AttrValue = AtomString; - type Identifier = AtomIdent; - type LocalName = AtomIdent; - type NamespacePrefix = AtomIdent; - type NamespaceUrl = Namespace; - type BorrowedNamespaceUrl = WeakNamespace; - type BorrowedLocalName = WeakAtom; - - type PseudoElement = PseudoElement; - type NonTSPseudoClass = NonTSPseudoClass; - - fn should_collect_attr_hash(name: &AtomIdent) -> bool { - !crate::bloom::is_attr_name_excluded_from_filter(name) - } -} - -impl<'a> SelectorParser<'a> { - fn is_pseudo_class_enabled(&self, pseudo_class: &NonTSPseudoClass) -> bool { - if pseudo_class.is_enabled_in_content() { - return true; - } - - if self.in_user_agent_stylesheet() && - pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS) - { - return true; - } - - if self.chrome_rules_enabled() && - pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME) - { - return true; - } - - return false; - } - - fn is_pseudo_element_enabled(&self, pseudo_element: &PseudoElement) -> bool { - if pseudo_element.enabled_in_content() { - return true; - } - - if self.in_user_agent_stylesheet() && pseudo_element.enabled_in_ua_sheets() { - return true; - } - - if self.chrome_rules_enabled() && pseudo_element.enabled_in_chrome() { - return true; - } - - return false; - } -} - -impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { - type Impl = SelectorImpl; - type Error = StyleParseErrorKind<'i>; - - fn parse_parent_selector(&self) -> bool { - static_prefs::pref!("layout.css.nesting.enabled") - } - - #[inline] - fn parse_slotted(&self) -> bool { - true - } - - #[inline] - fn parse_host(&self) -> bool { - true - } - - #[inline] - fn parse_nth_child_of(&self) -> bool { - true - } - - #[inline] - fn parse_is_and_where(&self) -> bool { - true - } - - #[inline] - fn parse_has(&self) -> bool { - static_prefs::pref!("layout.css.has-selector.enabled") - } - - #[inline] - fn parse_part(&self) -> bool { - true - } - - #[inline] - fn is_is_alias(&self, function: &str) -> bool { - function.eq_ignore_ascii_case("-moz-any") - } - - #[inline] - fn allow_forgiving_selectors(&self) -> bool { - !self.for_supports_rule - } - - fn parse_non_ts_pseudo_class( - &self, - location: SourceLocation, - name: CowRcStr<'i>, - ) -> Result> { - if let Some(pseudo_class) = NonTSPseudoClass::parse_non_functional(&name) { - if self.is_pseudo_class_enabled(&pseudo_class) { - return Ok(pseudo_class); - } - } - Err( - location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( - name, - )), - ) - } - - fn parse_non_ts_functional_pseudo_class<'t>( - &self, - name: CowRcStr<'i>, - parser: &mut Parser<'i, 't>, - ) -> Result> { - let pseudo_class = match_ignore_ascii_case! { &name, - "lang" => { - let result = parser.parse_comma_separated(|input| { - Ok(AtomIdent::from(input.expect_ident_or_string()?.as_ref())) - })?; - if result.is_empty() { - return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - NonTSPseudoClass::Lang(Lang(result.into())) - }, - "-moz-locale-dir" => { - NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?) - }, - "dir" => { - NonTSPseudoClass::Dir(Direction::parse(parser)?) - }, - _ => return Err(parser.new_custom_error( - SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()) - )) - }; - if self.is_pseudo_class_enabled(&pseudo_class) { - Ok(pseudo_class) - } else { - Err( - parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( - name, - )), - ) - } - } - - fn parse_pseudo_element( - &self, - location: SourceLocation, - name: CowRcStr<'i>, - ) -> Result> { - let allow_unkown_webkit = !self.for_supports_rule; - if let Some(pseudo) = PseudoElement::from_slice(&name, allow_unkown_webkit) { - if self.is_pseudo_element_enabled(&pseudo) { - return Ok(pseudo); - } - } - - Err( - location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( - name, - )), - ) - } - - fn parse_functional_pseudo_element<'t>( - &self, - name: CowRcStr<'i>, - parser: &mut Parser<'i, 't>, - ) -> Result> { - if starts_with_ignore_ascii_case(&name, "-moz-tree-") { - // Tree pseudo-elements can have zero or more arguments, separated - // by either comma or space. - let mut args = ThinVec::new(); - loop { - let location = parser.current_source_location(); - match parser.next() { - Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())), - Ok(&Token::Comma) => {}, - Ok(t) => return Err(location.new_unexpected_token_error(t.clone())), - Err(BasicParseError { - kind: BasicParseErrorKind::EndOfInput, - .. - }) => break, - _ => unreachable!("Parser::next() shouldn't return any other error"), - } - } - if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) { - if self.is_pseudo_element_enabled(&pseudo) { - return Ok(pseudo); - } - } - } else if name.eq_ignore_ascii_case("highlight") { - let pseudo = PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref())); - if self.is_pseudo_element_enabled(&pseudo) { - return Ok(pseudo); - } - } - Err( - parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( - name, - )), - ) - } - - fn default_namespace(&self) -> Option { - self.namespaces.default.clone() - } - - fn namespace_for_prefix(&self, prefix: &AtomIdent) -> Option { - self.namespaces.prefixes.get(prefix).cloned() - } -} - -impl SelectorImpl { - /// A helper to traverse each eagerly cascaded pseudo-element, executing - /// `fun` on it. - #[inline] - pub fn each_eagerly_cascaded_pseudo_element(mut fun: F) - where - F: FnMut(PseudoElement), - { - for pseudo in &EAGER_PSEUDOS { - fun(pseudo.clone()) - } - } -} - -// Selector and component sizes are important for matching performance. -size_of_test!(selectors::parser::Selector, 8); -size_of_test!(selectors::parser::Component, 24); -size_of_test!(PseudoElement, 16); -size_of_test!(NonTSPseudoClass, 16); diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs deleted file mode 100644 index 372e7fdb7f5..00000000000 --- a/components/style/gecko/snapshot.rs +++ /dev/null @@ -1,174 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A gecko snapshot, that stores the element attributes and state before they -//! change in order to properly calculate restyle hints. - -use crate::dom::TElement; -use crate::gecko::snapshot_helpers; -use crate::gecko::wrapper::GeckoElement; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::ServoElementSnapshot; -use crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags; -use crate::gecko_bindings::structs::ServoElementSnapshotTable; -use crate::invalidation::element::element_wrapper::ElementSnapshot; -use crate::selector_parser::AttrValue; -use crate::string_cache::{Atom, Namespace}; -use crate::values::{AtomIdent, AtomString}; -use crate::LocalName; -use crate::WeakAtom; -use dom::ElementState; -use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; - -/// A snapshot of a Gecko element. -pub type GeckoElementSnapshot = ServoElementSnapshot; - -/// A map from elements to snapshots for Gecko's style back-end. -pub type SnapshotMap = ServoElementSnapshotTable; - -impl SnapshotMap { - /// Gets the snapshot for this element, if any. - /// - /// FIXME(emilio): The transmute() business we do here is kind of nasty, but - /// it's a consequence of the map being a OpaqueNode -> Snapshot table in - /// Servo and an Element -> Snapshot table in Gecko. - /// - /// We should be able to make this a more type-safe with type annotations by - /// making SnapshotMap a trait and moving the implementations outside, but - /// that's a pain because it implies parameterizing SharedStyleContext. - pub fn get(&self, element: &E) -> Option<&GeckoElementSnapshot> { - debug_assert!(element.has_snapshot()); - - unsafe { - let element = ::std::mem::transmute::<&E, &GeckoElement>(element); - bindings::Gecko_GetElementSnapshot(self, element.0).as_ref() - } - } -} - -impl GeckoElementSnapshot { - #[inline] - fn has_any(&self, flags: Flags) -> bool { - (self.mContains as u8 & flags as u8) != 0 - } - - /// Returns true if the snapshot has stored state for pseudo-classes - /// that depend on things other than `ElementState`. - #[inline] - pub fn has_other_pseudo_class_state(&self) -> bool { - self.has_any(Flags::OtherPseudoClassState) - } - - /// Returns true if the snapshot recorded an id change. - #[inline] - pub fn id_changed(&self) -> bool { - self.mIdAttributeChanged() - } - - /// Returns true if the snapshot recorded a class attribute change. - #[inline] - pub fn class_changed(&self) -> bool { - self.mClassAttributeChanged() - } - - /// Executes the callback once for each attribute that changed. - #[inline] - pub fn each_attr_changed(&self, mut callback: F) - where - F: FnMut(&AtomIdent), - { - for attr in self.mChangedAttrNames.iter() { - unsafe { AtomIdent::with(attr.mRawPtr, &mut callback) } - } - } - - /// selectors::Element::attr_matches - pub fn attr_matches( - &self, - ns: &NamespaceConstraint<&Namespace>, - local_name: &LocalName, - operation: &AttrSelectorOperation<&AttrValue>, - ) -> bool { - snapshot_helpers::attr_matches(self.mAttrs.iter(), ns, local_name, operation) - } -} - -impl ElementSnapshot for GeckoElementSnapshot { - fn debug_list_attributes(&self) -> String { - use nsstring::nsCString; - let mut string = nsCString::new(); - unsafe { - bindings::Gecko_Snapshot_DebugListAttributes(self, &mut string); - } - String::from_utf8_lossy(&*string).into_owned() - } - - fn state(&self) -> Option { - if self.has_any(Flags::State) { - Some(ElementState::from_bits_truncate(self.mState)) - } else { - None - } - } - - #[inline] - fn has_attrs(&self) -> bool { - self.has_any(Flags::Attributes) - } - - #[inline] - fn id_attr(&self) -> Option<&WeakAtom> { - if !self.has_any(Flags::Id) { - return None; - } - - snapshot_helpers::get_id(&*self.mAttrs) - } - - #[inline] - fn is_part(&self, name: &AtomIdent) -> bool { - let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) { - Some(attr) => attr, - None => return false, - }; - - snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) - } - - #[inline] - fn imported_part(&self, name: &AtomIdent) -> Option { - snapshot_helpers::imported_part(&*self.mAttrs, name) - } - - #[inline] - fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - if !self.has_any(Flags::MaybeClass) { - return false; - } - - snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass) - } - - #[inline] - fn each_class(&self, callback: F) - where - F: FnMut(&AtomIdent), - { - if !self.has_any(Flags::MaybeClass) { - return; - } - - snapshot_helpers::each_class_or_part(&self.mClass, callback) - } - - #[inline] - fn lang_attr(&self) -> Option { - let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) }; - if ptr.is_null() { - None - } else { - Some(AtomString(unsafe { Atom::from_addrefed(ptr) })) - } - } -} diff --git a/components/style/gecko/snapshot_helpers.rs b/components/style/gecko/snapshot_helpers.rs deleted file mode 100644 index 818e767e0e9..00000000000 --- a/components/style/gecko/snapshot_helpers.rs +++ /dev/null @@ -1,309 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Element an snapshot common logic. - -use crate::dom::TElement; -use crate::gecko::wrapper::namespace_id_to_atom; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::{self, nsAtom}; -use crate::invalidation::element::element_wrapper::ElementSnapshot; -use crate::selector_parser::{AttrValue, SnapshotMap}; -use crate::string_cache::WeakAtom; -use crate::values::AtomIdent; -use crate::{Atom, CaseSensitivityExt, LocalName, Namespace}; -use selectors::attr::{CaseSensitivity, NamespaceConstraint, AttrSelectorOperation, AttrSelectorOperator}; -use smallvec::SmallVec; - -/// A function that, given an element of type `T`, allows you to get a single -/// class or a class list. -enum Class<'a> { - None, - One(*const nsAtom), - More(&'a [structs::RefPtr]), -} - -#[inline(always)] -fn base_type(attr: &structs::nsAttrValue) -> structs::nsAttrValue_ValueBaseType { - (attr.mBits & structs::NS_ATTRVALUE_BASETYPE_MASK) as structs::nsAttrValue_ValueBaseType -} - -#[inline(always)] -unsafe fn ptr(attr: &structs::nsAttrValue) -> *const T { - (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T -} - -#[inline(always)] -unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class { - debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr)); - let base_type = base_type(attr); - if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase { - return Class::One(ptr::(attr)); - } - if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase { - let container = ptr::(attr); - debug_assert_eq!( - (*container).mType, - structs::nsAttrValue_ValueType_eAtomArray - ); - // NOTE: Bindgen doesn't deal with AutoTArray, so cast it below. - let attr_array: *mut _ = *(*container) - .__bindgen_anon_1 - .mValue - .as_ref() - .__bindgen_anon_1 - .mAtomArray - .as_ref(); - let array = - (*attr_array).mArray.as_ptr() as *const structs::nsTArray>; - return Class::More(&**array); - } - debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eStringBase); - Class::None -} - -#[inline(always)] -unsafe fn get_id_from_attr(attr: &structs::nsAttrValue) -> &WeakAtom { - debug_assert_eq!( - base_type(attr), - structs::nsAttrValue_ValueBaseType_eAtomBase - ); - WeakAtom::new(ptr::(attr)) -} - -impl structs::nsAttrName { - #[inline] - fn is_nodeinfo(&self) -> bool { - self.mBits & 1 != 0 - } - - #[inline] - unsafe fn as_nodeinfo(&self) -> &structs::NodeInfo { - debug_assert!(self.is_nodeinfo()); - &*((self.mBits & !1) as *const structs::NodeInfo) - } - - #[inline] - fn namespace_id(&self) -> i32 { - if !self.is_nodeinfo() { - return structs::kNameSpaceID_None; - } - unsafe { self.as_nodeinfo() }.mInner.mNamespaceID - } - - /// Returns the attribute name as an atom pointer. - #[inline] - pub fn name(&self) -> *const nsAtom { - if self.is_nodeinfo() { - unsafe { self.as_nodeinfo() }.mInner.mName - } else { - self.mBits as *const nsAtom - } - } -} - -/// Find an attribute value with a given name and no namespace. -#[inline(always)] -pub fn find_attr<'a>( - attrs: &'a [structs::AttrArray_InternalAttr], - name: &Atom, -) -> Option<&'a structs::nsAttrValue> { - attrs - .iter() - .find(|attr| attr.mName.mBits == name.as_ptr() as usize) - .map(|attr| &attr.mValue) -} - -/// Finds the id attribute from a list of attributes. -#[inline(always)] -pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> { - Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) }) -} - -#[inline(always)] -pub(super) fn each_exported_part( - attrs: &[structs::AttrArray_InternalAttr], - name: &AtomIdent, - mut callback: impl FnMut(&AtomIdent), -) { - let attr = match find_attr(attrs, &atom!("exportparts")) { - Some(attr) => attr, - None => return, - }; - let mut length = 0; - let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) }; - if atoms.is_null() { - return; - } - - unsafe { - for atom in std::slice::from_raw_parts(atoms, length) { - AtomIdent::with(*atom, &mut callback) - } - } -} - -#[inline(always)] -pub(super) fn imported_part( - attrs: &[structs::AttrArray_InternalAttr], - name: &AtomIdent, -) -> Option { - let attr = find_attr(attrs, &atom!("exportparts"))?; - let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) }; - if atom.is_null() { - return None; - } - Some(AtomIdent(unsafe { Atom::from_raw(atom) })) -} - -/// Given a class or part name, a case sensitivity, and an array of attributes, -/// returns whether the attribute has that name. -#[inline(always)] -pub fn has_class_or_part( - name: &AtomIdent, - case_sensitivity: CaseSensitivity, - attr: &structs::nsAttrValue, -) -> bool { - match unsafe { get_class_or_part_from_attr(attr) } { - Class::None => false, - Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) }, - Class::More(atoms) => match case_sensitivity { - CaseSensitivity::CaseSensitive => { - let name_ptr = name.as_ptr(); - atoms.iter().any(|atom| atom.mRawPtr == name_ptr) - }, - CaseSensitivity::AsciiCaseInsensitive => unsafe { - atoms - .iter() - .any(|atom| WeakAtom::new(atom.mRawPtr).eq_ignore_ascii_case(name)) - }, - }, - } -} - -/// Given an item, a callback, and a getter, execute `callback` for each class -/// or part name this `item` has. -#[inline(always)] -pub fn each_class_or_part(attr: &structs::nsAttrValue, mut callback: F) -where - F: FnMut(&AtomIdent), -{ - unsafe { - match get_class_or_part_from_attr(attr) { - Class::None => {}, - Class::One(atom) => AtomIdent::with(atom, callback), - Class::More(atoms) => { - for atom in atoms { - AtomIdent::with(atom.mRawPtr, &mut callback) - } - }, - } - } -} - -/// Returns a list of classes that were either added to or removed from the -/// element since the snapshot. -pub fn classes_changed(element: &E, snapshots: &SnapshotMap) -> SmallVec<[Atom; 8]> { - debug_assert!(element.has_snapshot(), "Why bothering?"); - let snapshot = snapshots.get(element).expect("has_snapshot lied"); - if !snapshot.class_changed() { - return SmallVec::new(); - } - - let mut classes_changed = SmallVec::<[Atom; 8]>::new(); - snapshot.each_class(|c| { - if !element.has_class(c, CaseSensitivity::CaseSensitive) { - classes_changed.push(c.0.clone()); - } - }); - element.each_class(|c| { - if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) { - classes_changed.push(c.0.clone()); - } - }); - - classes_changed -} - -/// Returns whether a given attribute selector matches given the internal attrs. -pub(crate) fn attr_matches<'a>( - iter: impl Iterator, - ns: &NamespaceConstraint<&Namespace>, - local_name: &LocalName, - operation: &AttrSelectorOperation<&AttrValue>, -) -> bool { - let name_ptr = local_name.as_ptr(); - for attr in iter { - if attr.mName.name() != name_ptr { - continue; - } - - let ns_matches = match *ns { - NamespaceConstraint::Any => true, - NamespaceConstraint::Specific(ns) => { - if *ns == ns!() { - !attr.mName.is_nodeinfo() - } else { - ns.as_ptr() == unsafe { namespace_id_to_atom(attr.mName.namespace_id()) } - } - }, - }; - - if !ns_matches { - continue; - } - - let (operator, case_sensitivity, value) = match *operation { - AttrSelectorOperation::Exists => return true, - AttrSelectorOperation::WithValue { - operator, - case_sensitivity, - value, - } => (operator, case_sensitivity, value), - }; - let ignore_case = match case_sensitivity { - CaseSensitivity::CaseSensitive => false, - CaseSensitivity::AsciiCaseInsensitive => true, - }; - let value = value.as_ptr(); - let matches = unsafe { - match operator { - AttrSelectorOperator::Equal => bindings::Gecko_AttrEquals( - &attr.mValue, - value, - ignore_case, - ), - AttrSelectorOperator::Includes => bindings::Gecko_AttrIncludes( - &attr.mValue, - value, - ignore_case, - ), - AttrSelectorOperator::DashMatch => bindings::Gecko_AttrDashEquals( - &attr.mValue, - value, - ignore_case, - ), - AttrSelectorOperator::Prefix => bindings::Gecko_AttrHasPrefix( - &attr.mValue, - value, - ignore_case, - ), - AttrSelectorOperator::Suffix => bindings::Gecko_AttrHasSuffix( - &attr.mValue, - value, - ignore_case, - ), - AttrSelectorOperator::Substring => bindings::Gecko_AttrHasSubstring( - &attr.mValue, - value, - ignore_case, - ), - } - }; - if matches || *ns != NamespaceConstraint::Any { - return matches; - } - } - false -} diff --git a/components/style/gecko/traversal.rs b/components/style/gecko/traversal.rs deleted file mode 100644 index 71d1a2f949b..00000000000 --- a/components/style/gecko/traversal.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko-specific bits for the styling DOM traversal. - -use crate::context::{SharedStyleContext, StyleContext}; -use crate::dom::{TElement, TNode}; -use crate::gecko::wrapper::{GeckoElement, GeckoNode}; -use crate::traversal::{recalc_style_at, DomTraversal, PerLevelTraversalData}; - -/// This is the simple struct that Gecko uses to encapsulate a DOM traversal for -/// styling. -pub struct RecalcStyleOnly<'a> { - shared: SharedStyleContext<'a>, -} - -impl<'a> RecalcStyleOnly<'a> { - /// Create a `RecalcStyleOnly` traversal from a `SharedStyleContext`. - pub fn new(shared: SharedStyleContext<'a>) -> Self { - RecalcStyleOnly { shared: shared } - } -} - -impl<'recalc, 'le> DomTraversal> for RecalcStyleOnly<'recalc> { - fn process_preorder( - &self, - traversal_data: &PerLevelTraversalData, - context: &mut StyleContext>, - node: GeckoNode<'le>, - note_child: F, - ) where - F: FnMut(GeckoNode<'le>), - { - if let Some(el) = node.as_element() { - let mut data = unsafe { el.ensure_data() }; - recalc_style_at(self, traversal_data, context, el, &mut data, note_child); - } - } - - fn process_postorder(&self, _: &mut StyleContext>, _: GeckoNode<'le>) { - unreachable!(); - } - - /// We don't use the post-order traversal for anything. - fn needs_postorder_traversal() -> bool { - false - } - - fn shared_context(&self) -> &SharedStyleContext { - &self.shared - } -} diff --git a/components/style/gecko/url.rs b/components/style/gecko/url.rs deleted file mode 100644 index 8cf4aa51c0a..00000000000 --- a/components/style/gecko/url.rs +++ /dev/null @@ -1,383 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Common handling for the specified value CSS url() values. - -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs; -use crate::parser::{Parse, ParserContext}; -use crate::stylesheets::{CorsMode, UrlExtraData}; -use crate::values::computed::{Context, ToComputedValue}; -use cssparser::Parser; -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use nsstring::nsCString; -use servo_arc::Arc; -use std::collections::HashMap; -use std::fmt::{self, Write}; -use std::mem::ManuallyDrop; -use std::sync::RwLock; -use style_traits::{CssWriter, ParseError, ToCss}; -use to_shmem::{self, SharedMemoryBuilder, ToShmem}; - -/// A CSS url() value for gecko. -#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] -#[css(function = "url")] -#[repr(C)] -pub struct CssUrl(pub Arc); - -/// Data shared between CssUrls. -/// -/// cbindgen:derive-eq=false -/// cbindgen:derive-neq=false -#[derive(Debug, SpecifiedValueInfo, ToCss, ToShmem)] -#[repr(C)] -pub struct CssUrlData { - /// The URL in unresolved string form. - serialization: crate::OwnedStr, - - /// The URL extra data. - #[css(skip)] - pub extra_data: UrlExtraData, - - /// The CORS mode that will be used for the load. - #[css(skip)] - cors_mode: CorsMode, - - /// Data to trigger a load from Gecko. This is mutable in C++. - /// - /// TODO(emilio): Maybe we can eagerly resolve URLs and make this immutable? - #[css(skip)] - load_data: LoadDataSource, -} - -impl PartialEq for CssUrlData { - fn eq(&self, other: &Self) -> bool { - self.serialization == other.serialization && - self.extra_data == other.extra_data && - self.cors_mode == other.cors_mode - } -} - -impl CssUrl { - fn parse_with_cors_mode<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - cors_mode: CorsMode, - ) -> Result> { - let url = input.expect_url()?; - Ok(Self::parse_from_string( - url.as_ref().to_owned(), - context, - cors_mode, - )) - } - - /// Parse a URL from a string value that is a valid CSS token for a URL. - pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self { - CssUrl(Arc::new(CssUrlData { - serialization: url.into(), - extra_data: context.url_data.clone(), - cors_mode, - load_data: LoadDataSource::Owned(LoadData::default()), - })) - } - - /// Returns true if the URL is definitely invalid. We don't eagerly resolve - /// URLs in gecko, so we just return false here. - /// use its |resolved| status. - pub fn is_invalid(&self) -> bool { - false - } - - /// Returns true if this URL looks like a fragment. - /// See https://drafts.csswg.org/css-values/#local-urls - #[inline] - pub fn is_fragment(&self) -> bool { - self.0.is_fragment() - } - - /// Return the unresolved url as string, or the empty string if it's - /// invalid. - #[inline] - pub fn as_str(&self) -> &str { - self.0.as_str() - } -} - -impl CssUrlData { - /// Returns true if this URL looks like a fragment. - /// See https://drafts.csswg.org/css-values/#local-urls - pub fn is_fragment(&self) -> bool { - self.as_str() - .as_bytes() - .iter() - .next() - .map_or(false, |b| *b == b'#') - } - - /// Return the unresolved url as string, or the empty string if it's - /// invalid. - pub fn as_str(&self) -> &str { - &*self.serialization - } -} - -impl Parse for CssUrl { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - Self::parse_with_cors_mode(context, input, CorsMode::None) - } -} - -impl Eq for CssUrl {} - -impl MallocSizeOf for CssUrl { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - // XXX: measure `serialization` once bug 1397971 lands - - // We ignore `extra_data`, because RefPtr is tricky, and there aren't - // many of them in practise (sharing is common). - - 0 - } -} - -/// A key type for LOAD_DATA_TABLE. -#[derive(Eq, Hash, PartialEq)] -struct LoadDataKey(*const LoadDataSource); - -unsafe impl Sync for LoadDataKey {} -unsafe impl Send for LoadDataKey {} - -bitflags! { - /// Various bits of mutable state that are kept for image loads. - #[repr(C)] - pub struct LoadDataFlags: u8 { - /// Whether we tried to resolve the uri at least once. - const TRIED_TO_RESOLVE_URI = 1 << 0; - /// Whether we tried to resolve the image at least once. - const TRIED_TO_RESOLVE_IMAGE = 1 << 1; - } -} - -/// This is usable and movable from multiple threads just fine, as long as it's -/// not cloned (it is not clonable), and the methods that mutate it run only on -/// the main thread (when all the other threads we care about are paused). -unsafe impl Sync for LoadData {} -unsafe impl Send for LoadData {} - -/// The load data for a given URL. This is mutable from C++, and shouldn't be -/// accessed from rust for anything. -#[repr(C)] -#[derive(Debug)] -pub struct LoadData { - /// A strong reference to the imgRequestProxy, if any, that should be - /// released on drop. - /// - /// These are raw pointers because they are not safe to reference-count off - /// the main thread. - resolved_image: *mut structs::imgRequestProxy, - /// A strong reference to the resolved URI of this image. - resolved_uri: *mut structs::nsIURI, - /// A few flags that are set when resolving the image or such. - flags: LoadDataFlags, -} - -impl Drop for LoadData { - fn drop(&mut self) { - unsafe { bindings::Gecko_LoadData_Drop(self) } - } -} - -impl Default for LoadData { - fn default() -> Self { - Self { - resolved_image: std::ptr::null_mut(), - resolved_uri: std::ptr::null_mut(), - flags: LoadDataFlags::empty(), - } - } -} - -/// The data for a load, or a lazy-loaded, static member that will be stored in -/// LOAD_DATA_TABLE, keyed by the memory location of this object, which is -/// always in the heap because it's inside the CssUrlData object. -/// -/// This type is meant not to be used from C++ so we don't derive helper -/// methods. -/// -/// cbindgen:derive-helper-methods=false -#[derive(Debug)] -#[repr(u8, C)] -pub enum LoadDataSource { - /// An owned copy of the load data. - Owned(LoadData), - /// A lazily-resolved copy of it. - Lazy, -} - -impl LoadDataSource { - /// Gets the load data associated with the source. - /// - /// This relies on the source on being in a stable location if lazy. - #[inline] - pub unsafe fn get(&self) -> *const LoadData { - match *self { - LoadDataSource::Owned(ref d) => return d, - LoadDataSource::Lazy => {}, - } - - let key = LoadDataKey(self); - - { - let guard = LOAD_DATA_TABLE.read().unwrap(); - if let Some(r) = guard.get(&key) { - return &**r; - } - } - let mut guard = LOAD_DATA_TABLE.write().unwrap(); - let r = guard.entry(key).or_insert_with(Default::default); - &**r - } -} - -impl ToShmem for LoadDataSource { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { - Ok(ManuallyDrop::new(match self { - LoadDataSource::Owned(..) => LoadDataSource::Lazy, - LoadDataSource::Lazy => LoadDataSource::Lazy, - })) - } -} - -/// A specified non-image `url()` value. -pub type SpecifiedUrl = CssUrl; - -/// Clears LOAD_DATA_TABLE. Entries in this table, which are for specified URL -/// values that come from shared memory style sheets, would otherwise persist -/// until the end of the process and be reported as leaks. -pub fn shutdown() { - LOAD_DATA_TABLE.write().unwrap().clear(); -} - -impl ToComputedValue for SpecifiedUrl { - type ComputedValue = ComputedUrl; - - #[inline] - fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { - ComputedUrl(self.clone()) - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - computed.0.clone() - } -} - -/// A specified image `url()` value. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] -pub struct SpecifiedImageUrl(pub SpecifiedUrl); - -impl SpecifiedImageUrl { - /// Parse a URL from a string value that is a valid CSS token for a URL. - pub fn parse_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self { - SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context, cors_mode)) - } - - /// Provides an alternate method for parsing that associates the URL - /// with anonymous CORS headers. - pub fn parse_with_cors_mode<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - cors_mode: CorsMode, - ) -> Result> { - Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode( - context, input, cors_mode, - )?)) - } -} - -impl Parse for SpecifiedImageUrl { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl) - } -} - -impl ToComputedValue for SpecifiedImageUrl { - type ComputedValue = ComputedImageUrl; - - #[inline] - fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { - ComputedImageUrl(self.0.to_computed_value(context)) - } - - #[inline] - fn from_computed_value(computed: &Self::ComputedValue) -> Self { - SpecifiedImageUrl(ToComputedValue::from_computed_value(&computed.0)) - } -} - -/// The computed value of a CSS non-image `url()`. -/// -/// The only difference between specified and computed URLs is the -/// serialization. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] -#[repr(C)] -pub struct ComputedUrl(pub SpecifiedUrl); - -impl ComputedUrl { - fn serialize_with( - &self, - function: unsafe extern "C" fn(*const Self, *mut nsCString), - dest: &mut CssWriter, - ) -> fmt::Result - where - W: Write, - { - dest.write_str("url(")?; - unsafe { - let mut string = nsCString::new(); - function(self, &mut string); - string.as_str_unchecked().to_css(dest)?; - } - dest.write_char(')') - } -} - -impl ToCss for ComputedUrl { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest) - } -} - -/// The computed value of a CSS image `url()`. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] -#[repr(transparent)] -pub struct ComputedImageUrl(pub ComputedUrl); - -impl ToCss for ComputedImageUrl { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - self.0 - .serialize_with(bindings::Gecko_GetComputedImageURLSpec, dest) - } -} - -lazy_static! { - /// A table mapping CssUrlData objects to their lazily created LoadData - /// objects. - static ref LOAD_DATA_TABLE: RwLock>> = { - Default::default() - }; -} diff --git a/components/style/gecko/values.rs b/components/style/gecko/values.rs deleted file mode 100644 index fbdb02c6bad..00000000000 --- a/components/style/gecko/values.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#![allow(unsafe_code)] - -//! Different kind of helpers to interact with Gecko values. - -use crate::color::{AbsoluteColor, ColorSpace}; -use crate::counter_style::{Symbol, Symbols}; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::CounterStylePtr; -use crate::values::generics::CounterStyle; -use crate::values::Either; -use crate::Atom; - -/// Convert a color value to `nscolor`. -pub fn convert_absolute_color_to_nscolor(color: &AbsoluteColor) -> u32 { - let srgb = color.to_color_space(ColorSpace::Srgb); - u32::from_le_bytes([ - (srgb.components.0 * 255.0).round() as u8, - (srgb.components.1 * 255.0).round() as u8, - (srgb.components.2 * 255.0).round() as u8, - (srgb.alpha * 255.0).round() as u8, - ]) -} - -/// Convert a given `nscolor` to a Servo AbsoluteColor value. -pub fn convert_nscolor_to_absolute_color(color: u32) -> AbsoluteColor { - let [r, g, b, a] = color.to_le_bytes(); - AbsoluteColor::srgb( - r as f32 / 255.0, - g as f32 / 255.0, - b as f32 / 255.0, - a as f32 / 255.0, - ) -} - -impl CounterStyle { - /// Convert this counter style to a Gecko CounterStylePtr. - #[inline] - pub fn to_gecko_value(&self, gecko_value: &mut CounterStylePtr) { - unsafe { bindings::Gecko_CounterStyle_ToPtr(self, gecko_value) } - } - - /// Convert Gecko CounterStylePtr to CounterStyle or String. - pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either { - use crate::values::CustomIdent; - - let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) }; - if !name.is_null() { - let name = unsafe { Atom::from_raw(name) }; - debug_assert_ne!(name, atom!("none")); - Either::First(CounterStyle::Name(CustomIdent(name))) - } else { - let anonymous = - unsafe { bindings::Gecko_CounterStyle_GetAnonymous(gecko_value).as_ref() }.unwrap(); - let symbols = &anonymous.mSymbols; - if anonymous.mSingleString { - debug_assert_eq!(symbols.len(), 1); - Either::Second(symbols[0].to_string()) - } else { - let symbol_type = anonymous.mSymbolsType; - let symbols = symbols - .iter() - .map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into())) - .collect(); - Either::First(CounterStyle::Symbols(symbol_type, Symbols(symbols))) - } - } - } -} diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs deleted file mode 100644 index 490c063fbe7..00000000000 --- a/components/style/gecko/wrapper.rs +++ /dev/null @@ -1,2125 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#![allow(unsafe_code)] - -//! Wrapper definitions on top of Gecko types in order to be used in the style -//! system. -//! -//! This really follows the Servo pattern in -//! `components/script/layout_wrapper.rs`. -//! -//! This theoretically should live in its own crate, but now it lives in the -//! style system it's kind of pointless in the Stylo case, and only Servo forces -//! the separation between the style system implementation and everything else. - -use crate::applicable_declarations::ApplicableDeclarationBlock; -use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateAnimationsTasks}; -use crate::data::ElementData; -use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot}; -use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; -use crate::gecko::snapshot_helpers; -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations; -use crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations; -use crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions; -use crate::gecko_bindings::bindings::Gecko_ElementState; -use crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock; -use crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount; -use crate::gecko_bindings::bindings::Gecko_GetAnimationRule; -use crate::gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations; -use crate::gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock; -use crate::gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock; -use crate::gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock; -use crate::gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock; -use crate::gecko_bindings::bindings::Gecko_IsSignificantChild; -use crate::gecko_bindings::bindings::Gecko_MatchLang; -use crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr; -use crate::gecko_bindings::bindings::Gecko_UpdateAnimations; -use crate::gecko_bindings::structs; -use crate::gecko_bindings::structs::nsChangeHint; -use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel; -use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT; -use crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO; -use crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO; -use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT; -use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES; -use crate::gecko_bindings::structs::NODE_NEEDS_FRAME; -use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag}; -use crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement}; -use crate::global_style_data::GLOBAL_STYLE_DATA; -use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::media_queries::Device; -use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; -use crate::properties::{ComputedValues, LonghandId}; -use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; -use crate::rule_tree::CascadeLevel as ServoCascadeLevel; -use crate::selector_parser::{AttrValue, Lang}; -use crate::shared_lock::{Locked, SharedRwLock}; -use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; -use crate::stylist::CascadeData; -use crate::values::computed::Display; -use crate::values::{AtomIdent, AtomString}; -use crate::CaseSensitivityExt; -use crate::LocalName; -use app_units::Au; -use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; -use dom::{DocumentState, ElementState}; -use euclid::default::Size2D; -use fxhash::FxHashMap; -use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::VisitedHandlingMode; -use selectors::matching::{ElementSelectorFlags, MatchingContext}; -use selectors::sink::Push; -use selectors::{Element, OpaqueElement}; -use servo_arc::{Arc, ArcBorrow}; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::mem; -use std::ptr; -use std::sync::atomic::{AtomicU32, Ordering}; - -#[inline] -fn elements_with_id<'a, 'le>( - array: *const structs::nsTArray<*mut RawGeckoElement>, -) -> &'a [GeckoElement<'le>] { - unsafe { - if array.is_null() { - return &[]; - } - - let elements: &[*mut RawGeckoElement] = &**array; - - // NOTE(emilio): We rely on the in-memory representation of - // GeckoElement<'ld> and *mut RawGeckoElement being the same. - #[allow(dead_code)] - unsafe fn static_assert() { - mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _); - } - - mem::transmute(elements) - } -} - -/// A simple wrapper over `Document`. -#[derive(Clone, Copy)] -pub struct GeckoDocument<'ld>(pub &'ld structs::Document); - -impl<'ld> TDocument for GeckoDocument<'ld> { - type ConcreteNode = GeckoNode<'ld>; - - #[inline] - fn as_node(&self) -> Self::ConcreteNode { - GeckoNode(&self.0._base) - } - - #[inline] - fn is_html_document(&self) -> bool { - self.0.mType == structs::Document_Type::eHTML - } - - #[inline] - fn quirks_mode(&self) -> QuirksMode { - self.0.mCompatMode.into() - } - - #[inline] - fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()> - where - Self: 'a, - { - Ok(elements_with_id(unsafe { - bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr()) - })) - } - - fn shared_lock(&self) -> &SharedRwLock { - &GLOBAL_STYLE_DATA.shared_lock - } -} - -/// A simple wrapper over `ShadowRoot`. -#[derive(Clone, Copy)] -pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot); - -impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // TODO(emilio): Maybe print the host or something? - write!(f, " ({:#x})", self.as_node().opaque().0) - } -} - -impl<'lr> PartialEq for GeckoShadowRoot<'lr> { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0 as *const _ == other.0 as *const _ - } -} - -impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { - type ConcreteNode = GeckoNode<'lr>; - - #[inline] - fn as_node(&self) -> Self::ConcreteNode { - GeckoNode(&self.0._base._base._base._base) - } - - #[inline] - fn host(&self) -> GeckoElement<'lr> { - GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr }) - } - - #[inline] - fn style_data<'a>(&self) -> Option<&'a CascadeData> - where - Self: 'a, - { - let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? }; - Some(&author_styles.data) - } - - #[inline] - fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()> - where - Self: 'a, - { - Ok(elements_with_id(unsafe { - bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr()) - })) - } - - #[inline] - fn parts<'a>(&self) -> &[::ConcreteElement] - where - Self: 'a, - { - let slice: &[*const RawGeckoElement] = &*self.0.mParts; - - #[allow(dead_code)] - unsafe fn static_assert() { - mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _); - } - - unsafe { mem::transmute(slice) } - } -} - -/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer. -/// -/// Important: We don't currently refcount the DOM, because the wrapper lifetime -/// magic guarantees that our LayoutFoo references won't outlive the root, and -/// we don't mutate any of the references on the Gecko side during restyle. -/// -/// We could implement refcounting if need be (at a potentially non-trivial -/// performance cost) by implementing Drop and making LayoutFoo non-Copy. -#[derive(Clone, Copy)] -pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode); - -impl<'ln> PartialEq for GeckoNode<'ln> { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0 as *const _ == other.0 as *const _ - } -} - -impl<'ln> fmt::Debug for GeckoNode<'ln> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(el) = self.as_element() { - return el.fmt(f); - } - - if self.is_text_node() { - return write!(f, " ({:#x})", self.opaque().0); - } - - if self.is_document() { - return write!(f, " ({:#x})", self.opaque().0); - } - - if let Some(sr) = self.as_shadow_root() { - return sr.fmt(f); - } - - write!(f, " ({:#x})", self.opaque().0) - } -} - -impl<'ln> GeckoNode<'ln> { - #[inline] - fn is_document(&self) -> bool { - // This is a DOM constant that isn't going to change. - const DOCUMENT_NODE: u16 = 9; - self.node_info().mInner.mNodeType == DOCUMENT_NODE - } - - #[inline] - fn is_shadow_root(&self) -> bool { - self.is_in_shadow_tree() && self.parent_node().is_none() - } - - #[inline] - fn from_content(content: &'ln nsIContent) -> Self { - GeckoNode(&content._base) - } - - #[inline] - fn set_flags(&self, flags: u32) { - self.flags_atomic().fetch_or(flags, Ordering::Relaxed); - } - - #[inline] - fn flags_atomic(&self) -> &AtomicU32 { - use std::cell::Cell; - let flags: &Cell = &(self.0)._base._base_1.mFlags; - - #[allow(dead_code)] - fn static_assert() { - let _: [u8; std::mem::size_of::>()] = [0u8; std::mem::size_of::()]; - let _: [u8; std::mem::align_of::>()] = - [0u8; std::mem::align_of::()]; - } - - // Rust doesn't provide standalone atomic functions like GCC/clang do - // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees - // that the memory representation of u32 and AtomicU32 matches: - // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html - unsafe { std::mem::transmute::<&Cell, &AtomicU32>(flags) } - } - - #[inline] - fn flags(&self) -> u32 { - self.flags_atomic().load(Ordering::Relaxed) - } - - #[inline] - fn node_info(&self) -> &structs::NodeInfo { - debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null()); - unsafe { &*self.0.mNodeInfo.mRawPtr } - } - - // These live in different locations depending on processor architecture. - #[cfg(target_pointer_width = "64")] - #[inline] - fn bool_flags(&self) -> u32 { - (self.0)._base._base_1.mBoolFlags - } - - #[cfg(target_pointer_width = "32")] - #[inline] - fn bool_flags(&self) -> u32 { - (self.0).mBoolFlags - } - - #[inline] - fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool { - self.bool_flags() & (1u32 << flag as u32) != 0 - } - - /// This logic is duplicate in Gecko's nsINode::IsInShadowTree(). - #[inline] - fn is_in_shadow_tree(&self) -> bool { - use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE; - self.flags() & NODE_IS_IN_SHADOW_TREE != 0 - } - - /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the - /// same thing. - /// - /// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and - /// from profiles it seems that keeping this fast path makes the compiler not inline - /// `flattened_tree_parent` as a whole, so we're not gaining much either. - #[inline] - fn flattened_tree_parent_is_parent(&self) -> bool { - use crate::gecko_bindings::structs::*; - let flags = self.flags(); - - let parent = match self.parent_node() { - Some(p) => p, - None => return true, - }; - - if parent.is_shadow_root() { - return false; - } - - if let Some(parent) = parent.as_element() { - if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() { - return false; - } - if parent.shadow_root().is_some() || parent.is_html_slot_element() { - return false; - } - } - - true - } - - #[inline] - fn flattened_tree_parent(&self) -> Option { - if self.flattened_tree_parent_is_parent() { - debug_assert_eq!( - unsafe { - bindings::Gecko_GetFlattenedTreeParentNode(self.0) - .as_ref() - .map(GeckoNode) - }, - self.parent_node(), - "Fast path stopped holding!" - ); - return self.parent_node(); - } - - // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively. - unsafe { - bindings::Gecko_GetFlattenedTreeParentNode(self.0) - .as_ref() - .map(GeckoNode) - } - } - - #[inline] - fn contains_non_whitespace_content(&self) -> bool { - unsafe { Gecko_IsSignificantChild(self.0, false) } - } -} - -impl<'ln> NodeInfo for GeckoNode<'ln> { - #[inline] - fn is_element(&self) -> bool { - self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement) - } - - fn is_text_node(&self) -> bool { - // This is a DOM constant that isn't going to change. - const TEXT_NODE: u16 = 3; - self.node_info().mInner.mNodeType == TEXT_NODE - } -} - -impl<'ln> TNode for GeckoNode<'ln> { - type ConcreteDocument = GeckoDocument<'ln>; - type ConcreteShadowRoot = GeckoShadowRoot<'ln>; - type ConcreteElement = GeckoElement<'ln>; - - #[inline] - fn parent_node(&self) -> Option { - unsafe { self.0.mParent.as_ref().map(GeckoNode) } - } - - #[inline] - fn first_child(&self) -> Option { - unsafe { - self.0 - .mFirstChild - .raw() - .as_ref() - .map(GeckoNode::from_content) - } - } - - #[inline] - fn last_child(&self) -> Option { - unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) } - } - - #[inline] - fn prev_sibling(&self) -> Option { - unsafe { - let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?); - if prev_or_last.0.mNextSibling.raw().is_null() { - return None; - } - Some(prev_or_last) - } - } - - #[inline] - fn next_sibling(&self) -> Option { - unsafe { - self.0 - .mNextSibling - .raw() - .as_ref() - .map(GeckoNode::from_content) - } - } - - #[inline] - fn owner_doc(&self) -> Self::ConcreteDocument { - debug_assert!(!self.node_info().mDocument.is_null()); - GeckoDocument(unsafe { &*self.node_info().mDocument }) - } - - #[inline] - fn is_in_document(&self) -> bool { - self.get_bool_flag(nsINode_BooleanFlag::IsInDocument) - } - - fn traversal_parent(&self) -> Option> { - self.flattened_tree_parent().and_then(|n| n.as_element()) - } - - #[inline] - fn opaque(&self) -> OpaqueNode { - let ptr: usize = self.0 as *const _ as usize; - OpaqueNode(ptr) - } - - fn debug_id(self) -> usize { - unimplemented!() - } - - #[inline] - fn as_element(&self) -> Option> { - if !self.is_element() { - return None; - } - - Some(GeckoElement(unsafe { - &*(self.0 as *const _ as *const RawGeckoElement) - })) - } - - #[inline] - fn as_document(&self) -> Option { - if !self.is_document() { - return None; - } - - debug_assert_eq!(self.owner_doc().as_node(), *self, "How?"); - Some(self.owner_doc()) - } - - #[inline] - fn as_shadow_root(&self) -> Option { - if !self.is_shadow_root() { - return None; - } - - Some(GeckoShadowRoot(unsafe { - &*(self.0 as *const _ as *const structs::ShadowRoot) - })) - } -} - -/// A wrapper on top of two kind of iterators, depending on the parent being -/// iterated. -/// -/// We generally iterate children by traversing the light-tree siblings of the -/// first child like Servo does. -/// -/// However, for nodes with anonymous children, we use a custom (heavier-weight) -/// Gecko-implemented iterator. -/// -/// FIXME(emilio): If we take into account shadow DOM, we're going to need the -/// flat tree pretty much always. We can try to optimize the case where there's -/// no shadow root sibling, probably. -pub enum GeckoChildrenIterator<'a> { - /// A simple iterator that tracks the current node being iterated and - /// replaces it with the next sibling when requested. - Current(Option>), - /// A Gecko-implemented iterator we need to drop appropriately. - GeckoIterator(structs::StyleChildrenIterator), -} - -impl<'a> Drop for GeckoChildrenIterator<'a> { - fn drop(&mut self) { - if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self { - unsafe { - bindings::Gecko_DestroyStyleChildrenIterator(it); - } - } - } -} - -impl<'a> Iterator for GeckoChildrenIterator<'a> { - type Item = GeckoNode<'a>; - fn next(&mut self) -> Option> { - match *self { - GeckoChildrenIterator::Current(curr) => { - let next = curr.and_then(|node| node.next_sibling()); - *self = GeckoChildrenIterator::Current(next); - curr - }, - GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe { - // We do this unsafe lengthening of the lifetime here because - // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>, - // however we can't express this easily with bindgen, and it would - // introduce functions with two input lifetimes into bindgen, - // which would be out of scope for elision. - bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _)) - .as_ref() - .map(GeckoNode) - }, - } - } -} - -/// A simple wrapper over a non-null Gecko `Element` pointer. -#[derive(Clone, Copy)] -pub struct GeckoElement<'le>(pub &'le RawGeckoElement); - -impl<'le> fmt::Debug for GeckoElement<'le> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use nsstring::nsCString; - - write!(f, "<{}", self.local_name())?; - - let mut attrs = nsCString::new(); - unsafe { - bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs); - } - write!(f, "{}", attrs)?; - write!(f, "> ({:#x})", self.as_node().opaque().0) - } -} - -impl<'le> GeckoElement<'le> { - /// Gets the raw `ElementData` refcell for the element. - #[inline(always)] - pub fn get_data(&self) -> Option<&AtomicRefCell> { - unsafe { self.0.mServoData.get().as_ref() } - } - - /// Returns whether any animation applies to this element. - #[inline] - pub fn has_any_animation(&self) -> bool { - self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) } - } - - #[inline(always)] - fn attrs(&self) -> Option<&structs::AttrArray_Impl> { - unsafe { self.0.mAttrs.mImpl.mPtr.as_ref() } - } - - #[inline(always)] - fn non_mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { - let attrs = match self.attrs() { - Some(attrs) => attrs, - None => return &[], - }; - unsafe { - attrs.mBuffer.as_slice(attrs.mAttrCount as usize) - } - } - - #[inline(always)] - fn mapped_attrs(&self) -> &[structs::AttrArray_InternalAttr] { - let attrs = match self.attrs() { - Some(attrs) => attrs, - None => return &[], - }; - unsafe { - let attrs = match attrs.mMappedAttrs.as_ref() { - Some(attrs) => attrs, - None => return &[], - }; - - attrs.mBuffer.as_slice(attrs.mAttrCount as usize) - } - } - - #[inline] - fn iter_attrs(&self) -> impl Iterator { - self.non_mapped_attrs().iter().chain(self.mapped_attrs().iter()) - } - - #[inline(always)] - fn get_part_attr(&self) -> Option<&structs::nsAttrValue> { - if !self.has_part_attr() { - return None; - } - snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("part")) - } - - #[inline(always)] - fn get_class_attr(&self) -> Option<&structs::nsAttrValue> { - if !self.may_have_class() { - return None; - } - - if self.is_svg_element() { - let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() }; - if let Some(c) = svg_class { - return Some(c); - } - } - - snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("class")) - } - - #[inline] - fn may_have_anonymous_children(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren) - } - - #[inline] - fn flags(&self) -> u32 { - self.as_node().flags() - } - - #[inline] - fn set_flags(&self, flags: u32) { - self.as_node().set_flags(flags); - } - - #[inline] - unsafe fn unset_flags(&self, flags: u32) { - self.as_node() - .flags_atomic() - .fetch_and(!flags, Ordering::Relaxed); - } - - /// Returns true if this element has descendants for lazy frame construction. - #[inline] - pub fn descendants_need_frames(&self) -> bool { - self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0 - } - - /// Returns true if this element needs lazy frame construction. - #[inline] - pub fn needs_frame(&self) -> bool { - self.flags() & NODE_NEEDS_FRAME != 0 - } - - /// Returns a reference to the DOM slots for this Element, if they exist. - #[inline] - fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> { - let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots; - unsafe { slots.as_ref() } - } - - /// Returns a reference to the extended DOM slots for this Element. - #[inline] - fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> { - self.dom_slots().and_then(|s| unsafe { - // For the bit usage, see nsContentSlots::GetExtendedSlots. - let e_slots = s._base.mExtendedSlots & - !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag; - (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref() - }) - } - - #[inline] - fn namespace_id(&self) -> i32 { - self.as_node().node_info().mInner.mNamespaceID - } - - #[inline] - fn has_id(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasID) - } - - #[inline] - fn state_internal(&self) -> u64 { - if !self - .as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates) - { - return self.0.mState.bits; - } - unsafe { Gecko_ElementState(self.0) } - } - - #[inline] - fn document_state(&self) -> DocumentState { - DocumentState::from_bits_truncate(self.as_node().owner_doc().0.mDocumentState.bits) - } - - #[inline] - fn may_have_class(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass) - } - - #[inline] - fn has_properties(&self) -> bool { - use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES; - - self.flags() & NODE_HAS_PROPERTIES != 0 - } - - #[inline] - fn before_or_after_pseudo(&self, is_before: bool) -> Option { - if !self.has_properties() { - return None; - } - - unsafe { - bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before) - .as_ref() - .map(GeckoElement) - } - } - - #[inline] - fn may_have_style_attribute(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle) - } - - /// Only safe to call on the main thread, with exclusive access to the - /// element and its ancestors. - /// - /// This function is also called after display property changed for SMIL - /// animation. - /// - /// Also this function schedules style flush. - pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) { - use crate::gecko::restyle_damage::GeckoRestyleDamage; - - let damage = GeckoRestyleDamage::new(change_hint); - debug!( - "note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}", - self, restyle_hint, change_hint - ); - - debug_assert!( - !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()), - "Animation restyle hints should not appear with non-animation restyle hints" - ); - - let mut data = match self.mutate_data() { - Some(d) => d, - None => { - debug!("(Element not styled, discarding hints)"); - return; - }, - }; - - debug_assert!(data.has_styles(), "how?"); - - // Propagate the bit up the chain. - if restyle_hint.has_animation_hint() { - bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0); - } else { - bindings::Gecko_NoteDirtyElement(self.0); - } - - data.hint.insert(restyle_hint); - data.damage |= damage; - } - - /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree. - #[inline] - fn is_root_of_native_anonymous_subtree(&self) -> bool { - use crate::gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS_ROOT; - return self.flags() & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0; - } - - /// Returns true if this node is the shadow root of an use-element shadow tree. - #[inline] - fn is_root_of_use_element_shadow_tree(&self) -> bool { - if !self.as_node().is_in_shadow_tree() { - return false; - } - if !self.parent_node_is_shadow_root() { - return false; - } - let host = self.containing_shadow_host().unwrap(); - host.is_svg_element() && host.local_name() == &**local_name!("use") - } - - fn css_transitions_info(&self) -> FxHashMap> { - use crate::gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt; - use crate::gecko_bindings::bindings::Gecko_ElementTransitions_Length; - - let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize; - let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default()); - - for i in 0..collection_length { - let end_value = - unsafe { Arc::from_raw_addrefed(Gecko_ElementTransitions_EndValueAt(self.0, i)) }; - let property = end_value.id(); - debug_assert!(!property.is_logical()); - map.insert(property, end_value); - } - map - } - - fn needs_transitions_update_per_property( - &self, - longhand_id: LonghandId, - combined_duration_seconds: f32, - before_change_style: &ComputedValues, - after_change_style: &ComputedValues, - existing_transitions: &FxHashMap>, - ) -> bool { - use crate::values::animated::{Animate, Procedure}; - debug_assert!(!longhand_id.is_logical()); - - // If there is an existing transition, update only if the end value - // differs. - // - // If the end value has not changed, we should leave the currently - // running transition as-is since we don't want to interrupt its timing - // function. - if let Some(ref existing) = existing_transitions.get(&longhand_id) { - let after_value = - AnimationValue::from_computed_values(longhand_id, after_change_style).unwrap(); - - return ***existing != after_value; - } - - let from = AnimationValue::from_computed_values(longhand_id, before_change_style); - let to = AnimationValue::from_computed_values(longhand_id, after_change_style); - - debug_assert_eq!(to.is_some(), from.is_some()); - - combined_duration_seconds > 0.0f32 && - from != to && - from.unwrap() - .animate( - to.as_ref().unwrap(), - Procedure::Interpolate { progress: 0.5 }, - ) - .is_ok() - } -} - -/// Converts flags from the layout used by rust-selectors to the layout used -/// by Gecko. We could align these and then do this without conditionals, but -/// it's probably not worth the trouble. -fn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 { - use crate::gecko_bindings::structs::*; - let mut gecko_flags = 0u32; - if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR; - } - if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS; - } - if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) { - gecko_flags |= NODE_HAS_SLOW_SELECTOR_NTH_OF; - } - if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) { - gecko_flags |= NODE_HAS_EDGE_CHILD_SELECTOR; - } - if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) { - gecko_flags |= NODE_HAS_EMPTY_SELECTOR; - } - - gecko_flags -} - -fn get_animation_rule( - element: &GeckoElement, - cascade_level: CascadeLevel, -) -> Option>> { - use crate::properties::longhands::ANIMATABLE_PROPERTY_COUNT; - - // There's a very rough correlation between the number of effects - // (animations) on an element and the number of properties it is likely to - // animate, so we use that as an initial guess for the size of the - // AnimationValueMap in order to reduce the number of re-allocations needed. - let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) }; - // Also, we should try to reuse the PDB, to avoid creating extra rule nodes. - let mut animation_values = AnimationValueMap::with_capacity_and_hasher( - effect_count.min(ANIMATABLE_PROPERTY_COUNT), - Default::default(), - ); - if unsafe { Gecko_GetAnimationRule(element.0, cascade_level, &mut animation_values) } { - let shared_lock = &GLOBAL_STYLE_DATA.shared_lock; - Some(Arc::new(shared_lock.wrap( - PropertyDeclarationBlock::from_animation_value_map(&animation_values), - ))) - } else { - None - } -} - -/// Turns a gecko namespace id into an atom. Might panic if you pass any random thing that isn't a -/// namespace id. -#[inline(always)] -pub unsafe fn namespace_id_to_atom(id: i32) -> *mut nsAtom { - unsafe { - let namespace_manager = structs::nsNameSpaceManager_sInstance.mRawPtr; - (*namespace_manager).mURIArray[id as usize].mRawPtr - } -} - -impl<'le> TElement for GeckoElement<'le> { - type ConcreteNode = GeckoNode<'le>; - type TraversalChildrenIterator = GeckoChildrenIterator<'le>; - - fn inheritance_parent(&self) -> Option { - if self.is_pseudo_element() { - return self.pseudo_element_originating_element(); - } - - self.as_node() - .flattened_tree_parent() - .and_then(|n| n.as_element()) - } - - fn traversal_children(&self) -> LayoutIterator> { - // This condition is similar to the check that - // StyleChildrenIterator::IsNeeded does, except that it might return - // true if we used to (but no longer) have anonymous content from - // ::before/::after, or nsIAnonymousContentCreators. - if self.is_html_slot_element() || - self.shadow_root().is_some() || - self.may_have_anonymous_children() - { - unsafe { - let mut iter: structs::StyleChildrenIterator = ::std::mem::zeroed(); - bindings::Gecko_ConstructStyleChildrenIterator(self.0, &mut iter); - return LayoutIterator(GeckoChildrenIterator::GeckoIterator(iter)); - } - } - - LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child())) - } - - fn before_pseudo_element(&self) -> Option { - self.before_or_after_pseudo(/* is_before = */ true) - } - - fn after_pseudo_element(&self) -> Option { - self.before_or_after_pseudo(/* is_before = */ false) - } - - fn marker_pseudo_element(&self) -> Option { - if !self.has_properties() { - return None; - } - - unsafe { - bindings::Gecko_GetMarkerPseudo(self.0) - .as_ref() - .map(GeckoElement) - } - } - - #[inline] - fn is_html_element(&self) -> bool { - self.namespace_id() == structs::kNameSpaceID_XHTML as i32 - } - - #[inline] - fn is_mathml_element(&self) -> bool { - self.namespace_id() == structs::kNameSpaceID_MathML as i32 - } - - #[inline] - fn is_svg_element(&self) -> bool { - self.namespace_id() == structs::kNameSpaceID_SVG as i32 - } - - #[inline] - fn is_xul_element(&self) -> bool { - self.namespace_id() == structs::root::kNameSpaceID_XUL as i32 - } - - #[inline] - fn local_name(&self) -> &WeakAtom { - unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) } - } - - #[inline] - fn namespace(&self) -> &WeakNamespace { - unsafe { WeakNamespace::new(namespace_id_to_atom(self.namespace_id())) } - } - - #[inline] - fn query_container_size(&self, display: &Display) -> Size2D> { - // If an element gets 'display: contents' and its nsIFrame has not been removed yet, - // Gecko_GetQueryContainerSize will not notice that it can't have size containment. - // Other cases like 'display: inline' will be handled once the new nsIFrame is created. - if display.is_contents() { - return Size2D::new(None, None); - } - - let mut width = -1; - let mut height = -1; - unsafe { - bindings::Gecko_GetQueryContainerSize(self.0, &mut width, &mut height); - } - Size2D::new( - if width >= 0 { Some(Au(width)) } else { None }, - if height >= 0 { Some(Au(height)) } else { None }, - ) - } - - /// Return the list of slotted nodes of this node. - #[inline] - fn slotted_nodes(&self) -> &[Self::ConcreteNode] { - if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() { - return &[]; - } - - let slot: &structs::HTMLSlotElement = unsafe { mem::transmute(self.0) }; - - if cfg!(debug_assertions) { - let base: &RawGeckoElement = &slot._base._base._base._base; - assert_eq!(base as *const _, self.0 as *const _, "Bad cast"); - } - - // FIXME(emilio): Workaround a bindgen bug on Android that causes - // mAssignedNodes to be at the wrong offset. See bug 1466406. - // - // Bug 1466580 tracks running the Android layout tests on automation. - // - // The actual bindgen bug still needs reduction. - let assigned_nodes: &[structs::RefPtr] = if !cfg!(target_os = "android") { - debug_assert_eq!( - unsafe { bindings::Gecko_GetAssignedNodes(self.0) }, - &slot.mAssignedNodes as *const _, - ); - - &*slot.mAssignedNodes - } else { - unsafe { &**bindings::Gecko_GetAssignedNodes(self.0) } - }; - - debug_assert_eq!( - mem::size_of::>(), - mem::size_of::(), - "Bad cast!" - ); - - unsafe { mem::transmute(assigned_nodes) } - } - - #[inline] - fn shadow_root(&self) -> Option> { - let slots = self.extended_slots()?; - unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) } - } - - #[inline] - fn containing_shadow(&self) -> Option> { - let slots = self.extended_slots()?; - unsafe { - slots - ._base - .mContainingShadow - .mRawPtr - .as_ref() - .map(GeckoShadowRoot) - } - } - - fn each_anonymous_content_child(&self, mut f: F) - where - F: FnMut(Self), - { - if !self.may_have_anonymous_children() { - return; - } - - let array: *mut structs::nsTArray<*mut nsIContent> = - unsafe { bindings::Gecko_GetAnonymousContentForElement(self.0) }; - - if array.is_null() { - return; - } - - for content in unsafe { &**array } { - let node = GeckoNode::from_content(unsafe { &**content }); - let element = match node.as_element() { - Some(e) => e, - None => continue, - }; - - f(element); - } - - unsafe { bindings::Gecko_DestroyAnonymousContentList(array) }; - } - - #[inline] - fn as_node(&self) -> Self::ConcreteNode { - unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) } - } - - fn owner_doc_matches_for_testing(&self, device: &Device) -> bool { - self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _ - } - - fn style_attribute(&self) -> Option>> { - if !self.may_have_style_attribute() { - return None; - } - - unsafe { - let declarations = Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref()?; - Some(ArcBorrow::from_ref(declarations)) - } - } - - fn unset_dirty_style_attribute(&self) { - if !self.may_have_style_attribute() { - return; - } - - unsafe { Gecko_UnsetDirtyStyleAttr(self.0) }; - } - - fn smil_override(&self) -> Option>> { - unsafe { - let slots = self.extended_slots()?; - - let declaration: &structs::DeclarationBlock = - slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?; - - let raw: &structs::StyleLockedDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?; - Some(ArcBorrow::from_ref(raw)) - } - } - - fn animation_rule( - &self, - _: &SharedStyleContext, - ) -> Option>> { - get_animation_rule(self, CascadeLevel::Animations) - } - - fn transition_rule( - &self, - _: &SharedStyleContext, - ) -> Option>> { - get_animation_rule(self, CascadeLevel::Transitions) - } - - #[inline] - fn state(&self) -> ElementState { - ElementState::from_bits_truncate(self.state_internal()) - } - - #[inline] - fn has_part_attr(&self) -> bool { - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasPart) - } - - #[inline] - fn exports_any_part(&self) -> bool { - snapshot_helpers::find_attr(self.non_mapped_attrs(), &atom!("exportparts")).is_some() - } - - // FIXME(emilio): we should probably just return a reference to the Atom. - #[inline] - fn id(&self) -> Option<&WeakAtom> { - if !self.has_id() { - return None; - } - - snapshot_helpers::get_id(self.non_mapped_attrs()) - } - - fn each_attr_name(&self, mut callback: F) - where - F: FnMut(&AtomIdent), - { - for attr in self.iter_attrs() { - unsafe { - AtomIdent::with(attr.mName.name(), |a| callback(a)) - } - } - } - - fn each_class(&self, callback: F) - where - F: FnMut(&AtomIdent), - { - let attr = match self.get_class_attr() { - Some(c) => c, - None => return, - }; - - snapshot_helpers::each_class_or_part(attr, callback) - } - - #[inline] - fn each_exported_part(&self, name: &AtomIdent, callback: F) - where - F: FnMut(&AtomIdent), - { - snapshot_helpers::each_exported_part(self.non_mapped_attrs(), name, callback) - } - - fn each_part(&self, callback: F) - where - F: FnMut(&AtomIdent), - { - let attr = match self.get_part_attr() { - Some(c) => c, - None => return, - }; - - snapshot_helpers::each_class_or_part(attr, callback) - } - - #[inline] - fn has_snapshot(&self) -> bool { - self.flags() & ELEMENT_HAS_SNAPSHOT != 0 - } - - #[inline] - fn handled_snapshot(&self) -> bool { - self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0 - } - - unsafe fn set_handled_snapshot(&self) { - debug_assert!(self.has_data()); - self.set_flags(ELEMENT_HANDLED_SNAPSHOT) - } - - #[inline] - fn has_dirty_descendants(&self) -> bool { - self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0 - } - - unsafe fn set_dirty_descendants(&self) { - debug_assert!(self.has_data()); - debug!("Setting dirty descendants: {:?}", self); - self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO) - } - - unsafe fn unset_dirty_descendants(&self) { - self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO) - } - - #[inline] - fn has_animation_only_dirty_descendants(&self) -> bool { - self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0 - } - - unsafe fn set_animation_only_dirty_descendants(&self) { - self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO) - } - - unsafe fn unset_animation_only_dirty_descendants(&self) { - self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO) - } - - unsafe fn clear_descendant_bits(&self) { - self.unset_flags( - ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO | - ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO | - NODE_DESCENDANTS_NEED_FRAMES, - ) - } - - fn is_visited_link(&self) -> bool { - self.state().intersects(ElementState::VISITED) - } - - /// We want to match rules from the same tree in all cases, except for native anonymous content - /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or - /// pseudo-elements). - #[inline] - fn matches_user_and_content_rules(&self) -> bool { - use crate::gecko_bindings::structs::{ - NODE_HAS_BEEN_IN_UA_WIDGET, NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE, - }; - let flags = self.flags(); - (flags & NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) == 0 || - (flags & NODE_HAS_BEEN_IN_UA_WIDGET) != 0 - } - - #[inline] - fn implemented_pseudo_element(&self) -> Option { - if self.matches_user_and_content_rules() { - return None; - } - - if !self.has_properties() { - return None; - } - - PseudoElement::from_pseudo_type(unsafe { bindings::Gecko_GetImplementedPseudo(self.0) }) - } - - #[inline] - fn store_children_to_process(&self, _: isize) { - // This is only used for bottom-up traversal, and is thus a no-op for Gecko. - } - - fn did_process_child(&self) -> isize { - panic!("Atomic child count not implemented in Gecko"); - } - - unsafe fn ensure_data(&self) -> AtomicRefMut { - if !self.has_data() { - debug!("Creating ElementData for {:?}", self); - let ptr = Box::into_raw(Box::new(AtomicRefCell::new(ElementData::default()))); - self.0.mServoData.set(ptr); - } - self.mutate_data().unwrap() - } - - unsafe fn clear_data(&self) { - let ptr = self.0.mServoData.get(); - self.unset_flags( - ELEMENT_HAS_SNAPSHOT | - ELEMENT_HANDLED_SNAPSHOT | - structs::Element_kAllServoDescendantBits | - NODE_NEEDS_FRAME, - ); - if !ptr.is_null() { - debug!("Dropping ElementData for {:?}", self); - let data = Box::from_raw(self.0.mServoData.get()); - self.0.mServoData.set(ptr::null_mut()); - - // Perform a mutable borrow of the data in debug builds. This - // serves as an assertion that there are no outstanding borrows - // when we destroy the data. - debug_assert!({ - let _ = data.borrow_mut(); - true - }); - } - } - - #[inline] - fn skip_item_display_fixup(&self) -> bool { - debug_assert!( - !self.is_pseudo_element(), - "Just don't call me if I'm a pseudo, you should know the answer already" - ); - self.is_root_of_native_anonymous_subtree() - } - - #[inline] - fn may_have_animations(&self) -> bool { - if let Some(pseudo) = self.implemented_pseudo_element() { - if pseudo.animations_stored_in_parent() { - // FIXME(emilio): When would the parent of a ::before / ::after - // pseudo-element be null? - return self.parent_element().map_or(false, |p| { - p.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations) - }); - } - } - self.as_node() - .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations) - } - - /// Process various tasks that are a result of animation-only restyle. - fn process_post_animation(&self, tasks: PostAnimationTasks) { - debug_assert!(!tasks.is_empty(), "Should be involved a task"); - - // If display style was changed from none to other, we need to resolve - // the descendants in the display:none subtree. Instead of resolving - // those styles in animation-only restyle, we defer it to a subsequent - // normal restyle. - if tasks.intersects(PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL) { - debug_assert!( - self.implemented_pseudo_element() - .map_or(true, |p| !p.is_before_or_after()), - "display property animation shouldn't run on pseudo elements \ - since it's only for SMIL" - ); - unsafe { - self.note_explicit_hints( - RestyleHint::restyle_subtree(), - nsChangeHint::nsChangeHint_Empty, - ); - } - } - } - - /// Update various animation-related state on a given (pseudo-)element as - /// results of normal restyle. - fn update_animations( - &self, - before_change_style: Option>, - tasks: UpdateAnimationsTasks, - ) { - // We have to update animations even if the element has no computed - // style since it means the element is in a display:none subtree, we - // should destroy all CSS animations in display:none subtree. - let computed_data = self.borrow_data(); - let computed_values = computed_data.as_ref().map(|d| d.styles.primary()); - let before_change_values = before_change_style - .as_ref() - .map_or(ptr::null(), |x| x.as_gecko_computed_style()); - let computed_values_opt = computed_values - .as_ref() - .map_or(ptr::null(), |x| x.as_gecko_computed_style()); - unsafe { - Gecko_UpdateAnimations( - self.0, - before_change_values, - computed_values_opt, - tasks.bits(), - ); - } - } - - #[inline] - fn has_animations(&self, _: &SharedStyleContext) -> bool { - self.has_any_animation() - } - - fn has_css_animations(&self, _: &SharedStyleContext, _: Option) -> bool { - self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) } - } - - fn has_css_transitions(&self, _: &SharedStyleContext, _: Option) -> bool { - self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) } - } - - // Detect if there are any changes that require us to update transitions. - // - // This is used as a more thoroughgoing check than the cheaper - // might_need_transitions_update check. - // - // The following logic shadows the logic used on the Gecko side - // (nsTransitionManager::DoUpdateTransitions) where we actually perform the - // update. - // - // https://drafts.csswg.org/css-transitions/#starting - fn needs_transitions_update( - &self, - before_change_style: &ComputedValues, - after_change_style: &ComputedValues, - ) -> bool { - use crate::properties::LonghandIdSet; - - let after_change_ui_style = after_change_style.get_ui(); - let existing_transitions = self.css_transitions_info(); - - if after_change_style.get_box().clone_display().is_none() { - // We need to cancel existing transitions. - return !existing_transitions.is_empty(); - } - - let mut transitions_to_keep = LonghandIdSet::new(); - for transition_property in after_change_style.transition_properties() { - let physical_longhand = transition_property - .longhand_id - .to_physical(after_change_style.writing_mode); - transitions_to_keep.insert(physical_longhand); - if self.needs_transitions_update_per_property( - physical_longhand, - after_change_ui_style - .transition_combined_duration_at(transition_property.index) - .seconds(), - before_change_style, - after_change_style, - &existing_transitions, - ) { - return true; - } - } - - // Check if we have to cancel the running transition because this is not - // a matching transition-property value. - existing_transitions - .keys() - .any(|property| !transitions_to_keep.contains(*property)) - } - - /// Whether there is an ElementData container. - #[inline] - fn has_data(&self) -> bool { - self.get_data().is_some() - } - - /// Immutably borrows the ElementData. - fn borrow_data(&self) -> Option> { - self.get_data().map(|x| x.borrow()) - } - - /// Mutably borrows the ElementData. - fn mutate_data(&self) -> Option> { - self.get_data().map(|x| x.borrow_mut()) - } - - #[inline] - fn lang_attr(&self) -> Option { - let ptr = unsafe { bindings::Gecko_LangValue(self.0) }; - if ptr.is_null() { - None - } else { - Some(AtomString(unsafe { Atom::from_addrefed(ptr) })) - } - } - - fn match_element_lang(&self, override_lang: Option>, value: &Lang) -> bool { - // Gecko supports :lang() from CSS Selectors 4, which accepts a list - // of language tags, and does BCP47-style range matching. - let override_lang_ptr = match override_lang { - Some(Some(ref atom)) => atom.as_ptr(), - _ => ptr::null_mut(), - }; - value.0.iter().any(|lang| unsafe { - Gecko_MatchLang( - self.0, - override_lang_ptr, - override_lang.is_some(), - lang.as_slice().as_ptr(), - ) - }) - } - - fn is_html_document_body_element(&self) -> bool { - if self.local_name() != &**local_name!("body") { - return false; - } - - if !self.is_html_element() { - return false; - } - - unsafe { bindings::Gecko_IsDocumentBody(self.0) } - } - - fn synthesize_presentational_hints_for_legacy_attributes( - &self, - visited_handling: VisitedHandlingMode, - hints: &mut V, - ) where - V: Push, - { - use crate::properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang; - use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor; - use crate::stylesheets::layer_rule::LayerOrder; - use crate::values::specified::{color::Color, font::XTextScale}; - lazy_static! { - static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = { - let global_style_data = &*GLOBAL_STYLE_DATA; - let pdb = PropertyDeclarationBlock::with_one( - PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())), - Importance::Normal, - ); - let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); - ApplicableDeclarationBlock::from_declarations( - arc, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - ) - }; - static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = { - let global_style_data = &*GLOBAL_STYLE_DATA; - let pdb = PropertyDeclarationBlock::with_one( - PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))), - Importance::Normal, - ); - let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); - ApplicableDeclarationBlock::from_declarations( - arc, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - ) - }; - static ref SVG_TEXT_DISABLE_SCALE_RULE: ApplicableDeclarationBlock = { - let global_style_data = &*GLOBAL_STYLE_DATA; - let pdb = PropertyDeclarationBlock::with_one( - PropertyDeclaration::XTextScale(XTextScale::None), - Importance::Normal, - ); - let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb)); - ApplicableDeclarationBlock::from_declarations( - arc, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - ) - }; - }; - - let ns = self.namespace_id(); - //
elements get a default MozCenterOrInherit which may get overridden - if ns == structs::kNameSpaceID_XHTML as i32 { - if self.local_name().as_ptr() == atom!("table").as_ptr() && - self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks - { - hints.push(TABLE_COLOR_RULE.clone()); - } - } - if ns == structs::kNameSpaceID_SVG as i32 { - if self.local_name().as_ptr() == atom!("text").as_ptr() { - hints.push(SVG_TEXT_DISABLE_SCALE_RULE.clone()); - } - } - let declarations = - unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() }; - if let Some(decl) = declarations { - hints.push(ApplicableDeclarationBlock::from_declarations( - unsafe { Arc::from_raw_addrefed(decl) }, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - )); - } - let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() }; - if let Some(decl) = declarations { - hints.push(ApplicableDeclarationBlock::from_declarations( - unsafe { Arc::from_raw_addrefed(decl) }, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - )); - } - - // Support for link, vlink, and alink presentation hints on - if self.is_link() { - // Unvisited vs. visited styles are computed up-front based on the - // visited mode (not the element's actual state). - let declarations = match visited_handling { - VisitedHandlingMode::AllLinksVisitedAndUnvisited => { - unreachable!( - "We should never try to selector match with \ - AllLinksVisitedAndUnvisited" - ); - }, - VisitedHandlingMode::AllLinksUnvisited => unsafe { - Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref() - }, - VisitedHandlingMode::RelevantLinkVisited => unsafe { - Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref() - }, - }; - if let Some(decl) = declarations { - hints.push(ApplicableDeclarationBlock::from_declarations( - unsafe { Arc::from_raw_addrefed(decl) }, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - )); - } - - let active = self - .state() - .intersects(NonTSPseudoClass::Active.state_flag()); - if active { - let declarations = - unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() }; - if let Some(decl) = declarations { - hints.push(ApplicableDeclarationBlock::from_declarations( - unsafe { Arc::from_raw_addrefed(decl) }, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - )); - } - } - } - - // xml:lang has precedence over lang, which can be - // set by Gecko_GetHTMLPresentationAttrDeclarationBlock - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language - let ptr = unsafe { bindings::Gecko_GetXMLLangValue(self.0) }; - if !ptr.is_null() { - let global_style_data = &*GLOBAL_STYLE_DATA; - - let pdb = PropertyDeclarationBlock::with_one( - PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })), - Importance::Normal, - ); - let arc = Arc::new(global_style_data.shared_lock.wrap(pdb)); - hints.push(ApplicableDeclarationBlock::from_declarations( - arc, - ServoCascadeLevel::PresHints, - LayerOrder::root(), - )) - } - // MathML's default lang has precedence over both `lang` and `xml:lang` - if ns == structs::kNameSpaceID_MathML as i32 { - if self.local_name().as_ptr() == atom!("math").as_ptr() { - hints.push(MATHML_LANG_RULE.clone()); - } - } - } -} - -impl<'le> PartialEq for GeckoElement<'le> { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0 as *const _ == other.0 as *const _ - } -} - -impl<'le> Eq for GeckoElement<'le> {} - -impl<'le> Hash for GeckoElement<'le> { - #[inline] - fn hash(&self, state: &mut H) { - (self.0 as *const RawGeckoElement).hash(state); - } -} - -impl<'le> ::selectors::Element for GeckoElement<'le> { - type Impl = SelectorImpl; - - #[inline] - fn opaque(&self) -> OpaqueElement { - OpaqueElement::new(self.0) - } - - #[inline] - fn parent_element(&self) -> Option { - let parent_node = self.as_node().parent_node(); - parent_node.and_then(|n| n.as_element()) - } - - #[inline] - fn parent_node_is_shadow_root(&self) -> bool { - self.as_node() - .parent_node() - .map_or(false, |p| p.is_shadow_root()) - } - - #[inline] - fn containing_shadow_host(&self) -> Option { - let shadow = self.containing_shadow()?; - Some(shadow.host()) - } - - #[inline] - fn is_pseudo_element(&self) -> bool { - self.implemented_pseudo_element().is_some() - } - - #[inline] - fn pseudo_element_originating_element(&self) -> Option { - debug_assert!(self.is_pseudo_element()); - debug_assert!(!self.matches_user_and_content_rules()); - let mut current = *self; - loop { - if current.is_root_of_native_anonymous_subtree() { - return current.traversal_parent(); - } - - current = current.traversal_parent()?; - } - } - - #[inline] - fn assigned_slot(&self) -> Option { - let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr; - - unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base._base)) } - } - - #[inline] - fn prev_sibling_element(&self) -> Option { - let mut sibling = self.as_node().prev_sibling(); - while let Some(sibling_node) = sibling { - if let Some(el) = sibling_node.as_element() { - return Some(el); - } - sibling = sibling_node.prev_sibling(); - } - None - } - - #[inline] - fn next_sibling_element(&self) -> Option { - let mut sibling = self.as_node().next_sibling(); - while let Some(sibling_node) = sibling { - if let Some(el) = sibling_node.as_element() { - return Some(el); - } - sibling = sibling_node.next_sibling(); - } - None - } - - #[inline] - fn first_element_child(&self) -> Option { - let mut child = self.as_node().first_child(); - while let Some(child_node) = child { - if let Some(el) = child_node.as_element() { - return Some(el); - } - child = child_node.next_sibling(); - } - None - } - - fn apply_selector_flags(&self, flags: ElementSelectorFlags) { - // Handle flags that apply to the element. - let self_flags = flags.for_self(); - if !self_flags.is_empty() { - self.set_flags(selector_flags_to_node_flags(flags)) - } - - // Handle flags that apply to the parent. - let parent_flags = flags.for_parent(); - if !parent_flags.is_empty() { - if let Some(p) = self.as_node().parent_node() { - if p.is_element() || p.is_shadow_root() { - p.set_flags(selector_flags_to_node_flags(parent_flags)); - } - } - } - } - - fn has_attr_in_no_namespace(&self, local_name: &LocalName) -> bool { - for attr in self.iter_attrs() { - if attr.mName.mBits == local_name.as_ptr() as usize { - return true; - } - } - false - } - - fn attr_matches( - &self, - ns: &NamespaceConstraint<&Namespace>, - local_name: &LocalName, - operation: &AttrSelectorOperation<&AttrValue>, - ) -> bool { - if self.attrs().is_none() { - return false; - } - snapshot_helpers::attr_matches(self.iter_attrs(), ns, local_name, operation) - } - - #[inline] - fn is_root(&self) -> bool { - if self - .as_node() - .get_bool_flag(nsINode_BooleanFlag::ParentIsContent) - { - return false; - } - - if !self.as_node().is_in_document() { - return false; - } - - debug_assert!(self - .as_node() - .parent_node() - .map_or(false, |p| p.is_document())); - // XXX this should always return true at this point, shouldn't it? - unsafe { bindings::Gecko_IsRootElement(self.0) } - } - - fn is_empty(&self) -> bool { - !self - .as_node() - .dom_children() - .any(|child| unsafe { Gecko_IsSignificantChild(child.0, true) }) - } - - #[inline] - fn has_local_name(&self, name: &WeakAtom) -> bool { - self.local_name() == name - } - - #[inline] - fn has_namespace(&self, ns: &WeakNamespace) -> bool { - self.namespace() == ns - } - - #[inline] - fn is_same_type(&self, other: &Self) -> bool { - self.local_name() == other.local_name() && self.namespace() == other.namespace() - } - - fn match_non_ts_pseudo_class( - &self, - pseudo_class: &NonTSPseudoClass, - context: &mut MatchingContext, - ) -> bool { - use selectors::matching::*; - match *pseudo_class { - NonTSPseudoClass::Autofill | - NonTSPseudoClass::Defined | - NonTSPseudoClass::Focus | - NonTSPseudoClass::Enabled | - NonTSPseudoClass::Disabled | - NonTSPseudoClass::Checked | - NonTSPseudoClass::Fullscreen | - NonTSPseudoClass::Indeterminate | - NonTSPseudoClass::MozInert | - NonTSPseudoClass::PopoverOpen | - NonTSPseudoClass::PlaceholderShown | - NonTSPseudoClass::Target | - NonTSPseudoClass::Valid | - NonTSPseudoClass::Invalid | - NonTSPseudoClass::MozBroken | - NonTSPseudoClass::MozLoading | - NonTSPseudoClass::Required | - NonTSPseudoClass::Optional | - NonTSPseudoClass::ReadOnly | - NonTSPseudoClass::ReadWrite | - NonTSPseudoClass::FocusWithin | - NonTSPseudoClass::FocusVisible | - NonTSPseudoClass::MozDragOver | - NonTSPseudoClass::MozDevtoolsHighlighted | - NonTSPseudoClass::MozStyleeditorTransitioning | - NonTSPseudoClass::MozMathIncrementScriptLevel | - NonTSPseudoClass::InRange | - NonTSPseudoClass::OutOfRange | - NonTSPseudoClass::Default | - NonTSPseudoClass::UserValid | - NonTSPseudoClass::UserInvalid | - NonTSPseudoClass::MozMeterOptimum | - NonTSPseudoClass::MozMeterSubOptimum | - NonTSPseudoClass::MozMeterSubSubOptimum | - NonTSPseudoClass::MozHasDirAttr | - NonTSPseudoClass::MozDirAttrLTR | - NonTSPseudoClass::MozDirAttrRTL | - NonTSPseudoClass::MozDirAttrLikeAuto | - NonTSPseudoClass::Modal | - NonTSPseudoClass::MozTopmostModal | - NonTSPseudoClass::Active | - NonTSPseudoClass::Hover | - NonTSPseudoClass::MozAutofillPreview | - NonTSPseudoClass::MozRevealed | - NonTSPseudoClass::MozValueEmpty | - NonTSPseudoClass::Dir(..) => self.state().intersects(pseudo_class.state_flag()), - NonTSPseudoClass::AnyLink => self.is_link(), - NonTSPseudoClass::Link => { - self.is_link() && context.visited_handling().matches_unvisited() - }, - NonTSPseudoClass::Visited => { - self.is_link() && context.visited_handling().matches_visited() - }, - NonTSPseudoClass::MozFirstNode => { - if context.needs_selector_flags() { - self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR); - } - let mut elem = self.as_node(); - while let Some(prev) = elem.prev_sibling() { - if prev.contains_non_whitespace_content() { - return false; - } - elem = prev; - } - true - }, - NonTSPseudoClass::MozLastNode => { - if context.needs_selector_flags() { - self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR); - } - let mut elem = self.as_node(); - while let Some(next) = elem.next_sibling() { - if next.contains_non_whitespace_content() { - return false; - } - elem = next; - } - true - }, - NonTSPseudoClass::MozOnlyWhitespace => { - if context.needs_selector_flags() { - self.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR); - } - if self - .as_node() - .dom_children() - .any(|c| c.contains_non_whitespace_content()) - { - return false; - } - true - }, - NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(), - NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(), - NonTSPseudoClass::MozTableBorderNonzero => unsafe { - bindings::Gecko_IsTableBorderNonzero(self.0) - }, - NonTSPseudoClass::MozBrowserFrame => unsafe { bindings::Gecko_IsBrowserFrame(self.0) }, - NonTSPseudoClass::MozSelectListBox => unsafe { - bindings::Gecko_IsSelectListBox(self.0) - }, - NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(), - - NonTSPseudoClass::MozLWTheme | - NonTSPseudoClass::MozLocaleDir(..) | - NonTSPseudoClass::MozWindowInactive => { - let state_bit = pseudo_class.document_state_flag(); - if state_bit.is_empty() { - debug_assert!( - matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)), - "Only moz-locale-dir should ever return an empty state" - ); - return false; - } - if context - .extra_data - .invalidation_data - .document_state - .intersects(state_bit) - { - return !context.in_negation(); - } - self.document_state().contains(state_bit) - }, - NonTSPseudoClass::MozPlaceholder => false, - NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg), - } - } - - fn match_pseudo_element( - &self, - pseudo_element: &PseudoElement, - _context: &mut MatchingContext, - ) -> bool { - // TODO(emilio): I believe we could assert we are a pseudo-element and - // match the proper pseudo-element, given how we rulehash the stuff - // based on the pseudo. - match self.implemented_pseudo_element() { - Some(ref pseudo) => *pseudo == *pseudo_element, - None => false, - } - } - - #[inline] - fn is_link(&self) -> bool { - self.state().intersects(ElementState::VISITED_OR_UNVISITED) - } - - #[inline] - fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - if !self.has_id() { - return false; - } - - let element_id = match snapshot_helpers::get_id(self.non_mapped_attrs()) { - Some(id) => id, - None => return false, - }; - - case_sensitivity.eq_atom(element_id, id) - } - - #[inline] - fn is_part(&self, name: &AtomIdent) -> bool { - let attr = match self.get_part_attr() { - Some(c) => c, - None => return false, - }; - - snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) - } - - #[inline] - fn imported_part(&self, name: &AtomIdent) -> Option { - snapshot_helpers::imported_part(self.non_mapped_attrs(), name) - } - - #[inline(always)] - fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - let attr = match self.get_class_attr() { - Some(c) => c, - None => return false, - }; - - snapshot_helpers::has_class_or_part(name, case_sensitivity, attr) - } - - #[inline] - fn is_html_element_in_html_document(&self) -> bool { - self.is_html_element() && self.as_node().owner_doc().is_html_document() - } - - #[inline] - fn is_html_slot_element(&self) -> bool { - self.is_html_element() && self.local_name().as_ptr() == local_name!("slot").as_ptr() - } - - #[inline] - fn ignores_nth_child_selectors(&self) -> bool { - self.is_root_of_native_anonymous_subtree() - } -} diff --git a/components/style/gecko_bindings/mod.rs b/components/style/gecko_bindings/mod.rs deleted file mode 100644 index f0b0adc7ecb..00000000000 --- a/components/style/gecko_bindings/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko's C++ bindings, along with some rust helpers to ease its use. - -// FIXME: We allow `improper_ctypes` (for now), because the lint doesn't allow -// foreign structs to have `PhantomData`. We should remove this once the lint -// ignores this case. - -#[allow( - dead_code, - improper_ctypes, - non_camel_case_types, - non_snake_case, - non_upper_case_globals, - missing_docs -)] -// TODO: Remove this when updating bindgen, see -// https://github.com/rust-lang/rust-bindgen/issues/1651 -#[cfg_attr(test, allow(deref_nullptr))] -pub mod structs { - include!(concat!(env!("OUT_DIR"), "/gecko/structs.rs")); -} - -pub use self::structs as bindings; - -pub mod sugar; diff --git a/components/style/gecko_bindings/sugar/mod.rs b/components/style/gecko_bindings/sugar/mod.rs deleted file mode 100644 index 00faf63ba66..00000000000 --- a/components/style/gecko_bindings/sugar/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Rust sugar and convenience methods for Gecko types. - -mod ns_com_ptr; -mod ns_compatibility; -mod ns_style_auto_array; -mod ns_t_array; -pub mod origin_flags; -pub mod ownership; -pub mod refptr; diff --git a/components/style/gecko_bindings/sugar/ns_com_ptr.rs b/components/style/gecko_bindings/sugar/ns_com_ptr.rs deleted file mode 100644 index 1c54541bd80..00000000000 --- a/components/style/gecko_bindings/sugar/ns_com_ptr.rs +++ /dev/null @@ -1,15 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Little helpers for `nsCOMPtr`. - -use crate::gecko_bindings::structs::nsCOMPtr; - -impl nsCOMPtr { - /// Get this pointer as a raw pointer. - #[inline] - pub fn raw(&self) -> *mut T { - self.mRawPtr - } -} diff --git a/components/style/gecko_bindings/sugar/ns_compatibility.rs b/components/style/gecko_bindings/sugar/ns_compatibility.rs deleted file mode 100644 index f4b81e9f790..00000000000 --- a/components/style/gecko_bindings/sugar/ns_compatibility.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Little helper for `nsCompatibility`. - -use crate::context::QuirksMode; -use crate::gecko_bindings::structs::nsCompatibility; - -impl From for QuirksMode { - #[inline] - fn from(mode: nsCompatibility) -> QuirksMode { - match mode { - nsCompatibility::eCompatibility_FullStandards => QuirksMode::NoQuirks, - nsCompatibility::eCompatibility_AlmostStandards => QuirksMode::LimitedQuirks, - nsCompatibility::eCompatibility_NavQuirks => QuirksMode::Quirks, - } - } -} diff --git a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs b/components/style/gecko_bindings/sugar/ns_style_auto_array.rs deleted file mode 100644 index b5772a6c77a..00000000000 --- a/components/style/gecko_bindings/sugar/ns_style_auto_array.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Rust helpers for Gecko's `nsStyleAutoArray`. - -use crate::gecko_bindings::bindings::Gecko_EnsureStyleAnimationArrayLength; -use crate::gecko_bindings::bindings::Gecko_EnsureStyleScrollTimelineArrayLength; -use crate::gecko_bindings::bindings::Gecko_EnsureStyleTransitionArrayLength; -use crate::gecko_bindings::bindings::Gecko_EnsureStyleViewTimelineArrayLength; -use crate::gecko_bindings::structs::nsStyleAutoArray; -use crate::gecko_bindings::structs::{StyleAnimation, StyleTransition}; -use crate::gecko_bindings::structs::{StyleScrollTimeline, StyleViewTimeline}; -use std::iter::{once, Chain, IntoIterator, Once}; -use std::ops::{Index, IndexMut}; -use std::slice::{Iter, IterMut}; - -impl Index for nsStyleAutoArray { - type Output = T; - fn index(&self, index: usize) -> &T { - match index { - 0 => &self.mFirstElement, - _ => &self.mOtherElements[index - 1], - } - } -} - -impl IndexMut for nsStyleAutoArray { - fn index_mut(&mut self, index: usize) -> &mut T { - match index { - 0 => &mut self.mFirstElement, - _ => &mut self.mOtherElements[index - 1], - } - } -} - -impl nsStyleAutoArray { - /// Mutably iterate over the array elements. - pub fn iter_mut(&mut self) -> Chain, IterMut> { - once(&mut self.mFirstElement).chain(self.mOtherElements.iter_mut()) - } - - /// Iterate over the array elements. - pub fn iter(&self) -> Chain, Iter> { - once(&self.mFirstElement).chain(self.mOtherElements.iter()) - } - - /// Returns the length of the array. - /// - /// Note that often structs containing autoarrays will have additional - /// member fields that contain the length, which must be kept in sync. - pub fn len(&self) -> usize { - 1 + self.mOtherElements.len() - } -} - -impl nsStyleAutoArray { - /// Ensures that the array has length at least the given length. - pub fn ensure_len(&mut self, len: usize) { - unsafe { - Gecko_EnsureStyleAnimationArrayLength( - self as *mut nsStyleAutoArray as *mut _, - len, - ); - } - } -} - -impl nsStyleAutoArray { - /// Ensures that the array has length at least the given length. - pub fn ensure_len(&mut self, len: usize) { - unsafe { - Gecko_EnsureStyleTransitionArrayLength( - self as *mut nsStyleAutoArray as *mut _, - len, - ); - } - } -} - -impl nsStyleAutoArray { - /// Ensures that the array has length at least the given length. - pub fn ensure_len(&mut self, len: usize) { - unsafe { - Gecko_EnsureStyleViewTimelineArrayLength( - self as *mut nsStyleAutoArray as *mut _, - len, - ); - } - } -} - -impl nsStyleAutoArray { - /// Ensures that the array has length at least the given length. - pub fn ensure_len(&mut self, len: usize) { - unsafe { - Gecko_EnsureStyleScrollTimelineArrayLength( - self as *mut nsStyleAutoArray as *mut _, - len, - ); - } - } -} - -impl<'a, T> IntoIterator for &'a mut nsStyleAutoArray { - type Item = &'a mut T; - type IntoIter = Chain, IterMut<'a, T>>; - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} diff --git a/components/style/gecko_bindings/sugar/ns_t_array.rs b/components/style/gecko_bindings/sugar/ns_t_array.rs deleted file mode 100644 index d10ed420dd8..00000000000 --- a/components/style/gecko_bindings/sugar/ns_t_array.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Rust helpers for Gecko's nsTArray. - -use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::{nsTArray, nsTArrayHeader, CopyableTArray}; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::slice; - -impl Deref for nsTArray { - type Target = [T]; - - #[inline] - fn deref<'a>(&'a self) -> &'a [T] { - unsafe { slice::from_raw_parts(self.slice_begin(), self.header().mLength as usize) } - } -} - -impl DerefMut for nsTArray { - fn deref_mut<'a>(&'a mut self) -> &'a mut [T] { - unsafe { slice::from_raw_parts_mut(self.slice_begin(), self.header().mLength as usize) } - } -} - -impl nsTArray { - #[inline] - fn header<'a>(&'a self) -> &'a nsTArrayHeader { - debug_assert!(!self.mBuffer.is_null()); - unsafe { mem::transmute(self.mBuffer) } - } - - // unsafe, since header may be in shared static or something - unsafe fn header_mut<'a>(&'a mut self) -> &'a mut nsTArrayHeader { - debug_assert!(!self.mBuffer.is_null()); - - mem::transmute(self.mBuffer) - } - - #[inline] - unsafe fn slice_begin(&self) -> *mut T { - debug_assert!(!self.mBuffer.is_null()); - (self.mBuffer as *const nsTArrayHeader).offset(1) as *mut _ - } - - /// Ensures the array has enough capacity at least to hold `cap` elements. - /// - /// NOTE: This doesn't call the constructor on the values! - pub fn ensure_capacity(&mut self, cap: usize) { - if cap >= self.len() { - unsafe { - bindings::Gecko_EnsureTArrayCapacity( - self as *mut nsTArray as *mut _, - cap, - mem::size_of::(), - ) - } - } - } - - /// Clears the array storage without calling the destructor on the values. - #[inline] - pub unsafe fn clear(&mut self) { - if self.len() != 0 { - bindings::Gecko_ClearPODTArray( - self as *mut nsTArray as *mut _, - mem::size_of::(), - mem::align_of::(), - ); - } - } - - /// Clears a POD array. This is safe since copy types are memcopyable. - #[inline] - pub fn clear_pod(&mut self) - where - T: Copy, - { - unsafe { self.clear() } - } - - /// Resize and set the length of the array to `len`. - /// - /// unsafe because this may leave the array with uninitialized elements. - /// - /// This will not call constructors. If you need that, either manually add - /// bindings or run the typed `EnsureCapacity` call on the gecko side. - pub unsafe fn set_len(&mut self, len: u32) { - // this can leak - debug_assert!(len >= self.len() as u32); - if self.len() == len as usize { - return; - } - self.ensure_capacity(len as usize); - self.header_mut().mLength = len; - } - - /// Resizes an array containing only POD elements - /// - /// unsafe because this may leave the array with uninitialized elements. - /// - /// This will not leak since it only works on POD types (and thus doesn't assert) - pub unsafe fn set_len_pod(&mut self, len: u32) - where - T: Copy, - { - if self.len() == len as usize { - return; - } - self.ensure_capacity(len as usize); - let header = self.header_mut(); - header.mLength = len; - } - - /// Collects the given iterator into this array. - /// - /// Not unsafe because we won't leave uninitialized elements in the array. - pub fn assign_from_iter_pod(&mut self, iter: I) - where - T: Copy, - I: ExactSizeIterator + Iterator, - { - debug_assert!(iter.len() <= 0xFFFFFFFF); - unsafe { - self.set_len_pod(iter.len() as u32); - } - self.iter_mut().zip(iter).for_each(|(r, v)| *r = v); - } -} - -impl Deref for CopyableTArray { - type Target = nsTArray; - fn deref(&self) -> &Self::Target { - &self._base - } -} - -impl DerefMut for CopyableTArray { - fn deref_mut(&mut self) -> &mut nsTArray { - &mut self._base - } -} diff --git a/components/style/gecko_bindings/sugar/origin_flags.rs b/components/style/gecko_bindings/sugar/origin_flags.rs deleted file mode 100644 index e0f0981c5d8..00000000000 --- a/components/style/gecko_bindings/sugar/origin_flags.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Helper to iterate over `OriginFlags` bits. - -use crate::gecko_bindings::structs::OriginFlags; -use crate::stylesheets::OriginSet; - -/// Checks that the values for OriginFlags are the ones we expect. -pub fn assert_flags_match() { - use crate::stylesheets::origin::*; - debug_assert_eq!( - OriginFlags::UserAgent.0, - OriginSet::ORIGIN_USER_AGENT.bits() - ); - debug_assert_eq!(OriginFlags::Author.0, OriginSet::ORIGIN_AUTHOR.bits()); - debug_assert_eq!(OriginFlags::User.0, OriginSet::ORIGIN_USER.bits()); -} - -impl From for OriginSet { - fn from(flags: OriginFlags) -> Self { - Self::from_bits_truncate(flags.0) - } -} - -impl From for OriginFlags { - fn from(set: OriginSet) -> Self { - OriginFlags(set.bits()) - } -} diff --git a/components/style/gecko_bindings/sugar/ownership.rs b/components/style/gecko_bindings/sugar/ownership.rs deleted file mode 100644 index 31b512cf1e8..00000000000 --- a/components/style/gecko_bindings/sugar/ownership.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Helpers for different FFI pointer kinds that Gecko's FFI layer uses. - -use crate::gecko_bindings::structs::root::mozilla::detail::CopyablePtr; -use servo_arc::Arc; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::ptr; - -/// Gecko-FFI-safe Arc (T is an ArcInner). -/// -/// This can be null. -/// -/// Leaks on drop. Please don't drop this. -#[repr(C)] -pub struct Strong { - ptr: *const GeckoType, - _marker: PhantomData, -} - -impl From> for Strong { - fn from(arc: Arc) -> Self { - Self { - ptr: Arc::into_raw(arc), - _marker: PhantomData, - } - } -} - -impl Strong { - #[inline] - /// Returns whether this reference is null. - pub fn is_null(&self) -> bool { - self.ptr.is_null() - } - - #[inline] - /// Returns a null pointer - pub fn null() -> Self { - Self { - ptr: ptr::null(), - _marker: PhantomData, - } - } -} - -impl Deref for CopyablePtr { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.mPtr - } -} - -impl DerefMut for CopyablePtr { - fn deref_mut<'a>(&'a mut self) -> &'a mut T { - &mut self.mPtr - } -} diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs deleted file mode 100644 index c4a0479a077..00000000000 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ /dev/null @@ -1,289 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A rust helper to ease the use of Gecko's refcounted types. - -use crate::gecko_bindings::{bindings, structs}; -use crate::Atom; -use servo_arc::Arc; -use std::fmt::Write; -use std::marker::PhantomData; -use std::ops::Deref; -use std::{fmt, mem, ptr}; - -/// Trait for all objects that have Addref() and Release -/// methods and can be placed inside RefPtr -pub unsafe trait RefCounted { - /// Bump the reference count. - fn addref(&self); - /// Decrease the reference count. - unsafe fn release(&self); -} - -/// Trait for types which can be shared across threads in RefPtr. -pub unsafe trait ThreadSafeRefCounted: RefCounted {} - -/// A custom RefPtr implementation to take into account Drop semantics and -/// a bit less-painful memory management. -pub struct RefPtr { - ptr: *mut T, - _marker: PhantomData, -} - -impl fmt::Debug for RefPtr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("RefPtr { ")?; - self.ptr.fmt(f)?; - f.write_char('}') - } -} - -impl RefPtr { - /// Create a new RefPtr from an already addrefed pointer obtained from FFI. - /// - /// The pointer must be valid, non-null and have been addrefed. - pub unsafe fn from_addrefed(ptr: *mut T) -> Self { - debug_assert!(!ptr.is_null()); - RefPtr { - ptr, - _marker: PhantomData, - } - } - - /// Returns whether the current pointer is null. - pub fn is_null(&self) -> bool { - self.ptr.is_null() - } - - /// Returns a null pointer. - pub fn null() -> Self { - Self { - ptr: ptr::null_mut(), - _marker: PhantomData, - } - } - - /// Create a new RefPtr from a pointer obtained from FFI. - /// - /// This method calls addref() internally - pub unsafe fn new(ptr: *mut T) -> Self { - let ret = RefPtr { - ptr, - _marker: PhantomData, - }; - ret.addref(); - ret - } - - /// Produces an FFI-compatible RefPtr that can be stored in style structs. - /// - /// structs::RefPtr does not have a destructor, so this may leak - pub fn forget(self) -> structs::RefPtr { - let ret = structs::RefPtr { - mRawPtr: self.ptr, - _phantom_0: PhantomData, - }; - mem::forget(self); - ret - } - - /// Returns the raw inner pointer to be fed back into FFI. - pub fn get(&self) -> *mut T { - self.ptr - } - - /// Addref the inner data, obviously leaky on its own. - pub fn addref(&self) { - if !self.ptr.is_null() { - unsafe { - (*self.ptr).addref(); - } - } - } - - /// Release the inner data. - /// - /// Call only when the data actually needs releasing. - pub unsafe fn release(&self) { - if !self.ptr.is_null() { - (*self.ptr).release(); - } - } -} - -impl Deref for RefPtr { - type Target = T; - fn deref(&self) -> &T { - debug_assert!(!self.ptr.is_null()); - unsafe { &*self.ptr } - } -} - -impl structs::RefPtr { - /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount. - /// - /// Must be called on a valid, non-null structs::RefPtr. - pub unsafe fn to_safe(&self) -> RefPtr { - let r = RefPtr { - ptr: self.mRawPtr, - _marker: PhantomData, - }; - r.addref(); - r - } - /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping - /// the refcount). - pub unsafe fn into_safe(self) -> RefPtr { - debug_assert!(!self.mRawPtr.is_null()); - RefPtr { - ptr: self.mRawPtr, - _marker: PhantomData, - } - } - - /// Replace a structs::RefPtr with a different one, appropriately - /// addref/releasing. - /// - /// Both `self` and `other` must be valid, but can be null. - /// - /// Safe when called on an aliased pointer because the refcount in that case - /// needs to be at least two. - pub unsafe fn set(&mut self, other: &Self) { - self.clear(); - if !other.mRawPtr.is_null() { - *self = other.to_safe().forget(); - } - } - - /// Clear an instance of the structs::RefPtr, by releasing - /// it and setting its contents to null. - /// - /// `self` must be valid, but can be null. - pub unsafe fn clear(&mut self) { - if !self.mRawPtr.is_null() { - (*self.mRawPtr).release(); - self.mRawPtr = ptr::null_mut(); - } - } - - /// Replace a `structs::RefPtr` with a `RefPtr`, - /// consuming the `RefPtr`, and releasing the old - /// value in `self` if necessary. - /// - /// `self` must be valid, possibly null. - pub fn set_move(&mut self, other: RefPtr) { - if !self.mRawPtr.is_null() { - unsafe { - (*self.mRawPtr).release(); - } - } - *self = other.forget(); - } -} - -impl structs::RefPtr { - /// Returns a new, null refptr. - pub fn null() -> Self { - Self { - mRawPtr: ptr::null_mut(), - _phantom_0: PhantomData, - } - } - - /// Create a new RefPtr from an arc. - pub fn from_arc(s: Arc) -> Self { - Self { - mRawPtr: Arc::into_raw(s) as *mut _, - _phantom_0: PhantomData, - } - } - - /// Sets the contents to an Arc. - pub fn set_arc(&mut self, other: Arc) { - unsafe { - if !self.mRawPtr.is_null() { - let _ = Arc::from_raw(self.mRawPtr); - } - self.mRawPtr = Arc::into_raw(other) as *mut _; - } - } -} - -impl Drop for RefPtr { - fn drop(&mut self) { - unsafe { self.release() } - } -} - -impl Clone for RefPtr { - fn clone(&self) -> Self { - self.addref(); - RefPtr { - ptr: self.ptr, - _marker: PhantomData, - } - } -} - -impl PartialEq for RefPtr { - fn eq(&self, other: &Self) -> bool { - self.ptr == other.ptr - } -} - -unsafe impl Send for RefPtr {} -unsafe impl Sync for RefPtr {} - -macro_rules! impl_refcount { - ($t:ty, $addref:path, $release:path) => { - unsafe impl RefCounted for $t { - #[inline] - fn addref(&self) { - unsafe { $addref(self as *const _ as *mut _) } - } - - #[inline] - unsafe fn release(&self) { - $release(self as *const _ as *mut _) - } - } - }; -} - -// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING. -// -// Gets you a free RefCounted impl implemented via FFI. -macro_rules! impl_threadsafe_refcount { - ($t:ty, $addref:path, $release:path) => { - impl_refcount!($t, $addref, $release); - unsafe impl ThreadSafeRefCounted for $t {} - }; -} - -impl_threadsafe_refcount!( - structs::mozilla::URLExtraData, - bindings::Gecko_AddRefURLExtraDataArbitraryThread, - bindings::Gecko_ReleaseURLExtraDataArbitraryThread -); -impl_threadsafe_refcount!( - structs::nsIURI, - bindings::Gecko_AddRefnsIURIArbitraryThread, - bindings::Gecko_ReleasensIURIArbitraryThread -); -impl_threadsafe_refcount!( - structs::SheetLoadDataHolder, - bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread, - bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread -); - -#[inline] -unsafe fn addref_atom(atom: *mut structs::nsAtom) { - mem::forget(Atom::from_raw(atom)); -} - -#[inline] -unsafe fn release_atom(atom: *mut structs::nsAtom) { - let _ = Atom::from_addrefed(atom); -} -impl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom); diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs deleted file mode 100644 index cb040390cfe..00000000000 --- a/components/style/gecko_string_cache/mod.rs +++ /dev/null @@ -1,532 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#![allow(unsafe_code)] -// This is needed for the constants in atom_macro.rs, because we have some -// atoms whose names differ only by case, e.g. datetime and dateTime. -#![allow(non_upper_case_globals)] - -//! A drop-in replacement for string_cache, but backed by Gecko `nsAtom`s. - -use crate::gecko_bindings::bindings::Gecko_AddRefAtom; -use crate::gecko_bindings::bindings::Gecko_Atomize; -use crate::gecko_bindings::bindings::Gecko_Atomize16; -use crate::gecko_bindings::bindings::Gecko_ReleaseAtom; -use crate::gecko_bindings::structs::root::mozilla::detail::gGkAtoms; -use crate::gecko_bindings::structs::root::mozilla::detail::kGkAtomsArrayOffset; -use crate::gecko_bindings::structs::root::mozilla::detail::GkAtoms_Atoms_AtomsCount; -use crate::gecko_bindings::structs::{nsAtom, nsDynamicAtom, nsStaticAtom}; -use nsstring::{nsAString, nsStr}; -use precomputed_hash::PrecomputedHash; -use std::borrow::{Borrow, Cow}; -use std::char::{self, DecodeUtf16}; -use std::fmt::{self, Write}; -use std::hash::{Hash, Hasher}; -use std::iter::Cloned; -use std::mem::{self, ManuallyDrop}; -use std::num::NonZeroUsize; -use std::ops::Deref; -use std::{slice, str}; -use style_traits::SpecifiedValueInfo; -use to_shmem::{self, SharedMemoryBuilder, ToShmem}; - -#[macro_use] -#[allow(improper_ctypes, non_camel_case_types, missing_docs)] -pub mod atom_macro { - include!(concat!(env!("OUT_DIR"), "/gecko/atom_macro.rs")); -} - -#[macro_use] -pub mod namespace; - -pub use self::namespace::{Namespace, WeakNamespace}; - -/// A handle to a Gecko atom. This is a type that can represent either: -/// -/// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case -/// the `usize` just holds the pointer value. -/// -/// * A byte offset from `gGkAtoms` to the `nsStaticAtom` object (shifted to -/// the left one bit, and with the lower bit set to `1` to differentiate it -/// from the above), so `(offset << 1 | 1)`. -/// -#[derive(Eq, PartialEq)] -#[repr(C)] -pub struct Atom(NonZeroUsize); - -/// An atom *without* a strong reference. -/// -/// Only usable as `&'a WeakAtom`, -/// where `'a` is the lifetime of something that holds a strong reference to that atom. -pub struct WeakAtom(nsAtom); - -/// The number of static atoms we have. -const STATIC_ATOM_COUNT: usize = GkAtoms_Atoms_AtomsCount as usize; - -/// Returns the Gecko static atom array. -/// -/// We have this rather than use rust-bindgen to generate -/// mozilla::detail::gGkAtoms and then just reference gGkAtoms.mAtoms, so we -/// avoid a problem with lld-link.exe on Windows. -/// -/// https://bugzilla.mozilla.org/show_bug.cgi?id=1517685 -#[inline] -fn static_atoms() -> &'static [nsStaticAtom; STATIC_ATOM_COUNT] { - unsafe { - let addr = &gGkAtoms as *const _ as usize + kGkAtomsArrayOffset as usize; - &*(addr as *const _) - } -} - -/// Returns whether the specified address points to one of the nsStaticAtom -/// objects in the Gecko static atom array. -#[inline] -fn valid_static_atom_addr(addr: usize) -> bool { - unsafe { - let atoms = static_atoms(); - let start = atoms.as_ptr(); - let end = atoms.get_unchecked(STATIC_ATOM_COUNT) as *const _; - let in_range = addr >= start as usize && addr < end as usize; - let aligned = addr % mem::align_of::() == 0; - in_range && aligned - } -} - -impl Deref for Atom { - type Target = WeakAtom; - - #[inline] - fn deref(&self) -> &WeakAtom { - unsafe { - let addr = if self.is_static() { - (&gGkAtoms as *const _ as usize) + (self.0.get() >> 1) - } else { - self.0.get() - }; - debug_assert!(!self.is_static() || valid_static_atom_addr(addr)); - WeakAtom::new(addr as *const nsAtom) - } - } -} - -impl PrecomputedHash for Atom { - #[inline] - fn precomputed_hash(&self) -> u32 { - self.get_hash() - } -} - -impl Borrow for Atom { - #[inline] - fn borrow(&self) -> &WeakAtom { - self - } -} - -impl ToShmem for Atom { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { - if !self.is_static() { - return Err(format!( - "ToShmem failed for Atom: must be a static atom: {}", - self - )); - } - - Ok(ManuallyDrop::new(Atom(self.0))) - } -} - -impl Eq for WeakAtom {} -impl PartialEq for WeakAtom { - #[inline] - fn eq(&self, other: &Self) -> bool { - let weak: *const WeakAtom = self; - let other: *const WeakAtom = other; - weak == other - } -} - -impl PartialEq for WeakAtom { - #[inline] - fn eq(&self, other: &Atom) -> bool { - self == &**other - } -} - -unsafe impl Send for Atom {} -unsafe impl Sync for Atom {} -unsafe impl Sync for WeakAtom {} - -impl WeakAtom { - /// Construct a `WeakAtom` from a raw `nsAtom`. - #[inline] - pub unsafe fn new<'a>(atom: *const nsAtom) -> &'a mut Self { - &mut *(atom as *mut WeakAtom) - } - - /// Clone this atom, bumping the refcount if the atom is not static. - #[inline] - pub fn clone(&self) -> Atom { - unsafe { Atom::from_raw(self.as_ptr()) } - } - - /// Get the atom hash. - #[inline] - pub fn get_hash(&self) -> u32 { - self.0.mHash - } - - /// Get the atom as a slice of utf-16 chars. - #[inline] - pub fn as_slice(&self) -> &[u16] { - let string = if self.is_static() { - let atom_ptr = self.as_ptr() as *const nsStaticAtom; - let string_offset = unsafe { (*atom_ptr).mStringOffset }; - let string_offset = -(string_offset as isize); - let u8_ptr = atom_ptr as *const u8; - // It is safe to use offset() here because both addresses are within - // the same struct, e.g. mozilla::detail::gGkAtoms. - unsafe { u8_ptr.offset(string_offset) as *const u16 } - } else { - let atom_ptr = self.as_ptr() as *const nsDynamicAtom; - // Dynamic atom chars are stored at the end of the object. - unsafe { atom_ptr.offset(1) as *const u16 } - }; - unsafe { slice::from_raw_parts(string, self.len() as usize) } - } - - // NOTE: don't expose this, since it's slow, and easy to be misused. - fn chars(&self) -> DecodeUtf16>> { - char::decode_utf16(self.as_slice().iter().cloned()) - } - - /// Execute `cb` with the string that this atom represents. - /// - /// Find alternatives to this function when possible, please, since it's - /// pretty slow. - pub fn with_str(&self, cb: F) -> Output - where - F: FnOnce(&str) -> Output, - { - let mut buffer = mem::MaybeUninit::<[u8; 64]>::uninit(); - let buffer = unsafe { &mut *buffer.as_mut_ptr() }; - - // The total string length in utf16 is going to be less than or equal - // the slice length (each utf16 character is going to take at least one - // and at most 2 items in the utf16 slice). - // - // Each of those characters will take at most four bytes in the utf8 - // one. Thus if the slice is less than 64 / 4 (16) we can guarantee that - // we'll decode it in place. - let owned_string; - let len = self.len(); - let utf8_slice = if len <= 16 { - let mut total_len = 0; - - for c in self.chars() { - let c = c.unwrap_or(char::REPLACEMENT_CHARACTER); - let utf8_len = c.encode_utf8(&mut buffer[total_len..]).len(); - total_len += utf8_len; - } - - let slice = unsafe { str::from_utf8_unchecked(&buffer[..total_len]) }; - debug_assert_eq!(slice, String::from_utf16_lossy(self.as_slice())); - slice - } else { - owned_string = String::from_utf16_lossy(self.as_slice()); - &*owned_string - }; - - cb(utf8_slice) - } - - /// Returns whether this atom is static. - #[inline] - pub fn is_static(&self) -> bool { - self.0.mIsStatic() != 0 - } - - /// Returns whether this atom is ascii lowercase. - #[inline] - fn is_ascii_lowercase(&self) -> bool { - self.0.mIsAsciiLowercase() != 0 - } - - /// Returns the length of the atom string. - #[inline] - pub fn len(&self) -> u32 { - self.0.mLength() - } - - /// Returns whether this atom is the empty string. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the atom as a mutable pointer. - #[inline] - pub fn as_ptr(&self) -> *mut nsAtom { - let const_ptr: *const nsAtom = &self.0; - const_ptr as *mut nsAtom - } - - /// Convert this atom to ASCII lower-case - pub fn to_ascii_lowercase(&self) -> Atom { - if self.is_ascii_lowercase() { - return self.clone(); - } - - let slice = self.as_slice(); - let mut buffer = mem::MaybeUninit::<[u16; 64]>::uninit(); - let buffer = unsafe { &mut *buffer.as_mut_ptr() }; - let mut vec; - let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) { - buffer_prefix.copy_from_slice(slice); - buffer_prefix - } else { - vec = slice.to_vec(); - &mut vec - }; - for char16 in &mut *mutable_slice { - if *char16 <= 0x7F { - *char16 = (*char16 as u8).to_ascii_lowercase() as u16 - } - } - Atom::from(&*mutable_slice) - } - - /// Return whether two atoms are ASCII-case-insensitive matches - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { - if self == other { - return true; - } - - // If we know both atoms are ascii-lowercase, then we can stick with - // pointer equality. - if self.is_ascii_lowercase() && other.is_ascii_lowercase() { - debug_assert!(!self.eq_ignore_ascii_case_slow(other)); - return false; - } - - self.eq_ignore_ascii_case_slow(other) - } - - fn eq_ignore_ascii_case_slow(&self, other: &Self) -> bool { - let a = self.as_slice(); - let b = other.as_slice(); - - if a.len() != b.len() { - return false; - } - - a.iter().zip(b).all(|(&a16, &b16)| { - if a16 <= 0x7F && b16 <= 0x7F { - (a16 as u8).eq_ignore_ascii_case(&(b16 as u8)) - } else { - a16 == b16 - } - }) - } -} - -impl fmt::Debug for WeakAtom { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - write!(w, "Gecko WeakAtom({:p}, {})", self, self) - } -} - -impl fmt::Display for WeakAtom { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - for c in self.chars() { - w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))? - } - Ok(()) - } -} - -#[inline] -unsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize { - debug_assert!(!ptr.is_null()); - if !WeakAtom::new(ptr).is_static() { - NonZeroUsize::new_unchecked(ptr as usize) - } else { - make_static_handle(ptr as *mut nsStaticAtom) - } -} - -#[inline] -unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> NonZeroUsize { - // FIXME(heycam): Use offset_from once it's stabilized. - // https://github.com/rust-lang/rust/issues/41079 - debug_assert!(valid_static_atom_addr(ptr as usize)); - let base = &gGkAtoms as *const _; - let offset = ptr as usize - base as usize; - NonZeroUsize::new_unchecked((offset << 1) | 1) -} - -impl Atom { - #[inline] - fn is_static(&self) -> bool { - self.0.get() & 1 == 1 - } - - /// Execute a callback with the atom represented by `ptr`. - pub unsafe fn with(ptr: *const nsAtom, callback: F) -> R - where - F: FnOnce(&Atom) -> R, - { - let atom = Atom(make_handle(ptr as *mut nsAtom)); - let ret = callback(&atom); - mem::forget(atom); - ret - } - - /// Creates a static atom from its index in the static atom table, without - /// checking. - #[inline] - pub const unsafe fn from_index_unchecked(index: u16) -> Self { - // FIXME(emilio): No support for debug_assert! in const fn for now. Note - // that violating this invariant will debug-assert in the `Deref` impl - // though. - // - // debug_assert!((index as usize) < STATIC_ATOM_COUNT); - let offset = - (index as usize) * std::mem::size_of::() + kGkAtomsArrayOffset as usize; - Atom(NonZeroUsize::new_unchecked((offset << 1) | 1)) - } - - /// Creates an atom from an atom pointer. - #[inline(always)] - pub unsafe fn from_raw(ptr: *mut nsAtom) -> Self { - let atom = Atom(make_handle(ptr)); - if !atom.is_static() { - Gecko_AddRefAtom(ptr); - } - atom - } - - /// Creates an atom from an atom pointer that has already had AddRef - /// called on it. This may be a static or dynamic atom. - #[inline] - pub unsafe fn from_addrefed(ptr: *mut nsAtom) -> Self { - assert!(!ptr.is_null()); - Atom(make_handle(ptr)) - } - - /// Convert this atom into an addrefed nsAtom pointer. - #[inline] - pub fn into_addrefed(self) -> *mut nsAtom { - let ptr = self.as_ptr(); - mem::forget(self); - ptr - } -} - -impl Hash for Atom { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - state.write_u32(self.get_hash()); - } -} - -impl Hash for WeakAtom { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - state.write_u32(self.get_hash()); - } -} - -impl Clone for Atom { - #[inline(always)] - fn clone(&self) -> Atom { - unsafe { - let atom = Atom(self.0); - if !atom.is_static() { - Gecko_AddRefAtom(atom.as_ptr()); - } - atom - } - } -} - -impl Drop for Atom { - #[inline] - fn drop(&mut self) { - if !self.is_static() { - unsafe { - Gecko_ReleaseAtom(self.as_ptr()); - } - } - } -} - -impl Default for Atom { - #[inline] - fn default() -> Self { - atom!("") - } -} - -impl fmt::Debug for Atom { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - write!(w, "Atom(0x{:08x}, {})", self.0, self) - } -} - -impl fmt::Display for Atom { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - self.deref().fmt(w) - } -} - -impl<'a> From<&'a str> for Atom { - #[inline] - fn from(string: &str) -> Atom { - debug_assert!(string.len() <= u32::max_value() as usize); - unsafe { - Atom::from_addrefed(Gecko_Atomize( - string.as_ptr() as *const _, - string.len() as u32, - )) - } - } -} - -impl<'a> From<&'a [u16]> for Atom { - #[inline] - fn from(slice: &[u16]) -> Atom { - Atom::from(&*nsStr::from(slice)) - } -} - -impl<'a> From<&'a nsAString> for Atom { - #[inline] - fn from(string: &nsAString) -> Atom { - unsafe { Atom::from_addrefed(Gecko_Atomize16(string)) } - } -} - -impl<'a> From> for Atom { - #[inline] - fn from(string: Cow<'a, str>) -> Atom { - Atom::from(&*string) - } -} - -impl From for Atom { - #[inline] - fn from(string: String) -> Atom { - Atom::from(&*string) - } -} - -malloc_size_of_is_0!(Atom); - -impl SpecifiedValueInfo for Atom {} diff --git a/components/style/gecko_string_cache/namespace.rs b/components/style/gecko_string_cache/namespace.rs deleted file mode 100644 index d9745b9e21f..00000000000 --- a/components/style/gecko_string_cache/namespace.rs +++ /dev/null @@ -1,105 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A type to represent a namespace. - -use crate::gecko_bindings::structs::nsAtom; -use crate::string_cache::{Atom, WeakAtom}; -use precomputed_hash::PrecomputedHash; -use std::borrow::Borrow; -use std::fmt; -use std::ops::Deref; - -/// In Gecko namespaces are just regular atoms, so this is a simple macro to -/// forward one macro to the other. -#[macro_export] -macro_rules! ns { - () => { - $crate::string_cache::Namespace(atom!("")) - }; - ($s:tt) => { - $crate::string_cache::Namespace(atom!($s)) - }; -} - -/// A Gecko namespace is just a wrapped atom. -#[derive( - Clone, - Debug, - Default, - Eq, - Hash, - MallocSizeOf, - PartialEq, - ToComputedValue, - ToResolvedValue, - ToShmem, -)] -#[repr(transparent)] -pub struct Namespace(pub Atom); - -impl PrecomputedHash for Namespace { - #[inline] - fn precomputed_hash(&self) -> u32 { - self.0.precomputed_hash() - } -} - -/// A Gecko WeakNamespace is a wrapped WeakAtom. -#[derive(Deref, Hash)] -pub struct WeakNamespace(WeakAtom); - -impl Deref for Namespace { - type Target = WeakNamespace; - - #[inline] - fn deref(&self) -> &WeakNamespace { - let weak: *const WeakAtom = &*self.0; - unsafe { &*(weak as *const WeakNamespace) } - } -} - -impl<'a> From<&'a str> for Namespace { - fn from(s: &'a str) -> Self { - Namespace(Atom::from(s)) - } -} - -impl fmt::Display for Namespace { - fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(w) - } -} - -impl Borrow for Namespace { - #[inline] - fn borrow(&self) -> &WeakNamespace { - self - } -} - -impl WeakNamespace { - /// Trivially construct a WeakNamespace. - #[inline] - pub unsafe fn new<'a>(atom: *mut nsAtom) -> &'a Self { - &*(atom as *const WeakNamespace) - } - - /// Clone this WeakNamespace to obtain a strong reference to the same - /// underlying namespace. - #[inline] - pub fn clone(&self) -> Namespace { - Namespace(self.0.clone()) - } -} - -impl Eq for WeakNamespace {} -impl PartialEq for WeakNamespace { - #[inline] - fn eq(&self, other: &Self) -> bool { - let weak: *const WeakNamespace = self; - let other: *const WeakNamespace = other; - weak == other - } -} diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs deleted file mode 100644 index 8b5f3c89024..00000000000 --- a/components/style/global_style_data.rs +++ /dev/null @@ -1,173 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Global style data - -use crate::context::StyleSystemOptions; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::bindings; -use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; -use crate::shared_lock::SharedRwLock; -use crate::thread_state; -#[cfg(feature = "gecko")] -use gecko_profiler; -use parking_lot::{Mutex, RwLock, RwLockReadGuard}; -use rayon; -use std::{io, thread}; - -/// Global style data -pub struct GlobalStyleData { - /// Shared RWLock for CSSOM objects - pub shared_lock: SharedRwLock, - - /// Global style system options determined by env vars. - pub options: StyleSystemOptions, -} - -/// Global thread pool. -pub struct StyleThreadPool { - /// How many threads parallel styling can use. If not using a thread pool, this is set to `None`. - pub num_threads: Option, - - /// The parallel styling thread pool. - /// - /// For leak-checking purposes, we want to terminate the thread-pool, which - /// waits for all the async jobs to complete. Thus the RwLock. - style_thread_pool: RwLock>, -} - -fn thread_name(index: usize) -> String { - format!("Style#{}", index) -} - -lazy_static! { - /// JoinHandles for spawned style threads. These will be joined during - /// StyleThreadPool::shutdown() after exiting the thread pool. - /// - /// This would be quite inefficient if rayon destroyed and re-created - /// threads regularly during threadpool operation in response to demand, - /// however rayon actually never destroys its threads until the entire - /// thread pool is shut-down, so the size of this list is bounded. - static ref STYLE_THREAD_JOIN_HANDLES: Mutex>> = - Mutex::new(Vec::new()); -} - -fn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> { - let mut b = thread::Builder::new(); - if let Some(name) = options.name() { - b = b.name(name.to_owned()); - } - if let Some(stack_size) = options.stack_size() { - b = b.stack_size(stack_size); - } - let join_handle = b.spawn(|| options.run())?; - STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle); - Ok(()) -} - -fn thread_startup(_index: usize) { - thread_state::initialize_layout_worker_thread(); - #[cfg(feature = "gecko")] - unsafe { - bindings::Gecko_SetJemallocThreadLocalArena(true); - let name = thread_name(_index); - gecko_profiler::register_thread(&name); - } -} - -fn thread_shutdown(_: usize) { - #[cfg(feature = "gecko")] - unsafe { - gecko_profiler::unregister_thread(); - bindings::Gecko_SetJemallocThreadLocalArena(false); - } -} - -impl StyleThreadPool { - /// Shuts down the thread pool, waiting for all work to complete. - pub fn shutdown() { - if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() { - return; - } - { - // Drop the pool. - let _ = STYLE_THREAD_POOL.lock().unwrap().style_thread_pool.write().take(); - } - - // Join spawned threads until all of the threads have been joined. This - // will usually be pretty fast, as on shutdown there should be basically - // no threads left running. - while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() { - let _ = join_handle.join(); - } - } - - /// Returns a reference to the thread pool. - /// - /// We only really want to give read-only access to the pool, except - /// for shutdown(). - pub fn pool(&self) -> RwLockReadGuard> { - self.style_thread_pool.read() - } -} - -#[cfg(feature = "servo")] -fn stylo_threads_pref() -> i32 { - style_config::get_i32("layout.threads") -} - -#[cfg(feature = "gecko")] -fn stylo_threads_pref() -> i32 { - static_prefs::pref!("layout.css.stylo-threads") -} - -/// The performance benefit of additional threads seems to level off at around six, so we cap it -/// there on many-core machines (see bug 1431285 comment 14). -pub(crate) const STYLO_MAX_THREADS: usize = 6; - -lazy_static! { - /// Global thread pool - pub static ref STYLE_THREAD_POOL: std::sync::Mutex = { - use std::cmp; - // We always set this pref on startup, before layout or script have had a chance of - // accessing (and thus creating) the thread-pool. - let threads_pref: i32 = stylo_threads_pref(); - let num_threads = if threads_pref >= 0 { - threads_pref as usize - } else { - use num_cpus; - // The default heuristic is num_virtual_cores * .75. This gives us three threads on a - // hyper-threaded dual core, and six threads on a hyper-threaded quad core. - let threads = cmp::max(num_cpus::get() * 3 / 4, 1); - // There's no point in creating a thread pool if there's one thread. - if threads == 1 { 0 } else { threads } - }; - - let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS); - let (pool, num_threads) = if num_threads < 1 { - (None, None) - } else { - let workers = rayon::ThreadPoolBuilder::new() - .spawn_handler(thread_spawn) - .num_threads(num_threads) - .thread_name(thread_name) - .start_handler(thread_startup) - .exit_handler(thread_shutdown) - .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024) - .build(); - (workers.ok(), Some(num_threads)) - }; - - std::sync::Mutex::new(StyleThreadPool { - num_threads, - style_thread_pool: RwLock::new(pool), - }) - }; - - /// Global style data - pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData { - shared_lock: SharedRwLock::new_leaked(), - options: StyleSystemOptions::default(), - }; -} diff --git a/components/style/invalidation/element/document_state.rs b/components/style/invalidation/element/document_state.rs deleted file mode 100644 index b0bbce3b855..00000000000 --- a/components/style/invalidation/element/document_state.rs +++ /dev/null @@ -1,142 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! An invalidation processor for style changes due to document state changes. - -use crate::dom::TElement; -use crate::invalidation::element::invalidation_map::Dependency; -use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector}; -use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor}; -use crate::invalidation::element::state_and_attributes; -use crate::stylist::CascadeData; -use selectors::matching::{ - MatchingContext, MatchingMode, NeedsSelectorFlags, QuirksMode, VisitedHandlingMode, -}; -use selectors::NthIndexCache; -use style_traits::dom::DocumentState; - -/// A struct holding the members necessary to invalidate document state -/// selectors. -#[derive(Debug)] -pub struct InvalidationMatchingData { - /// The document state that has changed, which makes it always match. - pub document_state: DocumentState, -} - -impl Default for InvalidationMatchingData { - #[inline(always)] - fn default() -> Self { - Self { - document_state: DocumentState::empty(), - } - } -} - -/// An invalidation processor for style changes due to state and attribute -/// changes. -pub struct DocumentStateInvalidationProcessor<'a, E: TElement, I> { - rules: I, - matching_context: MatchingContext<'a, E::Impl>, - document_states_changed: DocumentState, -} - -impl<'a, E: TElement, I> DocumentStateInvalidationProcessor<'a, E, I> { - /// Creates a new DocumentStateInvalidationProcessor. - #[inline] - pub fn new( - rules: I, - document_states_changed: DocumentState, - nth_index_cache: &'a mut NthIndexCache, - quirks_mode: QuirksMode, - ) -> Self { - let mut matching_context = MatchingContext::<'a, E::Impl>::new_for_visited( - MatchingMode::Normal, - None, - nth_index_cache, - VisitedHandlingMode::AllLinksVisitedAndUnvisited, - quirks_mode, - NeedsSelectorFlags::No, - ); - - matching_context.extra_data.invalidation_data.document_state = document_states_changed; - - Self { - rules, - document_states_changed, - matching_context, - } - } -} - -impl<'a, E, I> InvalidationProcessor<'a, E> for DocumentStateInvalidationProcessor<'a, E, I> -where - E: TElement, - I: Iterator, -{ - fn check_outer_dependency(&mut self, _: &Dependency, _: E) -> bool { - debug_assert!( - false, - "how, we should only have parent-less dependencies here!" - ); - true - } - - fn collect_invalidations( - &mut self, - _element: E, - self_invalidations: &mut InvalidationVector<'a>, - _descendant_invalidations: &mut DescendantInvalidationLists<'a>, - _sibling_invalidations: &mut InvalidationVector<'a>, - ) -> bool { - for cascade_data in &mut self.rules { - let map = cascade_data.invalidation_map(); - for dependency in &map.document_state_selectors { - if !dependency.state.intersects(self.document_states_changed) { - continue; - } - - // We pass `None` as a scope, as document state selectors aren't - // affected by the current scope. - // - // FIXME(emilio): We should really pass the relevant host for - // self.rules, so that we invalidate correctly if the selector - // happens to have something like :host(:-moz-window-inactive) - // for example. - self_invalidations.push(Invalidation::new( - &dependency.dependency, - /* scope = */ None, - )); - } - } - - false - } - - fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> { - &mut self.matching_context - } - - fn recursion_limit_exceeded(&mut self, _: E) { - unreachable!("We don't run document state invalidation with stack limits") - } - - fn should_process_descendants(&mut self, element: E) -> bool { - match element.borrow_data() { - Some(d) => state_and_attributes::should_process_descendants(&d), - None => false, - } - } - - fn invalidated_descendants(&mut self, element: E, child: E) { - state_and_attributes::invalidated_descendants(element, child) - } - - fn invalidated_self(&mut self, element: E) { - state_and_attributes::invalidated_self(element); - } - - fn invalidated_sibling(&mut self, sibling: E, of: E) { - state_and_attributes::invalidated_sibling(sibling, of); - } -} diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs deleted file mode 100644 index 8c25153d1a8..00000000000 --- a/components/style/invalidation/element/element_wrapper.rs +++ /dev/null @@ -1,391 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A wrapper over an element and a snapshot, that allows us to selector-match -//! against a past state of the element. - -use crate::dom::TElement; -use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl}; -use crate::selector_parser::{Snapshot, SnapshotMap}; -use crate::values::AtomIdent; -use crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom}; -use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; -use selectors::matching::{ElementSelectorFlags, MatchingContext}; -use selectors::{Element, OpaqueElement}; -use std::cell::Cell; -use std::fmt; -use style_traits::dom::ElementState; - -/// In order to compute restyle hints, we perform a selector match against a -/// list of partial selectors whose rightmost simple selector may be sensitive -/// to the thing being changed. We do this matching twice, once for the element -/// as it exists now and once for the element as it existed at the time of the -/// last restyle. If the results of the selector match differ, that means that -/// the given partial selector is sensitive to the change, and we compute a -/// restyle hint based on its combinator. -/// -/// In order to run selector matching against the old element state, we generate -/// a wrapper for the element which claims to have the old state. This is the -/// ElementWrapper logic below. -/// -/// Gecko does this differently for element states, and passes a mask called -/// mStateMask, which indicates the states that need to be ignored during -/// selector matching. This saves an ElementWrapper allocation and an additional -/// selector match call at the expense of additional complexity inside the -/// selector matching logic. This only works for boolean states though, so we -/// still need to take the ElementWrapper approach for attribute-dependent -/// style. So we do it the same both ways for now to reduce complexity, but it's -/// worth measuring the performance impact (if any) of the mStateMask approach. -pub trait ElementSnapshot: Sized { - /// The state of the snapshot, if any. - fn state(&self) -> Option; - - /// If this snapshot contains attribute information. - fn has_attrs(&self) -> bool; - - /// Gets the attribute information of the snapshot as a string. - /// - /// Only for debugging purposes. - fn debug_list_attributes(&self) -> String { - String::new() - } - - /// The ID attribute per this snapshot. Should only be called if - /// `has_attrs()` returns true. - fn id_attr(&self) -> Option<&WeakAtom>; - - /// Whether this snapshot contains the class `name`. Should only be called - /// if `has_attrs()` returns true. - fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool; - - /// Whether this snapshot represents the part named `name`. Should only be - /// called if `has_attrs()` returns true. - fn is_part(&self, name: &AtomIdent) -> bool; - - /// See Element::imported_part. - fn imported_part(&self, name: &AtomIdent) -> Option; - - /// A callback that should be called for each class of the snapshot. Should - /// only be called if `has_attrs()` returns true. - fn each_class(&self, _: F) - where - F: FnMut(&AtomIdent); - - /// The `xml:lang=""` or `lang=""` attribute value per this snapshot. - fn lang_attr(&self) -> Option; -} - -/// A simple wrapper over an element and a snapshot, that allows us to -/// selector-match against a past state of the element. -#[derive(Clone)] -pub struct ElementWrapper<'a, E> -where - E: TElement, -{ - element: E, - cached_snapshot: Cell>, - snapshot_map: &'a SnapshotMap, -} - -impl<'a, E> ElementWrapper<'a, E> -where - E: TElement, -{ - /// Trivially constructs an `ElementWrapper`. - pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self { - ElementWrapper { - element: el, - cached_snapshot: Cell::new(None), - snapshot_map: snapshot_map, - } - } - - /// Gets the snapshot associated with this element, if any. - pub fn snapshot(&self) -> Option<&'a Snapshot> { - if !self.element.has_snapshot() { - return None; - } - - if let Some(s) = self.cached_snapshot.get() { - return Some(s); - } - - let snapshot = self.snapshot_map.get(&self.element); - debug_assert!(snapshot.is_some(), "has_snapshot lied!"); - - self.cached_snapshot.set(snapshot); - - snapshot - } - - /// Returns the states that have changed since the element was snapshotted. - pub fn state_changes(&self) -> ElementState { - let snapshot = match self.snapshot() { - Some(s) => s, - None => return ElementState::empty(), - }; - - match snapshot.state() { - Some(state) => state ^ self.element.state(), - None => ElementState::empty(), - } - } - - /// Returns the value of the `xml:lang=""` (or, if appropriate, `lang=""`) - /// attribute from this element's snapshot or the closest ancestor - /// element snapshot with the attribute specified. - fn get_lang(&self) -> Option { - let mut current = self.clone(); - loop { - let lang = match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(), - _ => current.element.lang_attr(), - }; - if lang.is_some() { - return lang; - } - current = current.parent_element()?; - } - } -} - -impl<'a, E> fmt::Debug for ElementWrapper<'a, E> -where - E: TElement, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Ignore other fields for now, can change later if needed. - self.element.fmt(f) - } -} - -impl<'a, E> Element for ElementWrapper<'a, E> -where - E: TElement, -{ - type Impl = SelectorImpl; - - fn match_non_ts_pseudo_class( - &self, - pseudo_class: &NonTSPseudoClass, - context: &mut MatchingContext, - ) -> bool { - // Some pseudo-classes need special handling to evaluate them against - // the snapshot. - match *pseudo_class { - // For :link and :visited, we don't actually want to test the - // element state directly. - // - // Instead, we use the `visited_handling` to determine if they - // match. - NonTSPseudoClass::Link => { - return self.is_link() && context.visited_handling().matches_unvisited(); - }, - NonTSPseudoClass::Visited => { - return self.is_link() && context.visited_handling().matches_visited(); - }, - - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozTableBorderNonzero => { - if let Some(snapshot) = self.snapshot() { - if snapshot.has_other_pseudo_class_state() { - return snapshot.mIsTableBorderNonzero(); - } - } - }, - - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozBrowserFrame => { - if let Some(snapshot) = self.snapshot() { - if snapshot.has_other_pseudo_class_state() { - return snapshot.mIsMozBrowserFrame(); - } - } - }, - - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozSelectListBox => { - if let Some(snapshot) = self.snapshot() { - if snapshot.has_other_pseudo_class_state() { - return snapshot.mIsSelectListBox(); - } - } - }, - - // :lang() needs to match using the closest ancestor xml:lang="" or - // lang="" attribtue from snapshots. - NonTSPseudoClass::Lang(ref lang_arg) => { - return self - .element - .match_element_lang(Some(self.get_lang()), lang_arg); - }, - - _ => {}, - } - - let flag = pseudo_class.state_flag(); - if flag.is_empty() { - return self - .element - .match_non_ts_pseudo_class(pseudo_class, context); - } - match self.snapshot().and_then(|s| s.state()) { - Some(snapshot_state) => snapshot_state.intersects(flag), - None => self - .element - .match_non_ts_pseudo_class(pseudo_class, context), - } - } - - fn apply_selector_flags(&self, _flags: ElementSelectorFlags) { - debug_assert!(false, "Shouldn't need selector flags for invalidation"); - } - - fn match_pseudo_element( - &self, - pseudo_element: &PseudoElement, - context: &mut MatchingContext, - ) -> bool { - self.element.match_pseudo_element(pseudo_element, context) - } - - fn is_link(&self) -> bool { - match self.snapshot().and_then(|s| s.state()) { - Some(state) => state.intersects(ElementState::VISITED_OR_UNVISITED), - None => self.element.is_link(), - } - } - - fn opaque(&self) -> OpaqueElement { - self.element.opaque() - } - - fn parent_element(&self) -> Option { - let parent = self.element.parent_element()?; - Some(Self::new(parent, self.snapshot_map)) - } - - fn parent_node_is_shadow_root(&self) -> bool { - self.element.parent_node_is_shadow_root() - } - - fn containing_shadow_host(&self) -> Option { - let host = self.element.containing_shadow_host()?; - Some(Self::new(host, self.snapshot_map)) - } - - fn prev_sibling_element(&self) -> Option { - let sibling = self.element.prev_sibling_element()?; - Some(Self::new(sibling, self.snapshot_map)) - } - - fn next_sibling_element(&self) -> Option { - let sibling = self.element.next_sibling_element()?; - Some(Self::new(sibling, self.snapshot_map)) - } - - fn first_element_child(&self) -> Option { - let child = self.element.first_element_child()?; - Some(Self::new(child, self.snapshot_map)) - } - - #[inline] - fn is_html_element_in_html_document(&self) -> bool { - self.element.is_html_element_in_html_document() - } - - #[inline] - fn is_html_slot_element(&self) -> bool { - self.element.is_html_slot_element() - } - - #[inline] - fn has_local_name( - &self, - local_name: &::BorrowedLocalName, - ) -> bool { - self.element.has_local_name(local_name) - } - - #[inline] - fn has_namespace( - &self, - ns: &::BorrowedNamespaceUrl, - ) -> bool { - self.element.has_namespace(ns) - } - - #[inline] - fn is_same_type(&self, other: &Self) -> bool { - self.element.is_same_type(&other.element) - } - - fn attr_matches( - &self, - ns: &NamespaceConstraint<&Namespace>, - local_name: &LocalName, - operation: &AttrSelectorOperation<&AttrValue>, - ) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => { - snapshot.attr_matches(ns, local_name, operation) - }, - _ => self.element.attr_matches(ns, local_name, operation), - } - } - - fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot - .id_attr() - .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)), - _ => self.element.has_id(id, case_sensitivity), - } - } - - fn is_part(&self, name: &AtomIdent) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name), - _ => self.element.is_part(name), - } - } - - fn imported_part(&self, name: &AtomIdent) -> Option { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name), - _ => self.element.imported_part(name), - } - } - - fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { - match self.snapshot() { - Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity), - _ => self.element.has_class(name, case_sensitivity), - } - } - - fn is_empty(&self) -> bool { - self.element.is_empty() - } - - fn is_root(&self) -> bool { - self.element.is_root() - } - - fn is_pseudo_element(&self) -> bool { - self.element.is_pseudo_element() - } - - fn pseudo_element_originating_element(&self) -> Option { - self.element - .pseudo_element_originating_element() - .map(|e| ElementWrapper::new(e, self.snapshot_map)) - } - - fn assigned_slot(&self) -> Option { - self.element - .assigned_slot() - .map(|e| ElementWrapper::new(e, self.snapshot_map)) - } -} diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs deleted file mode 100644 index 1cfc2fa7c05..00000000000 --- a/components/style/invalidation/element/invalidation_map.rs +++ /dev/null @@ -1,547 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Code for invalidations due to state or attribute changes. - -use crate::context::QuirksMode; -use crate::selector_map::{ - MaybeCaseInsensitiveHashMap, PrecomputedHashMap, SelectorMap, SelectorMapEntry, -}; -use crate::selector_parser::SelectorImpl; -use crate::AllocErr; -use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded}; -use selectors::attr::NamespaceConstraint; -use selectors::parser::{Combinator, Component}; -use selectors::parser::{Selector, SelectorIter}; -use selectors::visitor::{SelectorListKind, SelectorVisitor}; -use smallvec::SmallVec; -use style_traits::dom::{DocumentState, ElementState}; - -/// Mapping between (partial) CompoundSelectors (and the combinator to their -/// right) and the states and attributes they depend on. -/// -/// In general, for all selectors in all applicable stylesheets of the form: -/// -/// |a _ b _ c _ d _ e| -/// -/// Where: -/// * |b| and |d| are simple selectors that depend on state (like :hover) or -/// attributes (like [attr...], .foo, or #foo). -/// * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on -/// state or attributes. -/// -/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|, -/// even though those selectors may not appear on their own in any stylesheet. -/// This allows us to quickly scan through the dependency sites of all style -/// rules and determine the maximum effect that a given state or attribute -/// change may have on the style of elements in the document. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct Dependency { - /// The dependency selector. - #[cfg_attr( - feature = "gecko", - ignore_malloc_size_of = "CssRules have primary refs, we measure there" - )] - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] - pub selector: Selector, - - /// The offset into the selector that we should match on. - pub selector_offset: usize, - - /// The parent dependency for an ancestor selector. For example, consider - /// the following: - /// - /// .foo .bar:where(.baz span) .qux - /// ^ ^ ^ - /// A B C - /// - /// We'd generate: - /// - /// * One dependency for .qux (offset: 0, parent: None) - /// * One dependency for .baz pointing to B with parent being a - /// dependency pointing to C. - /// * One dependency from .bar pointing to C (parent: None) - /// * One dependency from .foo pointing to A (parent: None) - /// - pub parent: Option>, -} - -size_of_test!(Dependency, 24); - -/// The kind of elements down the tree this dependency may affect. -#[derive(Debug, Eq, PartialEq)] -pub enum DependencyInvalidationKind { - /// This dependency may affect the element that changed itself. - Element, - /// This dependency affects the style of the element itself, and also the - /// style of its descendants. - /// - /// TODO(emilio): Each time this feels more of a hack for eager pseudos... - ElementAndDescendants, - /// This dependency may affect descendants down the tree. - Descendants, - /// This dependency may affect siblings to the right of the element that - /// changed. - Siblings, - /// This dependency may affect slotted elements of the element that changed. - SlottedElements, - /// This dependency may affect parts of the element that changed. - Parts, -} - -impl Dependency { - /// Creates a dummy dependency to invalidate the whole selector. - /// - /// This is necessary because document state invalidation wants to - /// invalidate all elements in the document. - /// - /// The offset is such as that Invalidation::new(self) returns a zero - /// offset. That is, it points to a virtual "combinator" outside of the - /// selector, so calling combinator() on such a dependency will panic. - pub fn for_full_selector_invalidation(selector: Selector) -> Self { - Self { - selector_offset: selector.len() + 1, - selector, - parent: None, - } - } - - /// Returns the combinator to the right of the partial selector this - /// dependency represents. - /// - /// TODO(emilio): Consider storing inline if it helps cache locality? - pub fn combinator(&self) -> Option { - if self.selector_offset == 0 { - return None; - } - - Some( - self.selector - .combinator_at_match_order(self.selector_offset - 1), - ) - } - - /// The kind of invalidation that this would generate. - pub fn invalidation_kind(&self) -> DependencyInvalidationKind { - match self.combinator() { - None => DependencyInvalidationKind::Element, - Some(Combinator::Child) | Some(Combinator::Descendant) => { - DependencyInvalidationKind::Descendants - }, - Some(Combinator::LaterSibling) | Some(Combinator::NextSibling) => { - DependencyInvalidationKind::Siblings - }, - // TODO(emilio): We could look at the selector itself to see if it's - // an eager pseudo, and return only Descendants here if not. - Some(Combinator::PseudoElement) => DependencyInvalidationKind::ElementAndDescendants, - Some(Combinator::SlotAssignment) => DependencyInvalidationKind::SlottedElements, - Some(Combinator::Part) => DependencyInvalidationKind::Parts, - } - } -} - -impl SelectorMapEntry for Dependency { - fn selector(&self) -> SelectorIter { - self.selector.iter_from(self.selector_offset) - } -} - -/// The same, but for state selectors, which can track more exactly what state -/// do they track. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct StateDependency { - /// The other dependency fields. - pub dep: Dependency, - /// The state this dependency is affected by. - pub state: ElementState, -} - -impl SelectorMapEntry for StateDependency { - fn selector(&self) -> SelectorIter { - self.dep.selector() - } -} - -/// The same, but for document state selectors. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct DocumentStateDependency { - /// We track `Dependency` even though we don't need to track an offset, - /// since when it changes it changes for the whole document anyway. - #[cfg_attr( - feature = "gecko", - ignore_malloc_size_of = "CssRules have primary refs, we measure there" - )] - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] - pub dependency: Dependency, - /// The state this dependency is affected by. - pub state: DocumentState, -} - -/// A map where we store invalidations. -/// -/// This is slightly different to a SelectorMap, in the sense of that the same -/// selector may appear multiple times. -/// -/// In particular, we want to lookup as few things as possible to get the fewer -/// selectors the better, so this looks up by id, class, or looks at the list of -/// state/other attribute affecting selectors. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct InvalidationMap { - /// A map from a given class name to all the selectors with that class - /// selector. - pub class_to_selector: MaybeCaseInsensitiveHashMap>, - /// A map from a given id to all the selectors with that ID in the - /// stylesheets currently applying to the document. - pub id_to_selector: MaybeCaseInsensitiveHashMap>, - /// A map of all the state dependencies. - pub state_affecting_selectors: SelectorMap, - /// A list of document state dependencies in the rules we represent. - pub document_state_selectors: Vec, - /// A map of other attribute affecting selectors. - pub other_attribute_affecting_selectors: - PrecomputedHashMap>, -} - -impl InvalidationMap { - /// Creates an empty `InvalidationMap`. - pub fn new() -> Self { - Self { - class_to_selector: MaybeCaseInsensitiveHashMap::new(), - id_to_selector: MaybeCaseInsensitiveHashMap::new(), - state_affecting_selectors: SelectorMap::new(), - document_state_selectors: Vec::new(), - other_attribute_affecting_selectors: PrecomputedHashMap::default(), - } - } - - /// Returns the number of dependencies stored in the invalidation map. - pub fn len(&self) -> usize { - self.state_affecting_selectors.len() + - self.document_state_selectors.len() + - self.other_attribute_affecting_selectors - .iter() - .fold(0, |accum, (_, ref v)| accum + v.len()) + - self.id_to_selector - .iter() - .fold(0, |accum, (_, ref v)| accum + v.len()) + - self.class_to_selector - .iter() - .fold(0, |accum, (_, ref v)| accum + v.len()) - } - - /// Clears this map, leaving it empty. - pub fn clear(&mut self) { - self.class_to_selector.clear(); - self.id_to_selector.clear(); - self.state_affecting_selectors.clear(); - self.document_state_selectors.clear(); - self.other_attribute_affecting_selectors.clear(); - } - - /// Shrink the capacity of hash maps if needed. - pub fn shrink_if_needed(&mut self) { - self.class_to_selector.shrink_if_needed(); - self.id_to_selector.shrink_if_needed(); - self.state_affecting_selectors.shrink_if_needed(); - self.other_attribute_affecting_selectors.shrink_if_needed(); - } - - /// Adds a selector to this `InvalidationMap`. Returns Err(..) to - /// signify OOM. - pub fn note_selector( - &mut self, - selector: &Selector, - quirks_mode: QuirksMode, - ) -> Result<(), AllocErr> { - debug!("InvalidationMap::note_selector({:?})", selector); - - let mut document_state = DocumentState::empty(); - - { - let mut parent_stack = SmallVec::new(); - let mut alloc_error = None; - let mut collector = SelectorDependencyCollector { - map: self, - document_state: &mut document_state, - selector, - parent_selectors: &mut parent_stack, - quirks_mode, - compound_state: PerCompoundState::new(0), - alloc_error: &mut alloc_error, - }; - - let visit_result = collector.visit_whole_selector(); - debug_assert_eq!(!visit_result, alloc_error.is_some()); - if let Some(alloc_error) = alloc_error { - return Err(alloc_error); - } - } - - if !document_state.is_empty() { - let dep = DocumentStateDependency { - state: document_state, - dependency: Dependency::for_full_selector_invalidation(selector.clone()), - }; - self.document_state_selectors.try_reserve(1)?; - self.document_state_selectors.push(dep); - } - - Ok(()) - } -} - -struct PerCompoundState { - /// The offset at which our compound starts. - offset: usize, - - /// The state this compound selector is affected by. - element_state: ElementState, -} - -impl PerCompoundState { - fn new(offset: usize) -> Self { - Self { - offset, - element_state: ElementState::empty(), - } - } -} - -/// A struct that collects invalidations for a given compound selector. -struct SelectorDependencyCollector<'a> { - map: &'a mut InvalidationMap, - - /// The document this _complex_ selector is affected by. - /// - /// We don't need to track state per compound selector, since it's global - /// state and it changes for everything. - document_state: &'a mut DocumentState, - - /// The current selector and offset we're iterating. - selector: &'a Selector, - - /// The stack of parent selectors that we have, and at which offset of the - /// sequence. - /// - /// This starts empty. It grows when we find nested :is and :where selector - /// lists. - parent_selectors: &'a mut SmallVec<[(Selector, usize); 5]>, - - /// The quirks mode of the document where we're inserting dependencies. - quirks_mode: QuirksMode, - - /// State relevant to a given compound selector. - compound_state: PerCompoundState, - - /// The allocation error, if we OOM. - alloc_error: &'a mut Option, -} - -impl<'a> SelectorDependencyCollector<'a> { - fn visit_whole_selector(&mut self) -> bool { - let iter = self.selector.iter(); - self.visit_whole_selector_from(iter, 0) - } - - fn visit_whole_selector_from( - &mut self, - mut iter: SelectorIter, - mut index: usize, - ) -> bool { - loop { - // Reset the compound state. - self.compound_state = PerCompoundState::new(index); - - // Visit all the simple selectors in this sequence. - for ss in &mut iter { - if !ss.visit(self) { - return false; - } - index += 1; // Account for the simple selector. - } - - if !self.compound_state.element_state.is_empty() { - let dependency = self.dependency(); - let result = self.map.state_affecting_selectors.insert( - StateDependency { - dep: dependency, - state: self.compound_state.element_state, - }, - self.quirks_mode, - ); - if let Err(alloc_error) = result { - *self.alloc_error = Some(alloc_error.into()); - return false; - } - } - - let combinator = iter.next_sequence(); - if combinator.is_none() { - return true; - } - index += 1; // account for the combinator - } - } - - fn add_attr_dependency(&mut self, name: LocalName) -> bool { - let dependency = self.dependency(); - - let map = &mut self.map.other_attribute_affecting_selectors; - if let Err(err) = map.try_reserve(1) { - *self.alloc_error = Some(err.into()); - return false; - } - let vec = map.entry(name).or_default(); - if let Err(err) = vec.try_reserve(1) { - *self.alloc_error = Some(err.into()); - return false; - } - vec.push(dependency); - true - } - - fn dependency(&self) -> Dependency { - let mut parent = None; - - // TODO(emilio): Maybe we should refcount the parent dependencies, or - // cache them or something. - for &(ref selector, ref selector_offset) in self.parent_selectors.iter() { - debug_assert_ne!( - self.compound_state.offset, 0, - "Shouldn't bother creating nested dependencies for the rightmost compound", - ); - let new_parent = Dependency { - selector: selector.clone(), - selector_offset: *selector_offset, - parent, - }; - parent = Some(Box::new(new_parent)); - } - - Dependency { - selector: self.selector.clone(), - selector_offset: self.compound_state.offset, - parent, - } - } -} - -impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> { - type Impl = SelectorImpl; - - fn visit_selector_list( - &mut self, - _list_kind: SelectorListKind, - list: &[Selector], - ) -> bool { - for selector in list { - // Here we cheat a bit: We can visit the rightmost compound with - // the "outer" visitor, and it'd be fine. This reduces the amount of - // state and attribute invalidations, and we need to check the outer - // selector to the left anyway to avoid over-invalidation, so it - // avoids matching it twice uselessly. - let mut iter = selector.iter(); - let mut index = 0; - - for ss in &mut iter { - if !ss.visit(self) { - return false; - } - index += 1; - } - - let combinator = iter.next_sequence(); - if combinator.is_none() { - continue; - } - - index += 1; // account for the combinator. - - self.parent_selectors - .push((self.selector.clone(), self.compound_state.offset)); - let mut nested = SelectorDependencyCollector { - map: &mut *self.map, - document_state: &mut *self.document_state, - selector, - parent_selectors: &mut *self.parent_selectors, - quirks_mode: self.quirks_mode, - compound_state: PerCompoundState::new(index), - alloc_error: &mut *self.alloc_error, - }; - if !nested.visit_whole_selector_from(iter, index) { - return false; - } - self.parent_selectors.pop(); - } - true - } - - fn visit_simple_selector(&mut self, s: &Component) -> bool { - use crate::selector_parser::NonTSPseudoClass; - - match *s { - Component::ID(ref atom) | Component::Class(ref atom) => { - let dependency = self.dependency(); - let map = match *s { - Component::ID(..) => &mut self.map.id_to_selector, - Component::Class(..) => &mut self.map.class_to_selector, - _ => unreachable!(), - }; - let entry = match map.try_entry(atom.0.clone(), self.quirks_mode) { - Ok(entry) => entry, - Err(err) => { - *self.alloc_error = Some(err.into()); - return false; - }, - }; - let vec = entry.or_insert_with(SmallVec::new); - if let Err(err) = vec.try_reserve(1) { - *self.alloc_error = Some(err.into()); - return false; - } - vec.push(dependency); - true - }, - Component::NonTSPseudoClass(ref pc) => { - self.compound_state.element_state |= pc.state_flag(); - *self.document_state |= pc.document_state_flag(); - - let attr_name = match *pc { - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozTableBorderNonzero => local_name!("border"), - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozBrowserFrame => local_name!("mozbrowser"), - #[cfg(feature = "gecko")] - NonTSPseudoClass::MozSelectListBox => { - // This depends on two attributes. - return self.add_attr_dependency(local_name!("multiple")) && - self.add_attr_dependency(local_name!("size")); - }, - NonTSPseudoClass::Lang(..) => local_name!("lang"), - _ => return true, - }; - - self.add_attr_dependency(attr_name) - }, - _ => true, - } - } - - fn visit_attribute_selector( - &mut self, - _: &NamespaceConstraint<&Namespace>, - local_name: &LocalName, - local_name_lower: &LocalName, - ) -> bool { - if !self.add_attr_dependency(local_name.clone()) { - return false; - } - - if local_name != local_name_lower && !self.add_attr_dependency(local_name_lower.clone()) { - return false; - } - - true - } -} diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs deleted file mode 100644 index 00f714c5b15..00000000000 --- a/components/style/invalidation/element/invalidator.rs +++ /dev/null @@ -1,1017 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The struct that takes care of encapsulating all the logic on where and how -//! element styles need to be invalidated. - -use crate::context::StackLimitChecker; -use crate::dom::{TElement, TNode, TShadowRoot}; -use crate::invalidation::element::invalidation_map::{Dependency, DependencyInvalidationKind}; -use selectors::matching::matches_compound_selector_from; -use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext}; -use selectors::parser::{Combinator, Component}; -use selectors::OpaqueElement; -use smallvec::SmallVec; -use std::fmt; -use std::fmt::Write; - -/// A trait to abstract the collection of invalidations for a given pass. -pub trait InvalidationProcessor<'a, E> -where - E: TElement, -{ - /// Whether an invalidation that contains only a pseudo-element selector - /// like ::before or ::after triggers invalidation of the element that would - /// originate it. - fn invalidates_on_pseudo_element(&self) -> bool { - false - } - - /// Whether the invalidation processor only cares about light-tree - /// descendants of a given element, that is, doesn't invalidate - /// pseudo-elements, NAC, shadow dom... - fn light_tree_only(&self) -> bool { - false - } - - /// When a dependency from a :where or :is selector matches, it may still be - /// the case that we don't need to invalidate the full style. Consider the - /// case of: - /// - /// div .foo:where(.bar *, .baz) .qux - /// - /// We can get to the `*` part after a .bar class change, but you only need - /// to restyle the element if it also matches .foo. - /// - /// Similarly, you only need to restyle .baz if the whole result of matching - /// the selector changes. - /// - /// This function is called to check the result of matching the "outer" - /// dependency that we generate for the parent of the `:where` selector, - /// that is, in the case above it should match - /// `div .foo:where(.bar *, .baz)`. - /// - /// Returning true unconditionally here is over-optimistic and may - /// over-invalidate. - fn check_outer_dependency(&mut self, dependency: &Dependency, element: E) -> bool; - - /// The matching context that should be used to process invalidations. - fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl>; - - /// Collect invalidations for a given element's descendants and siblings. - /// - /// Returns whether the element itself was invalidated. - fn collect_invalidations( - &mut self, - element: E, - self_invalidations: &mut InvalidationVector<'a>, - descendant_invalidations: &mut DescendantInvalidationLists<'a>, - sibling_invalidations: &mut InvalidationVector<'a>, - ) -> bool; - - /// Returns whether the invalidation process should process the descendants - /// of the given element. - fn should_process_descendants(&mut self, element: E) -> bool; - - /// Executes an arbitrary action when the recursion limit is exceded (if - /// any). - fn recursion_limit_exceeded(&mut self, element: E); - - /// Executes an action when `Self` is invalidated. - fn invalidated_self(&mut self, element: E); - - /// Executes an action when `sibling` is invalidated as a sibling of - /// `of`. - fn invalidated_sibling(&mut self, sibling: E, of: E); - - /// Executes an action when any descendant of `Self` is invalidated. - fn invalidated_descendants(&mut self, element: E, child: E); -} - -/// Different invalidation lists for descendants. -#[derive(Debug, Default)] -pub struct DescendantInvalidationLists<'a> { - /// Invalidations for normal DOM children and pseudo-elements. - /// - /// TODO(emilio): Having a list of invalidations just for pseudo-elements - /// may save some work here and there. - pub dom_descendants: InvalidationVector<'a>, - /// Invalidations for slotted children of an element. - pub slotted_descendants: InvalidationVector<'a>, - /// Invalidations for ::part()s of an element. - pub parts: InvalidationVector<'a>, -} - -impl<'a> DescendantInvalidationLists<'a> { - fn is_empty(&self) -> bool { - self.dom_descendants.is_empty() && - self.slotted_descendants.is_empty() && - self.parts.is_empty() - } -} - -/// The struct that takes care of encapsulating all the logic on where and how -/// element styles need to be invalidated. -pub struct TreeStyleInvalidator<'a, 'b, E, P: 'a> -where - 'b: 'a, - E: TElement, - P: InvalidationProcessor<'b, E>, -{ - element: E, - stack_limit_checker: Option<&'a StackLimitChecker>, - processor: &'a mut P, - _marker: ::std::marker::PhantomData<&'b ()>, -} - -/// A vector of invalidations, optimized for small invalidation sets. -pub type InvalidationVector<'a> = SmallVec<[Invalidation<'a>; 10]>; - -/// The kind of descendant invalidation we're processing. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum DescendantInvalidationKind { - /// A DOM descendant invalidation. - Dom, - /// A ::slotted() descendant invalidation. - Slotted, - /// A ::part() descendant invalidation. - Part, -} - -/// The kind of invalidation we're processing. -/// -/// We can use this to avoid pushing invalidations of the same kind to our -/// descendants or siblings. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum InvalidationKind { - Descendant(DescendantInvalidationKind), - Sibling, -} - -/// An `Invalidation` is a complex selector that describes which elements, -/// relative to a current element we are processing, must be restyled. -#[derive(Clone)] -pub struct Invalidation<'a> { - /// The dependency that generated this invalidation. - /// - /// Note that the offset inside the dependency is not really useful after - /// construction. - dependency: &'a Dependency, - /// The right shadow host from where the rule came from, if any. - /// - /// This is needed to ensure that we match the selector with the right - /// state, as whether some selectors like :host and ::part() match depends - /// on it. - scope: Option, - /// The offset of the selector pointing to a compound selector. - /// - /// This order is a "parse order" offset, that is, zero is the leftmost part - /// of the selector written as parsed / serialized. - /// - /// It is initialized from the offset from `dependency`. - offset: usize, - /// Whether the invalidation was already matched by any previous sibling or - /// ancestor. - /// - /// If this is the case, we can avoid pushing invalidations generated by - /// this one if the generated invalidation is effective for all the siblings - /// or descendants after us. - matched_by_any_previous: bool, -} - -impl<'a> Invalidation<'a> { - /// Create a new invalidation for matching a dependency. - pub fn new(dependency: &'a Dependency, scope: Option) -> Self { - debug_assert!( - dependency.selector_offset == dependency.selector.len() + 1 || - dependency.invalidation_kind() != DependencyInvalidationKind::Element, - "No point to this, if the dependency matched the element we should just invalidate it" - ); - Self { - dependency, - scope, - // + 1 to go past the combinator. - offset: dependency.selector.len() + 1 - dependency.selector_offset, - matched_by_any_previous: false, - } - } - - /// Whether this invalidation is effective for the next sibling or - /// descendant after us. - fn effective_for_next(&self) -> bool { - if self.offset == 0 { - return true; - } - - // TODO(emilio): For pseudo-elements this should be mostly false, except - // for the weird pseudos in . - // - // We should be able to do better here! - match self - .dependency - .selector - .combinator_at_parse_order(self.offset - 1) - { - Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true, - Combinator::Part | - Combinator::SlotAssignment | - Combinator::NextSibling | - Combinator::Child => false, - } - } - - fn kind(&self) -> InvalidationKind { - if self.offset == 0 { - return InvalidationKind::Descendant(DescendantInvalidationKind::Dom); - } - - match self - .dependency - .selector - .combinator_at_parse_order(self.offset - 1) - { - Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => { - InvalidationKind::Descendant(DescendantInvalidationKind::Dom) - }, - Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part), - Combinator::SlotAssignment => { - InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) - }, - Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling, - } - } -} - -impl<'a> fmt::Debug for Invalidation<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use cssparser::ToCss; - - f.write_str("Invalidation(")?; - for component in self - .dependency - .selector - .iter_raw_parse_order_from(self.offset) - { - if matches!(*component, Component::Combinator(..)) { - break; - } - component.to_css(f)?; - } - f.write_char(')') - } -} - -/// The result of processing a single invalidation for a given element. -struct SingleInvalidationResult { - /// Whether the element itself was invalidated. - invalidated_self: bool, - /// Whether the invalidation matched, either invalidating the element or - /// generating another invalidation. - matched: bool, -} - -/// The result of a whole invalidation process for a given element. -pub struct InvalidationResult { - /// Whether the element itself was invalidated. - invalidated_self: bool, - /// Whether the element's descendants were invalidated. - invalidated_descendants: bool, - /// Whether the element's siblings were invalidated. - invalidated_siblings: bool, -} - -impl InvalidationResult { - /// Create an emtpy result. - pub fn empty() -> Self { - Self { - invalidated_self: false, - invalidated_descendants: false, - invalidated_siblings: false, - } - } - - /// Whether the invalidation has invalidate the element itself. - pub fn has_invalidated_self(&self) -> bool { - self.invalidated_self - } - - /// Whether the invalidation has invalidate desendants. - pub fn has_invalidated_descendants(&self) -> bool { - self.invalidated_descendants - } - - /// Whether the invalidation has invalidate siblings. - pub fn has_invalidated_siblings(&self) -> bool { - self.invalidated_siblings - } -} - -impl<'a, 'b, E, P: 'a> TreeStyleInvalidator<'a, 'b, E, P> -where - 'b: 'a, - E: TElement, - P: InvalidationProcessor<'b, E>, -{ - /// Trivially constructs a new `TreeStyleInvalidator`. - pub fn new( - element: E, - stack_limit_checker: Option<&'a StackLimitChecker>, - processor: &'a mut P, - ) -> Self { - Self { - element, - stack_limit_checker, - processor, - _marker: ::std::marker::PhantomData, - } - } - - /// Perform the invalidation pass. - pub fn invalidate(mut self) -> InvalidationResult { - debug!("StyleTreeInvalidator::invalidate({:?})", self.element); - - let mut self_invalidations = InvalidationVector::new(); - let mut descendant_invalidations = DescendantInvalidationLists::default(); - let mut sibling_invalidations = InvalidationVector::new(); - - let mut invalidated_self = self.processor.collect_invalidations( - self.element, - &mut self_invalidations, - &mut descendant_invalidations, - &mut sibling_invalidations, - ); - - debug!("Collected invalidations (self: {}): ", invalidated_self); - debug!( - " > self: {}, {:?}", - self_invalidations.len(), - self_invalidations - ); - debug!(" > descendants: {:?}", descendant_invalidations); - debug!( - " > siblings: {}, {:?}", - sibling_invalidations.len(), - sibling_invalidations - ); - - let invalidated_self_from_collection = invalidated_self; - - invalidated_self |= self.process_descendant_invalidations( - &self_invalidations, - &mut descendant_invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Dom, - ); - - if invalidated_self && !invalidated_self_from_collection { - self.processor.invalidated_self(self.element); - } - - let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations); - let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations); - - InvalidationResult { - invalidated_self, - invalidated_descendants, - invalidated_siblings, - } - } - - /// Go through later DOM siblings, invalidating style as needed using the - /// `sibling_invalidations` list. - /// - /// Returns whether any sibling's style or any sibling descendant's style - /// was invalidated. - fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool { - if sibling_invalidations.is_empty() { - return false; - } - - let mut current = self.element.next_sibling_element(); - let mut any_invalidated = false; - - while let Some(sibling) = current { - let mut sibling_invalidator = - TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor); - - let mut invalidations_for_descendants = DescendantInvalidationLists::default(); - let invalidated_sibling = sibling_invalidator.process_sibling_invalidations( - &mut invalidations_for_descendants, - sibling_invalidations, - ); - - if invalidated_sibling { - sibling_invalidator - .processor - .invalidated_sibling(sibling, self.element); - } - - any_invalidated |= invalidated_sibling; - - any_invalidated |= - sibling_invalidator.invalidate_descendants(&invalidations_for_descendants); - - if sibling_invalidations.is_empty() { - break; - } - - current = sibling.next_sibling_element(); - } - - any_invalidated - } - - fn invalidate_pseudo_element_or_nac( - &mut self, - child: E, - invalidations: &[Invalidation<'b>], - ) -> bool { - let mut sibling_invalidations = InvalidationVector::new(); - - let result = self.invalidate_child( - child, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Dom, - ); - - // Roots of NAC subtrees can indeed generate sibling invalidations, but - // they can be just ignored, since they have no siblings. - // - // Note that we can end up testing selectors that wouldn't end up - // matching due to this being NAC, like those coming from document - // rules, but we overinvalidate instead of checking this. - - result - } - - /// Invalidate a child and recurse down invalidating its descendants if - /// needed. - fn invalidate_child( - &mut self, - child: E, - invalidations: &[Invalidation<'b>], - sibling_invalidations: &mut InvalidationVector<'b>, - descendant_invalidation_kind: DescendantInvalidationKind, - ) -> bool { - let mut invalidations_for_descendants = DescendantInvalidationLists::default(); - - let mut invalidated_child = false; - let invalidated_descendants = { - let mut child_invalidator = - TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor); - - invalidated_child |= child_invalidator.process_sibling_invalidations( - &mut invalidations_for_descendants, - sibling_invalidations, - ); - - invalidated_child |= child_invalidator.process_descendant_invalidations( - invalidations, - &mut invalidations_for_descendants, - sibling_invalidations, - descendant_invalidation_kind, - ); - - if invalidated_child { - child_invalidator.processor.invalidated_self(child); - } - - child_invalidator.invalidate_descendants(&invalidations_for_descendants) - }; - - // The child may not be a flattened tree child of the current element, - // but may be arbitrarily deep. - // - // Since we keep the traversal flags in terms of the flattened tree, - // we need to propagate it as appropriate. - if invalidated_child || invalidated_descendants { - self.processor.invalidated_descendants(self.element, child); - } - - invalidated_child || invalidated_descendants - } - - fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool { - let mut any_nac_root = false; - - let element = self.element; - element.each_anonymous_content_child(|nac| { - any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations); - }); - - any_nac_root - } - - // NB: It's important that this operates on DOM children, which is what - // selector-matching operates on. - fn invalidate_dom_descendants_of( - &mut self, - parent: E::ConcreteNode, - invalidations: &[Invalidation<'b>], - ) -> bool { - let mut any_descendant = false; - - let mut sibling_invalidations = InvalidationVector::new(); - for child in parent.dom_children() { - let child = match child.as_element() { - Some(e) => e, - None => continue, - }; - - any_descendant |= self.invalidate_child( - child, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Dom, - ); - } - - any_descendant - } - - fn invalidate_parts_in_shadow_tree( - &mut self, - shadow: ::ConcreteShadowRoot, - invalidations: &[Invalidation<'b>], - ) -> bool { - debug_assert!(!invalidations.is_empty()); - - let mut any = false; - let mut sibling_invalidations = InvalidationVector::new(); - - for node in shadow.as_node().dom_descendants() { - let element = match node.as_element() { - Some(e) => e, - None => continue, - }; - - if element.has_part_attr() { - any |= self.invalidate_child( - element, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Part, - ); - debug_assert!( - sibling_invalidations.is_empty(), - "::part() shouldn't have sibling combinators to the right, \ - this makes no sense! {:?}", - sibling_invalidations - ); - } - - if let Some(shadow) = element.shadow_root() { - if element.exports_any_part() { - any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations) - } - } - } - - any - } - - fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool { - if invalidations.is_empty() { - return false; - } - - let shadow = match self.element.shadow_root() { - Some(s) => s, - None => return false, - }; - - self.invalidate_parts_in_shadow_tree(shadow, invalidations) - } - - fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool { - if invalidations.is_empty() { - return false; - } - - let slot = self.element; - self.invalidate_slotted_elements_in_slot(slot, invalidations) - } - - fn invalidate_slotted_elements_in_slot( - &mut self, - slot: E, - invalidations: &[Invalidation<'b>], - ) -> bool { - let mut any = false; - - let mut sibling_invalidations = InvalidationVector::new(); - for node in slot.slotted_nodes() { - let element = match node.as_element() { - Some(e) => e, - None => continue, - }; - - if element.is_html_slot_element() { - any |= self.invalidate_slotted_elements_in_slot(element, invalidations); - } else { - any |= self.invalidate_child( - element, - invalidations, - &mut sibling_invalidations, - DescendantInvalidationKind::Slotted, - ); - } - - debug_assert!( - sibling_invalidations.is_empty(), - "::slotted() shouldn't have sibling combinators to the right, \ - this makes no sense! {:?}", - sibling_invalidations - ); - } - - any - } - - fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool { - if invalidations.is_empty() { - return false; - } - - if self.processor.light_tree_only() { - let node = self.element.as_node(); - return self.invalidate_dom_descendants_of(node, invalidations); - } - - let mut any_descendant = false; - - // NOTE(emilio): This is only needed for Shadow DOM to invalidate - // correctly on :host(..) changes. Instead of doing this, we could add - // a third kind of invalidation list that walks shadow root children, - // but it's not clear it's worth it. - // - // Also, it's needed as of right now for document state invalidation, - // where we rely on iterating every element that ends up in the composed - // doc, but we could fix that invalidating per subtree. - if let Some(root) = self.element.shadow_root() { - any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations); - } - - if let Some(marker) = self.element.marker_pseudo_element() { - any_descendant |= self.invalidate_pseudo_element_or_nac(marker, invalidations); - } - - if let Some(before) = self.element.before_pseudo_element() { - any_descendant |= self.invalidate_pseudo_element_or_nac(before, invalidations); - } - - let node = self.element.as_node(); - any_descendant |= self.invalidate_dom_descendants_of(node, invalidations); - - if let Some(after) = self.element.after_pseudo_element() { - any_descendant |= self.invalidate_pseudo_element_or_nac(after, invalidations); - } - - any_descendant |= self.invalidate_nac(invalidations); - - any_descendant - } - - /// Given the descendant invalidation lists, go through the current - /// element's descendants, and invalidate style on them. - fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool { - if invalidations.is_empty() { - return false; - } - - debug!( - "StyleTreeInvalidator::invalidate_descendants({:?})", - self.element - ); - debug!(" > {:?}", invalidations); - - let should_process = self.processor.should_process_descendants(self.element); - - if !should_process { - return false; - } - - if let Some(checker) = self.stack_limit_checker { - if checker.limit_exceeded() { - self.processor.recursion_limit_exceeded(self.element); - return true; - } - } - - let mut any_descendant = false; - - any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants); - any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants); - any_descendant |= self.invalidate_parts(&invalidations.parts); - - any_descendant - } - - /// Process the given sibling invalidations coming from our previous - /// sibling. - /// - /// The sibling invalidations are somewhat special because they can be - /// modified on the fly. New invalidations may be added and removed. - /// - /// In particular, all descendants get the same set of invalidations from - /// the parent, but the invalidations from a given sibling depend on the - /// ones we got from the previous one. - /// - /// Returns whether invalidated the current element's style. - fn process_sibling_invalidations( - &mut self, - descendant_invalidations: &mut DescendantInvalidationLists<'b>, - sibling_invalidations: &mut InvalidationVector<'b>, - ) -> bool { - let mut i = 0; - let mut new_sibling_invalidations = InvalidationVector::new(); - let mut invalidated_self = false; - - while i < sibling_invalidations.len() { - let result = self.process_invalidation( - &sibling_invalidations[i], - descendant_invalidations, - &mut new_sibling_invalidations, - InvalidationKind::Sibling, - ); - - invalidated_self |= result.invalidated_self; - sibling_invalidations[i].matched_by_any_previous |= result.matched; - if sibling_invalidations[i].effective_for_next() { - i += 1; - } else { - sibling_invalidations.remove(i); - } - } - - sibling_invalidations.extend(new_sibling_invalidations.drain(..)); - invalidated_self - } - - /// Process a given invalidation list coming from our parent, - /// adding to `descendant_invalidations` and `sibling_invalidations` as - /// needed. - /// - /// Returns whether our style was invalidated as a result. - fn process_descendant_invalidations( - &mut self, - invalidations: &[Invalidation<'b>], - descendant_invalidations: &mut DescendantInvalidationLists<'b>, - sibling_invalidations: &mut InvalidationVector<'b>, - descendant_invalidation_kind: DescendantInvalidationKind, - ) -> bool { - let mut invalidated = false; - - for invalidation in invalidations { - let result = self.process_invalidation( - invalidation, - descendant_invalidations, - sibling_invalidations, - InvalidationKind::Descendant(descendant_invalidation_kind), - ); - - invalidated |= result.invalidated_self; - if invalidation.effective_for_next() { - let mut invalidation = invalidation.clone(); - invalidation.matched_by_any_previous |= result.matched; - debug_assert_eq!( - descendant_invalidation_kind, - DescendantInvalidationKind::Dom, - "Slotted or part invalidations don't propagate." - ); - descendant_invalidations.dom_descendants.push(invalidation); - } - } - - invalidated - } - - /// Processes a given invalidation, potentially invalidating the style of - /// the current element. - /// - /// Returns whether invalidated the style of the element, and whether the - /// invalidation should be effective to subsequent siblings or descendants - /// down in the tree. - fn process_invalidation( - &mut self, - invalidation: &Invalidation<'b>, - descendant_invalidations: &mut DescendantInvalidationLists<'b>, - sibling_invalidations: &mut InvalidationVector<'b>, - invalidation_kind: InvalidationKind, - ) -> SingleInvalidationResult { - debug!( - "TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})", - self.element, invalidation, invalidation_kind - ); - - let matching_result = { - let context = self.processor.matching_context(); - context.current_host = invalidation.scope; - - matches_compound_selector_from( - &invalidation.dependency.selector, - invalidation.offset, - context, - &self.element, - ) - }; - - let next_invalidation = match matching_result { - CompoundSelectorMatchingResult::NotMatched => { - return SingleInvalidationResult { - invalidated_self: false, - matched: false, - } - }, - CompoundSelectorMatchingResult::FullyMatched => { - debug!(" > Invalidation matched completely"); - // We matched completely. If we're an inner selector now we need - // to go outside our selector and carry on invalidating. - let mut cur_dependency = invalidation.dependency; - loop { - cur_dependency = match cur_dependency.parent { - None => { - return SingleInvalidationResult { - invalidated_self: true, - matched: true, - } - }, - Some(ref p) => &**p, - }; - - debug!(" > Checking outer dependency {:?}", cur_dependency); - - // The inner selector changed, now check if the full - // previous part of the selector did, before keeping - // checking for descendants. - if !self - .processor - .check_outer_dependency(cur_dependency, self.element) - { - return SingleInvalidationResult { - invalidated_self: false, - matched: false, - }; - } - - if cur_dependency.invalidation_kind() == DependencyInvalidationKind::Element { - continue; - } - - debug!(" > Generating invalidation"); - break Invalidation::new(cur_dependency, invalidation.scope); - } - }, - CompoundSelectorMatchingResult::Matched { - next_combinator_offset, - } => Invalidation { - dependency: invalidation.dependency, - scope: invalidation.scope, - offset: next_combinator_offset + 1, - matched_by_any_previous: false, - }, - }; - - debug_assert_ne!( - next_invalidation.offset, 0, - "Rightmost selectors shouldn't generate more invalidations", - ); - - let mut invalidated_self = false; - let next_combinator = next_invalidation - .dependency - .selector - .combinator_at_parse_order(next_invalidation.offset - 1); - - if matches!(next_combinator, Combinator::PseudoElement) && - self.processor.invalidates_on_pseudo_element() - { - // We need to invalidate the element whenever pseudos change, for - // two reasons: - // - // * Eager pseudo styles are stored as part of the originating - // element's computed style. - // - // * Lazy pseudo-styles might be cached on the originating - // element's pseudo-style cache. - // - // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS - // hint?). - // - // Note that we'll also restyle the pseudo-element because it would - // match this invalidation. - // - // FIXME: For non-element-backed pseudos this is still not quite - // correct. For example for ::selection even though we invalidate - // the style properly there's nothing that triggers a repaint - // necessarily. Though this matches old Gecko behavior, and the - // ::selection implementation needs to change significantly anyway - // to implement https://github.com/w3c/csswg-drafts/issues/2474 for - // example. - invalidated_self = true; - } - - debug!( - " > Invalidation matched, next: {:?}, ({:?})", - next_invalidation, next_combinator - ); - - let next_invalidation_kind = next_invalidation.kind(); - - // We can skip pushing under some circumstances, and we should - // because otherwise the invalidation list could grow - // exponentially. - // - // * First of all, both invalidations need to be of the same - // kind. This is because of how we propagate them going to - // the right of the tree for sibling invalidations and going - // down the tree for children invalidations. A sibling - // invalidation that ends up generating a children - // invalidation ends up (correctly) in five different lists, - // not in the same list five different times. - // - // * Then, the invalidation needs to be matched by a previous - // ancestor/sibling, in order to know that this invalidation - // has been generated already. - // - // * Finally, the new invalidation needs to be - // `effective_for_next()`, in order for us to know that it is - // still in the list, since we remove the dependencies that - // aren't from the lists for our children / siblings. - // - // To go through an example, let's imagine we are processing a - // dom subtree like: - // - //
- // - // And an invalidation list with a single invalidation like: - // - // [div div div] - // - // When we process the invalidation list for the outer div, we - // match it, and generate a `div div` invalidation, so for the - //
child we have: - // - // [div div div, div div] - // - // With the first of them marked as `matched`. - // - // When we process the
child, we don't match any of - // them, so both invalidations go untouched to our children. - // - // When we process the second
, we match _both_ - // invalidations. - // - // However, when matching the first, we can tell it's been - // matched, and not push the corresponding `div div` - // invalidation, since we know it's necessarily already on the - // list. - // - // Thus, without skipping the push, we'll arrive to the - // innermost
with: - // - // [div div div, div div, div div, div] - // - // While skipping it, we won't arrive here with duplicating - // dependencies: - // - // [div div div, div div, div] - // - let can_skip_pushing = next_invalidation_kind == invalidation_kind && - invalidation.matched_by_any_previous && - next_invalidation.effective_for_next(); - - if can_skip_pushing { - debug!( - " > Can avoid push, since the invalidation had \ - already been matched before" - ); - } else { - match next_invalidation_kind { - InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => { - descendant_invalidations - .dom_descendants - .push(next_invalidation); - }, - InvalidationKind::Descendant(DescendantInvalidationKind::Part) => { - descendant_invalidations.parts.push(next_invalidation); - }, - InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => { - descendant_invalidations - .slotted_descendants - .push(next_invalidation); - }, - InvalidationKind::Sibling => { - sibling_invalidations.push(next_invalidation); - }, - } - } - - SingleInvalidationResult { - invalidated_self, - matched: true, - } - } -} diff --git a/components/style/invalidation/element/mod.rs b/components/style/invalidation/element/mod.rs deleted file mode 100644 index 1f19cc54f59..00000000000 --- a/components/style/invalidation/element/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Invalidation of element styles due to attribute or style changes. - -pub mod document_state; -pub mod element_wrapper; -pub mod invalidation_map; -pub mod invalidator; -pub mod restyle_hints; -pub mod state_and_attributes; diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs deleted file mode 100644 index ffadecd7aa2..00000000000 --- a/components/style/invalidation/element/restyle_hints.rs +++ /dev/null @@ -1,190 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Restyle hints: an optimization to avoid unnecessarily matching selectors. - -use crate::traversal_flags::TraversalFlags; - -bitflags! { - /// The kind of restyle we need to do for a given element. - #[repr(C)] - pub struct RestyleHint: u16 { - /// Do a selector match of the element. - const RESTYLE_SELF = 1 << 0; - - /// Do a selector match of the element's pseudo-elements. Always to be combined with - /// RESTYLE_SELF. - const RESTYLE_PSEUDOS = 1 << 1; - - /// Do a selector match if the element is a pseudo-element. - const RESTYLE_SELF_IF_PSEUDO = 1 << 2; - - /// Do a selector match of the element's descendants. - const RESTYLE_DESCENDANTS = 1 << 3; - - /// Recascade the current element. - const RECASCADE_SELF = 1 << 4; - - /// Recascade the current element if it inherits any reset style. - const RECASCADE_SELF_IF_INHERIT_RESET_STYLE = 1 << 5; - - /// Recascade all descendant elements. - const RECASCADE_DESCENDANTS = 1 << 6; - - /// Replace the style data coming from CSS transitions without updating - /// any other style data. This hint is only processed in animation-only - /// traversal which is prior to normal traversal. - const RESTYLE_CSS_TRANSITIONS = 1 << 7; - - /// Replace the style data coming from CSS animations without updating - /// any other style data. This hint is only processed in animation-only - /// traversal which is prior to normal traversal. - const RESTYLE_CSS_ANIMATIONS = 1 << 8; - - /// Don't re-run selector-matching on the element, only the style - /// attribute has changed, and this change didn't have any other - /// dependencies. - const RESTYLE_STYLE_ATTRIBUTE = 1 << 9; - - /// Replace the style data coming from SMIL animations without updating - /// any other style data. This hint is only processed in animation-only - /// traversal which is prior to normal traversal. - const RESTYLE_SMIL = 1 << 10; - } -} - -impl RestyleHint { - /// Creates a new `RestyleHint` indicating that the current element and all - /// its descendants must be fully restyled. - pub fn restyle_subtree() -> Self { - RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS - } - - /// Creates a new `RestyleHint` indicating that the current element and all - /// its descendants must be recascaded. - pub fn recascade_subtree() -> Self { - RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS - } - - /// Returns whether this hint invalidates the element and all its - /// descendants. - pub fn contains_subtree(&self) -> bool { - self.contains(Self::restyle_subtree()) - } - - /// Returns whether we'll recascade all of the descendants. - pub fn will_recascade_subtree(&self) -> bool { - self.contains_subtree() || self.contains(Self::recascade_subtree()) - } - - /// Returns whether we need to restyle this element. - pub fn has_non_animation_invalidations(&self) -> bool { - !(*self & !Self::for_animations()).is_empty() - } - - /// Propagates this restyle hint to a child element. - pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self { - use std::mem; - - // In the middle of an animation only restyle, we don't need to - // propagate any restyle hints, and we need to remove ourselves. - if traversal_flags.for_animation_only() { - self.remove_animation_hints(); - return Self::empty(); - } - - debug_assert!( - !self.has_animation_hint(), - "There should not be any animation restyle hints \ - during normal traversal" - ); - - // Else we should clear ourselves, and return the propagated hint. - mem::replace(self, Self::empty()).propagate_for_non_animation_restyle() - } - - /// Returns a new `RestyleHint` appropriate for children of the current element. - fn propagate_for_non_animation_restyle(&self) -> Self { - if self.contains(RestyleHint::RESTYLE_DESCENDANTS) { - return Self::restyle_subtree(); - } - let mut result = Self::empty(); - if self.contains(RestyleHint::RESTYLE_PSEUDOS) { - result |= Self::RESTYLE_SELF_IF_PSEUDO; - } - if self.contains(RestyleHint::RECASCADE_DESCENDANTS) { - result |= Self::recascade_subtree(); - } - result - } - - /// Returns a hint that contains all the replacement hints. - pub fn replacements() -> Self { - RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations() - } - - /// The replacements for the animation cascade levels. - #[inline] - pub fn for_animations() -> Self { - RestyleHint::RESTYLE_SMIL | - RestyleHint::RESTYLE_CSS_ANIMATIONS | - RestyleHint::RESTYLE_CSS_TRANSITIONS - } - - /// Returns whether the hint specifies that an animation cascade level must - /// be replaced. - #[inline] - pub fn has_animation_hint(&self) -> bool { - self.intersects(Self::for_animations()) - } - - /// Returns whether the hint specifies that an animation cascade level must - /// be replaced. - #[inline] - pub fn has_animation_hint_or_recascade(&self) -> bool { - self.intersects( - Self::for_animations() | - Self::RECASCADE_SELF | - Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE, - ) - } - - /// Returns whether the hint specifies some restyle work other than an - /// animation cascade level replacement. - #[inline] - pub fn has_non_animation_hint(&self) -> bool { - !(*self & !Self::for_animations()).is_empty() - } - - /// Returns whether the hint specifies that some cascade levels must be - /// replaced. - #[inline] - pub fn has_replacements(&self) -> bool { - self.intersects(Self::replacements()) - } - - /// Removes all of the animation-related hints. - #[inline] - pub fn remove_animation_hints(&mut self) { - self.remove(Self::for_animations()); - - // While RECASCADE_SELF is not animation-specific, we only ever add and process it during - // traversal. If we are here, removing animation hints, then we are in an animation-only - // traversal, and we know that any RECASCADE_SELF flag must have been set due to changes in - // inherited values after restyling for animations, and thus we want to remove it so that - // we don't later try to restyle the element during a normal restyle. - // (We could have separate RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to - // make it clear, but this isn't currently necessary.) - self.remove(Self::RECASCADE_SELF | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE); - } -} - -impl Default for RestyleHint { - fn default() -> Self { - Self::empty() - } -} - -#[cfg(feature = "servo")] -malloc_size_of_is_0!(RestyleHint); diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs deleted file mode 100644 index bd69f35c66c..00000000000 --- a/components/style/invalidation/element/state_and_attributes.rs +++ /dev/null @@ -1,552 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! An invalidation processor for style changes due to state and attribute -//! changes. - -use crate::context::SharedStyleContext; -use crate::data::ElementData; -use crate::dom::{TElement, TNode}; -use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; -use crate::invalidation::element::invalidation_map::*; -use crate::invalidation::element::invalidator::{DescendantInvalidationLists, InvalidationVector}; -use crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor}; -use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::selector_map::SelectorMap; -use crate::selector_parser::Snapshot; -use crate::stylesheets::origin::OriginSet; -use crate::{Atom, WeakAtom}; -use selectors::attr::CaseSensitivity; -use selectors::matching::{ - matches_selector, MatchingContext, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode, -}; -use selectors::NthIndexCache; -use smallvec::SmallVec; -use style_traits::dom::ElementState; - -/// The collector implementation. -struct Collector<'a, 'b: 'a, 'selectors: 'a, E> -where - E: TElement, -{ - element: E, - wrapper: ElementWrapper<'b, E>, - snapshot: &'a Snapshot, - matching_context: &'a mut MatchingContext<'b, E::Impl>, - lookup_element: E, - removed_id: Option<&'a WeakAtom>, - added_id: Option<&'a WeakAtom>, - classes_removed: &'a SmallVec<[Atom; 8]>, - classes_added: &'a SmallVec<[Atom; 8]>, - state_changes: ElementState, - descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>, - sibling_invalidations: &'a mut InvalidationVector<'selectors>, - invalidates_self: bool, -} - -/// An invalidation processor for style changes due to state and attribute -/// changes. -pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { - shared_context: &'a SharedStyleContext<'b>, - element: E, - data: &'a mut ElementData, - matching_context: MatchingContext<'a, E::Impl>, -} - -impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> { - /// Creates a new StateAndAttrInvalidationProcessor. - pub fn new( - shared_context: &'a SharedStyleContext<'b>, - element: E, - data: &'a mut ElementData, - nth_index_cache: &'a mut NthIndexCache, - ) -> Self { - let matching_context = MatchingContext::new_for_visited( - MatchingMode::Normal, - None, - nth_index_cache, - VisitedHandlingMode::AllLinksVisitedAndUnvisited, - shared_context.quirks_mode(), - NeedsSelectorFlags::No, - ); - - Self { - shared_context, - element, - data, - matching_context, - } - } -} - -/// Checks a dependency against a given element and wrapper, to see if something -/// changed. -pub fn check_dependency( - dependency: &Dependency, - element: &E, - wrapper: &W, - context: &mut MatchingContext<'_, E::Impl>, -) -> bool -where - E: TElement, - W: selectors::Element, -{ - let matches_now = matches_selector( - &dependency.selector, - dependency.selector_offset, - None, - element, - context, - ); - - let matched_then = matches_selector( - &dependency.selector, - dependency.selector_offset, - None, - wrapper, - context, - ); - - matched_then != matches_now -} - -/// Whether we should process the descendants of a given element for style -/// invalidation. -pub fn should_process_descendants(data: &ElementData) -> bool { - !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS) -} - -/// Propagates the bits after invalidating a descendant child. -pub fn propagate_dirty_bit_up_to(ancestor: E, child: E) -where - E: TElement, -{ - // The child may not be a flattened tree child of the current element, - // but may be arbitrarily deep. - // - // Since we keep the traversal flags in terms of the flattened tree, - // we need to propagate it as appropriate. - let mut current = child.traversal_parent(); - while let Some(parent) = current.take() { - unsafe { parent.set_dirty_descendants() }; - current = parent.traversal_parent(); - - if parent == ancestor { - return; - } - } - debug_assert!( - false, - "Should've found {:?} as an ancestor of {:?}", - ancestor, child - ); -} - -/// Propagates the bits after invalidating a descendant child, if needed. -pub fn invalidated_descendants(element: E, child: E) -where - E: TElement, -{ - if !child.has_data() { - return; - } - propagate_dirty_bit_up_to(element, child) -} - -/// Sets the appropriate restyle hint after invalidating the style of a given -/// element. -pub fn invalidated_self(element: E) -> bool -where - E: TElement, -{ - let mut data = match element.mutate_data() { - Some(data) => data, - None => return false, - }; - data.hint.insert(RestyleHint::RESTYLE_SELF); - true -} - -/// Sets the appropriate hint after invalidating the style of a sibling. -pub fn invalidated_sibling(element: E, of: E) -where - E: TElement, -{ - debug_assert_eq!( - element.as_node().parent_node(), - of.as_node().parent_node(), - "Should be siblings" - ); - if !invalidated_self(element) { - return; - } - if element.traversal_parent() != of.traversal_parent() { - let parent = element.as_node().parent_element_or_host(); - debug_assert!( - parent.is_some(), - "How can we have siblings without parent nodes?" - ); - if let Some(e) = parent { - propagate_dirty_bit_up_to(e, element) - } - } -} - -impl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, E> - for StateAndAttrInvalidationProcessor<'a, 'b, E> -where - E: TElement, -{ - /// We need to invalidate style on pseudo-elements, in order to process - /// changes that could otherwise end up in ::before or ::after content being - /// generated, and invalidate lazy pseudo caches. - fn invalidates_on_pseudo_element(&self) -> bool { - true - } - - fn check_outer_dependency(&mut self, dependency: &Dependency, element: E) -> bool { - // We cannot assert about `element` having a snapshot here (in fact it - // most likely won't), because it may be an arbitrary descendant or - // later-sibling of the element we started invalidating with. - let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map); - check_dependency(dependency, &element, &wrapper, &mut self.matching_context) - } - - fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> { - &mut self.matching_context - } - - fn collect_invalidations( - &mut self, - element: E, - _self_invalidations: &mut InvalidationVector<'a>, - descendant_invalidations: &mut DescendantInvalidationLists<'a>, - sibling_invalidations: &mut InvalidationVector<'a>, - ) -> bool { - debug_assert_eq!(element, self.element); - debug_assert!(element.has_snapshot(), "Why bothering?"); - - let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map); - - let state_changes = wrapper.state_changes(); - let snapshot = wrapper.snapshot().expect("has_snapshot lied"); - - if !snapshot.has_attrs() && state_changes.is_empty() { - return false; - } - - let mut classes_removed = SmallVec::<[Atom; 8]>::new(); - let mut classes_added = SmallVec::<[Atom; 8]>::new(); - if snapshot.class_changed() { - // TODO(emilio): Do this more efficiently! - snapshot.each_class(|c| { - if !element.has_class(c, CaseSensitivity::CaseSensitive) { - classes_removed.push(c.0.clone()) - } - }); - - element.each_class(|c| { - if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) { - classes_added.push(c.0.clone()) - } - }) - } - - let mut id_removed = None; - let mut id_added = None; - if snapshot.id_changed() { - let old_id = snapshot.id_attr(); - let current_id = element.id(); - - if old_id != current_id { - id_removed = old_id; - id_added = current_id; - } - } - - if log_enabled!(::log::Level::Debug) { - debug!("Collecting changes for: {:?}", element); - if !state_changes.is_empty() { - debug!(" > state: {:?}", state_changes); - } - if snapshot.id_changed() { - debug!(" > id changed: +{:?} -{:?}", id_added, id_removed); - } - if snapshot.class_changed() { - debug!( - " > class changed: +{:?} -{:?}", - classes_added, classes_removed - ); - } - let mut attributes_changed = false; - snapshot.each_attr_changed(|_| { - attributes_changed = true; - }); - if attributes_changed { - debug!( - " > attributes changed, old: {}", - snapshot.debug_list_attributes() - ) - } - } - - let lookup_element = if element.implemented_pseudo_element().is_some() { - element.pseudo_element_originating_element().unwrap() - } else { - element - }; - - let mut shadow_rule_datas = SmallVec::<[_; 3]>::new(); - let matches_document_author_rules = - element.each_applicable_non_document_style_rule_data(|data, host| { - shadow_rule_datas.push((data, host.opaque())) - }); - - let invalidated_self = { - let mut collector = Collector { - wrapper, - lookup_element, - state_changes, - element, - snapshot: &snapshot, - matching_context: &mut self.matching_context, - removed_id: id_removed, - added_id: id_added, - classes_removed: &classes_removed, - classes_added: &classes_added, - descendant_invalidations, - sibling_invalidations, - invalidates_self: false, - }; - - let document_origins = if !matches_document_author_rules { - OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER - } else { - OriginSet::all() - }; - - for (cascade_data, origin) in self.shared_context.stylist.iter_origins() { - if document_origins.contains(origin.into()) { - collector - .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map()); - } - } - - for &(ref data, ref host) in &shadow_rule_datas { - collector.matching_context.current_host = Some(host.clone()); - collector.collect_dependencies_in_invalidation_map(data.invalidation_map()); - } - - collector.invalidates_self - }; - - // If we generated a ton of descendant invalidations, it's probably not - // worth to go ahead and try to process them. - // - // Just restyle the descendants directly. - // - // This number is completely made-up, but the page that made us add this - // code generated 1960+ invalidations (bug 1420741). - // - // We don't look at slotted_descendants because those don't propagate - // down more than one level anyway. - if descendant_invalidations.dom_descendants.len() > 150 { - self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); - } - - if invalidated_self { - self.data.hint.insert(RestyleHint::RESTYLE_SELF); - } - - invalidated_self - } - - fn should_process_descendants(&mut self, element: E) -> bool { - if element == self.element { - return should_process_descendants(&self.data); - } - - match element.borrow_data() { - Some(d) => should_process_descendants(&d), - None => return false, - } - } - - fn recursion_limit_exceeded(&mut self, element: E) { - if element == self.element { - self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); - return; - } - - if let Some(mut data) = element.mutate_data() { - data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS); - } - } - - fn invalidated_descendants(&mut self, element: E, child: E) { - invalidated_descendants(element, child) - } - - fn invalidated_self(&mut self, element: E) { - debug_assert_ne!(element, self.element); - invalidated_self(element); - } - - fn invalidated_sibling(&mut self, element: E, of: E) { - debug_assert_ne!(element, self.element); - invalidated_sibling(element, of); - } -} - -impl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E> -where - E: TElement, - 'selectors: 'a, -{ - fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) { - let quirks_mode = self.matching_context.quirks_mode(); - let removed_id = self.removed_id; - if let Some(ref id) = removed_id { - if let Some(deps) = map.id_to_selector.get(id, quirks_mode) { - for dep in deps { - self.scan_dependency(dep); - } - } - } - - let added_id = self.added_id; - if let Some(ref id) = added_id { - if let Some(deps) = map.id_to_selector.get(id, quirks_mode) { - for dep in deps { - self.scan_dependency(dep); - } - } - } - - for class in self.classes_added.iter().chain(self.classes_removed.iter()) { - if let Some(deps) = map.class_to_selector.get(class, quirks_mode) { - for dep in deps { - self.scan_dependency(dep); - } - } - } - - self.snapshot.each_attr_changed(|attribute| { - if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) { - for dep in deps { - self.scan_dependency(dep); - } - } - }); - - self.collect_state_dependencies(&map.state_affecting_selectors) - } - - fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap) { - if self.state_changes.is_empty() { - return; - } - map.lookup_with_additional( - self.lookup_element, - self.matching_context.quirks_mode(), - self.removed_id, - self.classes_removed, - self.state_changes, - |dependency| { - if !dependency.state.intersects(self.state_changes) { - return true; - } - self.scan_dependency(&dependency.dep); - true - }, - ); - } - - /// Check whether a dependency should be taken into account. - #[inline] - fn check_dependency(&mut self, dependency: &Dependency) -> bool { - check_dependency( - dependency, - &self.element, - &self.wrapper, - &mut self.matching_context, - ) - } - - fn scan_dependency(&mut self, dependency: &'selectors Dependency) { - debug!( - "TreeStyleInvalidator::scan_dependency({:?}, {:?})", - self.element, dependency - ); - - if !self.dependency_may_be_relevant(dependency) { - return; - } - - if self.check_dependency(dependency) { - return self.note_dependency(dependency); - } - } - - fn note_dependency(&mut self, dependency: &'selectors Dependency) { - debug_assert!(self.dependency_may_be_relevant(dependency)); - - let invalidation_kind = dependency.invalidation_kind(); - if matches!(invalidation_kind, DependencyInvalidationKind::Element) { - if let Some(ref parent) = dependency.parent { - // We know something changed in the inner selector, go outwards - // now. - self.scan_dependency(parent); - } else { - self.invalidates_self = true; - } - return; - } - - debug_assert_ne!(dependency.selector_offset, 0); - debug_assert_ne!(dependency.selector_offset, dependency.selector.len()); - - let invalidation = - Invalidation::new(&dependency, self.matching_context.current_host.clone()); - - match invalidation_kind { - DependencyInvalidationKind::Element => unreachable!(), - DependencyInvalidationKind::ElementAndDescendants => { - self.invalidates_self = true; - self.descendant_invalidations - .dom_descendants - .push(invalidation); - }, - DependencyInvalidationKind::Descendants => { - self.descendant_invalidations - .dom_descendants - .push(invalidation); - }, - DependencyInvalidationKind::Siblings => { - self.sibling_invalidations.push(invalidation); - }, - DependencyInvalidationKind::Parts => { - self.descendant_invalidations.parts.push(invalidation); - }, - DependencyInvalidationKind::SlottedElements => { - self.descendant_invalidations - .slotted_descendants - .push(invalidation); - }, - } - } - - /// Returns whether `dependency` may cause us to invalidate the style of - /// more elements than what we've already invalidated. - fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool { - match dependency.invalidation_kind() { - DependencyInvalidationKind::Element => !self.invalidates_self, - DependencyInvalidationKind::SlottedElements => self.element.is_html_slot_element(), - DependencyInvalidationKind::Parts => self.element.shadow_root().is_some(), - DependencyInvalidationKind::ElementAndDescendants | - DependencyInvalidationKind::Siblings | - DependencyInvalidationKind::Descendants => true, - } - } -} diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs deleted file mode 100644 index 6928b29d3d9..00000000000 --- a/components/style/invalidation/media_queries.rs +++ /dev/null @@ -1,130 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Code related to the invalidation of media-query-affected rules. - -use crate::context::QuirksMode; -use crate::media_queries::Device; -use crate::shared_lock::SharedRwLockReadGuard; -use crate::stylesheets::{DocumentRule, ImportRule, MediaRule}; -use crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule}; -use fxhash::FxHashSet; - -/// A key for a given media query result. -/// -/// NOTE: It happens to be the case that all the media lists we care about -/// happen to have a stable address, so we can just use an opaque pointer to -/// represent them. -/// -/// Also, note that right now when a rule or stylesheet is removed, we do a full -/// style flush, so there's no need to worry about other item created with the -/// same pointer address. -/// -/// If this changes, though, we may need to remove the item from the cache if -/// present before it goes away. -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] -pub struct MediaListKey(usize); - -impl MediaListKey { - /// Create a MediaListKey from a raw usize. - pub fn from_raw(k: usize) -> Self { - MediaListKey(k) - } -} - -/// A trait to get a given `MediaListKey` for a given item that can hold a -/// `MediaList`. -pub trait ToMediaListKey: Sized { - /// Get a `MediaListKey` for this item. This key needs to uniquely identify - /// the item. - fn to_media_list_key(&self) -> MediaListKey { - MediaListKey(self as *const Self as usize) - } -} - -impl ToMediaListKey for StylesheetContents {} -impl ToMediaListKey for ImportRule {} -impl ToMediaListKey for MediaRule {} - -/// A struct that holds the result of a media query evaluation pass for the -/// media queries that evaluated successfully. -#[derive(Clone, Debug, MallocSizeOf, PartialEq)] -pub struct EffectiveMediaQueryResults { - /// The set of media lists that matched last time. - set: FxHashSet, -} - -impl EffectiveMediaQueryResults { - /// Trivially constructs an empty `EffectiveMediaQueryResults`. - pub fn new() -> Self { - Self { - set: FxHashSet::default(), - } - } - - /// Resets the results, using an empty key. - pub fn clear(&mut self) { - self.set.clear() - } - - /// Returns whether a given item was known to be effective when the results - /// were cached. - pub fn was_effective(&self, item: &T) -> bool - where - T: ToMediaListKey, - { - self.set.contains(&item.to_media_list_key()) - } - - /// Notices that an effective item has been seen, and caches it as matching. - pub fn saw_effective(&mut self, item: &T) - where - T: ToMediaListKey, - { - // NOTE(emilio): We can't assert that we don't cache the same item twice - // because of stylesheet reusing... shrug. - self.set.insert(item.to_media_list_key()); - } -} - -/// A filter that filters over effective rules, but allowing all potentially -/// effective `@media` rules. -pub struct PotentiallyEffectiveMediaRules; - -impl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules { - fn process_import( - _: &SharedRwLockReadGuard, - _: &Device, - _: QuirksMode, - _: &ImportRule, - ) -> bool { - true - } - - fn process_media(_: &SharedRwLockReadGuard, _: &Device, _: QuirksMode, _: &MediaRule) -> bool { - true - } - - /// Whether we should process the nested rules in a given `@-moz-document` rule. - fn process_document( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &DocumentRule, - ) -> bool { - use crate::stylesheets::EffectiveRules; - EffectiveRules::process_document(guard, device, quirks_mode, rule) - } - - /// Whether we should process the nested rules in a given `@supports` rule. - fn process_supports( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &SupportsRule, - ) -> bool { - use crate::stylesheets::EffectiveRules; - EffectiveRules::process_supports(guard, device, quirks_mode, rule) - } -} diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs deleted file mode 100644 index 12b0d06853b..00000000000 --- a/components/style/invalidation/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Different bits of code related to invalidating style. - -pub mod element; -pub mod media_queries; -pub mod stylesheets; -pub mod viewport_units; diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs deleted file mode 100644 index 6ae67452dbd..00000000000 --- a/components/style/invalidation/stylesheets.rs +++ /dev/null @@ -1,655 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A collection of invalidations due to changes in which stylesheets affect a -//! document. - -#![deny(unsafe_code)] - -use crate::context::QuirksMode; -use crate::dom::{TDocument, TElement, TNode}; -use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; -use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::media_queries::Device; -use crate::selector_map::{MaybeCaseInsensitiveHashMap, PrecomputedHashMap}; -use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap}; -use crate::shared_lock::SharedRwLockReadGuard; -use crate::stylesheets::{CssRule, StylesheetInDocument}; -use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator}; -use crate::values::AtomIdent; -use crate::LocalName as SelectorLocalName; -use crate::{Atom, ShrinkIfNeeded}; -use selectors::parser::{Component, LocalName, Selector}; - -/// The kind of change that happened for a given rule. -#[repr(u32)] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] -pub enum RuleChangeKind { - /// The rule was inserted. - Insertion, - /// The rule was removed. - Removal, - /// Some change in the rule which we don't know about, and could have made - /// the rule change in any way. - Generic, - /// A change in the declarations of a style rule. - StyleRuleDeclarations, -} - -/// A style sheet invalidation represents a kind of element or subtree that may -/// need to be restyled. Whether it represents a whole subtree or just a single -/// element is determined by the given InvalidationKind in -/// StylesheetInvalidationSet's maps. -#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] -enum Invalidation { - /// An element with a given id. - ID(AtomIdent), - /// An element with a given class name. - Class(AtomIdent), - /// An element with a given local name. - LocalName { - name: SelectorLocalName, - lower_name: SelectorLocalName, - }, -} - -impl Invalidation { - fn is_id(&self) -> bool { - matches!(*self, Invalidation::ID(..)) - } - - fn is_id_or_class(&self) -> bool { - matches!(*self, Invalidation::ID(..) | Invalidation::Class(..)) - } -} - -/// Whether we should invalidate just the element, or the whole subtree within -/// it. -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)] -enum InvalidationKind { - None = 0, - Element, - Scope, -} - -impl std::ops::BitOrAssign for InvalidationKind { - #[inline] - fn bitor_assign(&mut self, other: Self) { - *self = std::cmp::max(*self, other); - } -} - -impl InvalidationKind { - #[inline] - fn is_scope(self) -> bool { - matches!(self, Self::Scope) - } - - #[inline] - fn add(&mut self, other: Option<&InvalidationKind>) { - if let Some(other) = other { - *self |= *other; - } - } -} - -/// A set of invalidations due to stylesheet additions. -/// -/// TODO(emilio): We might be able to do the same analysis for media query -/// changes too (or even selector changes?). -#[derive(Debug, Default, MallocSizeOf)] -pub struct StylesheetInvalidationSet { - classes: MaybeCaseInsensitiveHashMap, - ids: MaybeCaseInsensitiveHashMap, - local_names: PrecomputedHashMap, - fully_invalid: bool, -} - -impl StylesheetInvalidationSet { - /// Create an empty `StylesheetInvalidationSet`. - pub fn new() -> Self { - Default::default() - } - - /// Mark the DOM tree styles' as fully invalid. - pub fn invalidate_fully(&mut self) { - debug!("StylesheetInvalidationSet::invalidate_fully"); - self.clear(); - self.fully_invalid = true; - } - - fn shrink_if_needed(&mut self) { - if self.fully_invalid { - return; - } - self.classes.shrink_if_needed(); - self.ids.shrink_if_needed(); - self.local_names.shrink_if_needed(); - } - - /// Analyze the given stylesheet, and collect invalidations from their - /// rules, in order to avoid doing a full restyle when we style the document - /// next time. - pub fn collect_invalidations_for( - &mut self, - device: &Device, - stylesheet: &S, - guard: &SharedRwLockReadGuard, - ) where - S: StylesheetInDocument, - { - debug!("StylesheetInvalidationSet::collect_invalidations_for"); - if self.fully_invalid { - debug!(" > Fully invalid already"); - return; - } - - if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { - debug!(" > Stylesheet was not effective"); - return; // Nothing to do here. - } - - let quirks_mode = device.quirks_mode(); - for rule in stylesheet.effective_rules(device, guard) { - self.collect_invalidations_for_rule(rule, guard, device, quirks_mode); - if self.fully_invalid { - break; - } - } - - self.shrink_if_needed(); - - debug!(" > resulting class invalidations: {:?}", self.classes); - debug!(" > resulting id invalidations: {:?}", self.ids); - debug!( - " > resulting local name invalidations: {:?}", - self.local_names - ); - debug!(" > fully_invalid: {}", self.fully_invalid); - } - - /// Clears the invalidation set, invalidating elements as needed if - /// `document_element` is provided. - /// - /// Returns true if any invalidations ocurred. - pub fn flush(&mut self, document_element: Option, snapshots: Option<&SnapshotMap>) -> bool - where - E: TElement, - { - debug!( - "Stylist::flush({:?}, snapshots: {})", - document_element, - snapshots.is_some() - ); - let have_invalidations = match document_element { - Some(e) => self.process_invalidations(e, snapshots), - None => false, - }; - self.clear(); - have_invalidations - } - - /// Returns whether there's no invalidation to process. - pub fn is_empty(&self) -> bool { - !self.fully_invalid && - self.classes.is_empty() && - self.ids.is_empty() && - self.local_names.is_empty() - } - - fn invalidation_kind_for( - &self, - element: E, - snapshot: Option<&Snapshot>, - quirks_mode: QuirksMode, - ) -> InvalidationKind - where - E: TElement, - { - debug_assert!(!self.fully_invalid); - - let mut kind = InvalidationKind::None; - - if !self.classes.is_empty() { - element.each_class(|c| { - kind.add(self.classes.get(c, quirks_mode)); - }); - - if kind.is_scope() { - return kind; - } - - if let Some(snapshot) = snapshot { - snapshot.each_class(|c| { - kind.add(self.classes.get(c, quirks_mode)); - }); - - if kind.is_scope() { - return kind; - } - } - } - - if !self.ids.is_empty() { - if let Some(ref id) = element.id() { - kind.add(self.ids.get(id, quirks_mode)); - if kind.is_scope() { - return kind; - } - } - - if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) { - kind.add(self.ids.get(old_id, quirks_mode)); - if kind.is_scope() { - return kind; - } - } - } - - if !self.local_names.is_empty() { - kind.add(self.local_names.get(element.local_name())); - } - - kind - } - - /// Clears the invalidation set without processing. - pub fn clear(&mut self) { - self.classes.clear(); - self.ids.clear(); - self.local_names.clear(); - self.fully_invalid = false; - debug_assert!(self.is_empty()); - } - - fn process_invalidations(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool - where - E: TElement, - { - debug!("Stylist::process_invalidations({:?}, {:?})", element, self); - - { - let mut data = match element.mutate_data() { - Some(data) => data, - None => return false, - }; - - if self.fully_invalid { - debug!("process_invalidations: fully_invalid({:?})", element); - data.hint.insert(RestyleHint::restyle_subtree()); - return true; - } - } - - if self.is_empty() { - debug!("process_invalidations: empty invalidation set"); - return false; - } - - let quirks_mode = element.as_node().owner_doc().quirks_mode(); - self.process_invalidations_in_subtree(element, snapshots, quirks_mode) - } - - /// Process style invalidations in a given subtree. This traverses the - /// subtree looking for elements that match the invalidations in our hash - /// map members. - /// - /// Returns whether it invalidated at least one element's style. - #[allow(unsafe_code)] - fn process_invalidations_in_subtree( - &self, - element: E, - snapshots: Option<&SnapshotMap>, - quirks_mode: QuirksMode, - ) -> bool - where - E: TElement, - { - debug!("process_invalidations_in_subtree({:?})", element); - let mut data = match element.mutate_data() { - Some(data) => data, - None => return false, - }; - - if !data.has_styles() { - return false; - } - - if data.hint.contains_subtree() { - debug!( - "process_invalidations_in_subtree: {:?} was already invalid", - element - ); - return false; - } - - let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s)); - let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot()); - - match self.invalidation_kind_for(element, snapshot, quirks_mode) { - InvalidationKind::None => {}, - InvalidationKind::Element => { - debug!( - "process_invalidations_in_subtree: {:?} matched self", - element - ); - data.hint.insert(RestyleHint::RESTYLE_SELF); - }, - InvalidationKind::Scope => { - debug!( - "process_invalidations_in_subtree: {:?} matched subtree", - element - ); - data.hint.insert(RestyleHint::restyle_subtree()); - return true; - }, - } - - let mut any_children_invalid = false; - - for child in element.traversal_children() { - let child = match child.as_element() { - Some(e) => e, - None => continue, - }; - - any_children_invalid |= - self.process_invalidations_in_subtree(child, snapshots, quirks_mode); - } - - if any_children_invalid { - debug!( - "Children of {:?} changed, setting dirty descendants", - element - ); - unsafe { element.set_dirty_descendants() } - } - - data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid - } - - /// TODO(emilio): Reuse the bucket stuff from selectormap? That handles - /// :is() / :where() etc. - fn scan_component( - component: &Component, - invalidation: &mut Option, - ) { - match *component { - Component::LocalName(LocalName { - ref name, - ref lower_name, - }) => { - if invalidation.is_none() { - *invalidation = Some(Invalidation::LocalName { - name: name.clone(), - lower_name: lower_name.clone(), - }); - } - }, - Component::Class(ref class) => { - if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) { - *invalidation = Some(Invalidation::Class(class.clone())); - } - }, - Component::ID(ref id) => { - if invalidation.as_ref().map_or(true, |s| !s.is_id()) { - *invalidation = Some(Invalidation::ID(id.clone())); - } - }, - _ => { - // Ignore everything else, at least for now. - }, - } - } - - /// Collect invalidations for a given selector. - /// - /// We look at the outermost local name, class, or ID selector to the left - /// of an ancestor combinator, in order to restyle only a given subtree. - /// - /// If the selector has no ancestor combinator, then we do the same for - /// the only sequence it has, but record it as an element invalidation - /// instead of a subtree invalidation. - /// - /// We prefer IDs to classs, and classes to local names, on the basis - /// that the former should be more specific than the latter. We also - /// prefer to generate subtree invalidations for the outermost part - /// of the selector, to reduce the amount of traversal we need to do - /// when flushing invalidations. - fn collect_invalidations( - &mut self, - selector: &Selector, - quirks_mode: QuirksMode, - ) { - debug!( - "StylesheetInvalidationSet::collect_invalidations({:?})", - selector - ); - - let mut element_invalidation: Option = None; - let mut subtree_invalidation: Option = None; - - let mut scan_for_element_invalidation = true; - let mut scan_for_subtree_invalidation = false; - - let mut iter = selector.iter(); - - loop { - for component in &mut iter { - if scan_for_element_invalidation { - Self::scan_component(component, &mut element_invalidation); - } else if scan_for_subtree_invalidation { - Self::scan_component(component, &mut subtree_invalidation); - } - } - match iter.next_sequence() { - None => break, - Some(combinator) => { - scan_for_subtree_invalidation = combinator.is_ancestor(); - }, - } - scan_for_element_invalidation = false; - } - - if let Some(s) = subtree_invalidation { - debug!(" > Found subtree invalidation: {:?}", s); - if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) { - return; - } - } - - if let Some(s) = element_invalidation { - debug!(" > Found element invalidation: {:?}", s); - if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) { - return; - } - } - - // The selector was of a form that we can't handle. Any element could - // match it, so let's just bail out. - debug!(" > Can't handle selector or OOMd, marking fully invalid"); - self.invalidate_fully() - } - - fn insert_invalidation( - &mut self, - invalidation: Invalidation, - kind: InvalidationKind, - quirks_mode: QuirksMode, - ) -> bool { - match invalidation { - Invalidation::Class(c) => { - let entry = match self.classes.try_entry(c.0, quirks_mode) { - Ok(e) => e, - Err(..) => return false, - }; - *entry.or_insert(InvalidationKind::None) |= kind; - }, - Invalidation::ID(i) => { - let entry = match self.ids.try_entry(i.0, quirks_mode) { - Ok(e) => e, - Err(..) => return false, - }; - *entry.or_insert(InvalidationKind::None) |= kind; - }, - Invalidation::LocalName { name, lower_name } => { - let insert_lower = name != lower_name; - if self.local_names.try_reserve(1).is_err() { - return false; - } - let entry = self.local_names.entry(name); - *entry.or_insert(InvalidationKind::None) |= kind; - if insert_lower { - if self.local_names.try_reserve(1).is_err() { - return false; - } - let entry = self.local_names.entry(lower_name); - *entry.or_insert(InvalidationKind::None) |= kind; - } - }, - } - - true - } - - /// Collects invalidations for a given CSS rule, if not fully invalid - /// already. - /// - /// TODO(emilio): we can't check whether the rule is inside a non-effective - /// subtree, we potentially could do that. - pub fn rule_changed( - &mut self, - stylesheet: &S, - rule: &CssRule, - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - change_kind: RuleChangeKind, - ) where - S: StylesheetInDocument, - { - use crate::stylesheets::CssRule::*; - - debug!("StylesheetInvalidationSet::rule_changed"); - if self.fully_invalid { - return; - } - - if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) { - debug!(" > Stylesheet was not effective"); - return; // Nothing to do here. - } - - let is_generic_change = change_kind == RuleChangeKind::Generic; - - match *rule { - Namespace(..) => { - // It's not clear what handling changes for this correctly would - // look like. - }, - LayerStatement(..) => { - // Layer statement insertions might alter styling order, so we need to always - // invalidate fully. - return self.invalidate_fully(); - }, - CounterStyle(..) | - Page(..) | - Property(..) | - FontFeatureValues(..) | - FontPaletteValues(..) | - FontFace(..) | - Keyframes(..) | - Container(..) | - Style(..) => { - if is_generic_change { - // TODO(emilio): We need to do this for selector / keyframe - // name / font-face changes, because we don't have the old - // selector / name. If we distinguish those changes - // specially, then we can at least use this invalidation for - // style declaration changes. - return self.invalidate_fully(); - } - - self.collect_invalidations_for_rule(rule, guard, device, quirks_mode) - }, - Document(..) | Import(..) | Media(..) | Supports(..) | LayerBlock(..) => { - if !is_generic_change && - !EffectiveRules::is_effective(guard, device, quirks_mode, rule) - { - return; - } - - let rules = - EffectiveRulesIterator::effective_children(device, quirks_mode, guard, rule); - for rule in rules { - self.collect_invalidations_for_rule(rule, guard, device, quirks_mode); - if self.fully_invalid { - break; - } - } - }, - } - } - - /// Collects invalidations for a given CSS rule. - fn collect_invalidations_for_rule( - &mut self, - rule: &CssRule, - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - ) { - use crate::stylesheets::CssRule::*; - debug!("StylesheetInvalidationSet::collect_invalidations_for_rule"); - debug_assert!(!self.fully_invalid, "Not worth to be here!"); - - match *rule { - Style(ref lock) => { - let style_rule = lock.read_with(guard); - for selector in &style_rule.selectors.0 { - self.collect_invalidations(selector, quirks_mode); - if self.fully_invalid { - return; - } - } - }, - Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) | - Container(..) | LayerStatement(..) | LayerBlock(..) => { - // Do nothing, relevant nested rules are visited as part of the - // iteration. - }, - FontFace(..) => { - // Do nothing, @font-face doesn't affect computed style - // information. We'll restyle when the font face loads, if - // needed. - }, - Keyframes(ref lock) => { - let keyframes_rule = lock.read_with(guard); - if device.animation_name_may_be_referenced(&keyframes_rule.name) { - debug!( - " > Found @keyframes rule potentially referenced \ - from the page, marking the whole tree invalid." - ); - self.fully_invalid = true; - } else { - // Do nothing, this animation can't affect the style of - // existing elements. - } - }, - CounterStyle(..) | - Page(..) | - Property(..) | - FontFeatureValues(..) | - FontPaletteValues(..) => { - debug!(" > Found unsupported rule, marking the whole subtree invalid."); - - // TODO(emilio): Can we do better here? - // - // At least in `@page`, we could check the relevant media, I - // guess. - self.fully_invalid = true; - }, - } - } -} diff --git a/components/style/invalidation/viewport_units.rs b/components/style/invalidation/viewport_units.rs deleted file mode 100644 index 06faeb14c46..00000000000 --- a/components/style/invalidation/viewport_units.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Invalidates style of all elements that depend on viewport units. - -use crate::data::ViewportUnitUsage; -use crate::dom::{TElement, TNode}; -use crate::invalidation::element::restyle_hints::RestyleHint; - -/// Invalidates style of all elements that depend on viewport units. -/// -/// Returns whether any element was invalidated. -pub fn invalidate(root: E) -> bool -where - E: TElement, -{ - debug!("invalidation::viewport_units::invalidate({:?})", root); - invalidate_recursively(root) -} - -fn invalidate_recursively(element: E) -> bool -where - E: TElement, -{ - let mut data = match element.mutate_data() { - Some(data) => data, - None => return false, - }; - - if data.hint.will_recascade_subtree() { - debug!("invalidate_recursively: {:?} was already invalid", element); - return false; - } - - let usage = data.styles.viewport_unit_usage(); - let uses_viewport_units = usage != ViewportUnitUsage::None; - if uses_viewport_units { - debug!( - "invalidate_recursively: {:?} uses viewport units {:?}", - element, usage - ); - } - - match usage { - ViewportUnitUsage::None => {}, - ViewportUnitUsage::FromQuery => { - data.hint.insert(RestyleHint::RESTYLE_SELF); - }, - ViewportUnitUsage::FromDeclaration => { - data.hint.insert(RestyleHint::RECASCADE_SELF); - }, - } - - let mut any_children_invalid = false; - for child in element.traversal_children() { - if let Some(child) = child.as_element() { - any_children_invalid |= invalidate_recursively(child); - } - } - - if any_children_invalid { - debug!( - "invalidate_recursively: Children of {:?} changed, setting dirty descendants", - element - ); - unsafe { element.set_dirty_descendants() } - } - - uses_viewport_units || any_children_invalid -} diff --git a/components/style/lib.rs b/components/style/lib.rs deleted file mode 100644 index 90a52e567b1..00000000000 --- a/components/style/lib.rs +++ /dev/null @@ -1,329 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Calculate [specified][specified] and [computed values][computed] from a -//! tree of DOM nodes and a set of stylesheets. -//! -//! [computed]: https://drafts.csswg.org/css-cascade/#computed -//! [specified]: https://drafts.csswg.org/css-cascade/#specified -//! -//! In particular, this crate contains the definitions of supported properties, -//! the code to parse them into specified values and calculate the computed -//! values based on the specified values, as well as the code to serialize both -//! specified and computed values. -//! -//! The main entry point is [`recalc_style_at`][recalc_style_at]. -//! -//! [recalc_style_at]: traversal/fn.recalc_style_at.html -//! -//! Major dependencies are the [cssparser][cssparser] and [selectors][selectors] -//! crates. -//! -//! [cssparser]: ../cssparser/index.html -//! [selectors]: ../selectors/index.html - -#![deny(missing_docs)] - -#[macro_use] -extern crate bitflags; -#[macro_use] -extern crate cssparser; -#[macro_use] -extern crate debug_unreachable; -#[macro_use] -extern crate derive_more; -#[macro_use] -#[cfg(feature = "gecko")] -extern crate gecko_profiler; -#[cfg(feature = "gecko")] -#[macro_use] -pub mod gecko_string_cache; -#[cfg(feature = "servo")] -#[macro_use] -extern crate html5ever; -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; -#[macro_use] -extern crate malloc_size_of; -#[macro_use] -extern crate malloc_size_of_derive; -#[cfg(feature = "gecko")] -pub use nsstring; -#[cfg(feature = "gecko")] -extern crate num_cpus; -#[macro_use] -extern crate num_derive; -#[macro_use] -extern crate serde; -pub use servo_arc; -#[cfg(feature = "servo")] -#[macro_use] -extern crate servo_atoms; -#[macro_use] -extern crate static_assertions; -#[macro_use] -extern crate style_derive; -#[macro_use] -extern crate to_shmem_derive; - -#[macro_use] -mod macros; - -pub mod animation; -pub mod applicable_declarations; -#[allow(missing_docs)] // TODO. -#[cfg(feature = "servo")] -pub mod attr; -pub mod author_styles; -pub mod bezier; -pub mod bloom; -pub mod color; -#[path = "properties/computed_value_flags.rs"] -pub mod computed_value_flags; -pub mod context; -pub mod counter_style; -pub mod custom_properties; -pub mod data; -pub mod dom; -pub mod dom_apis; -pub mod driver; -#[cfg(feature = "servo")] -mod encoding_support; -pub mod error_reporting; -pub mod font_face; -pub mod font_metrics; -#[cfg(feature = "gecko")] -#[allow(unsafe_code)] -pub mod gecko_bindings; -pub mod global_style_data; -pub mod invalidation; -#[allow(missing_docs)] // TODO. -pub mod logical_geometry; -pub mod matching; -pub mod media_queries; -pub mod parallel; -pub mod parser; -pub mod piecewise_linear; -pub mod properties_and_values; -#[macro_use] -pub mod queries; -pub mod rule_cache; -pub mod rule_collector; -pub mod rule_tree; -pub mod scoped_tls; -pub mod selector_map; -pub mod selector_parser; -pub mod shared_lock; -pub mod sharing; -pub mod str; -pub mod style_adjuster; -pub mod style_resolver; -pub mod stylesheet_set; -pub mod stylesheets; -pub mod stylist; -pub mod thread_state; -pub mod traversal; -pub mod traversal_flags; -pub mod use_counters; -#[macro_use] -#[allow(non_camel_case_types)] -pub mod values; - -#[cfg(feature = "gecko")] -pub use crate::gecko_string_cache as string_cache; -#[cfg(feature = "gecko")] -pub use crate::gecko_string_cache::Atom; -/// The namespace prefix type for Gecko, which is just an atom. -#[cfg(feature = "gecko")] -pub type Prefix = crate::values::AtomIdent; -/// The local name of an element for Gecko, which is just an atom. -#[cfg(feature = "gecko")] -pub type LocalName = crate::values::AtomIdent; -#[cfg(feature = "gecko")] -pub use crate::gecko_string_cache::Namespace; - -#[cfg(feature = "servo")] -pub use servo_atoms::Atom; - -#[cfg(feature = "servo")] -#[allow(missing_docs)] -pub type LocalName = crate::values::GenericAtomIdent; -#[cfg(feature = "servo")] -#[allow(missing_docs)] -pub type Namespace = crate::values::GenericAtomIdent; -#[cfg(feature = "servo")] -#[allow(missing_docs)] -pub type Prefix = crate::values::GenericAtomIdent; - -pub use style_traits::arc_slice::ArcSlice; -pub use style_traits::owned_slice::OwnedSlice; -pub use style_traits::owned_str::OwnedStr; - -use std::hash::{BuildHasher, Hash}; - -#[macro_use] -pub mod properties; - -#[cfg(feature = "gecko")] -#[allow(unsafe_code)] -pub mod gecko; - -// uses a macro from properties -#[cfg(feature = "servo")] -#[allow(unsafe_code)] -pub mod servo; - -macro_rules! reexport_computed_values { - ( $( { $name: ident } )+ ) => { - /// Types for [computed values][computed]. - /// - /// [computed]: https://drafts.csswg.org/css-cascade/#computed - pub mod computed_values { - $( - pub use crate::properties::longhands::$name::computed_value as $name; - )+ - // Don't use a side-specific name needlessly: - pub use crate::properties::longhands::border_top_style::computed_value as border_style; - } - } -} -longhand_properties_idents!(reexport_computed_values); -#[cfg(feature = "gecko")] -use crate::gecko_string_cache::WeakAtom; -#[cfg(feature = "servo")] -use servo_atoms::Atom as WeakAtom; - -/// Extension methods for selectors::attr::CaseSensitivity -pub trait CaseSensitivityExt { - /// Return whether two atoms compare equal according to this case sensitivity. - fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool; -} - -impl CaseSensitivityExt for selectors::attr::CaseSensitivity { - fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool { - match self { - selectors::attr::CaseSensitivity::CaseSensitive => a == b, - selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b), - } - } -} - -/// A trait pretty much similar to num_traits::Zero, but without the need of -/// implementing `Add`. -pub trait Zero { - /// Returns the zero value. - fn zero() -> Self; - - /// Returns whether this value is zero. - fn is_zero(&self) -> bool; -} - -impl Zero for T -where - T: num_traits::Zero, -{ - fn zero() -> Self { - ::zero() - } - - fn is_zero(&self) -> bool { - ::is_zero(self) - } -} - -/// A trait implementing a function to tell if the number is zero without a percent -pub trait ZeroNoPercent { - /// So, `0px` should return `true`, but `0%` or `1px` should return `false` - fn is_zero_no_percent(&self) -> bool; -} - -/// A trait pretty much similar to num_traits::One, but without the need of -/// implementing `Mul`. -pub trait One { - /// Reutrns the one value. - fn one() -> Self; - - /// Returns whether this value is one. - fn is_one(&self) -> bool; -} - -impl One for T -where - T: num_traits::One + PartialEq, -{ - fn one() -> Self { - ::one() - } - - fn is_one(&self) -> bool { - *self == One::one() - } -} - -/// An allocation error. -/// -/// TODO(emilio): Would be nice to have more information here, or for SmallVec -/// to return the standard error type (and then we can just return that). -/// -/// But given we use these mostly to bail out and ignore them, it's not a big -/// deal. -#[derive(Debug)] -pub struct AllocErr; - -impl From for AllocErr { - #[inline] - fn from(_: smallvec::CollectionAllocErr) -> Self { - Self - } -} - -impl From for AllocErr { - #[inline] - fn from(_: std::collections::TryReserveError) -> Self { - Self - } -} - -/// Shrink the capacity of the collection if needed. -pub(crate) trait ShrinkIfNeeded { - fn shrink_if_needed(&mut self); -} - -/// We shrink the capacity of a collection if we're wasting more than a 25% of -/// its capacity, and if the collection is arbitrarily big enough -/// (>= CAPACITY_THRESHOLD entries). -#[inline] -fn should_shrink(len: usize, capacity: usize) -> bool { - const CAPACITY_THRESHOLD: usize = 64; - capacity >= CAPACITY_THRESHOLD && len + capacity / 4 < capacity -} - -impl ShrinkIfNeeded for std::collections::HashMap -where - K: Eq + Hash, - H: BuildHasher, -{ - fn shrink_if_needed(&mut self) { - if should_shrink(self.len(), self.capacity()) { - self.shrink_to_fit(); - } - } -} - -impl ShrinkIfNeeded for std::collections::HashSet -where - T: Eq + Hash, - H: BuildHasher, -{ - fn shrink_if_needed(&mut self) { - if should_shrink(self.len(), self.capacity()) { - self.shrink_to_fit(); - } - } -} - -// TODO(emilio): Measure and see if we're wasting a lot of memory on Vec / -// SmallVec, and if so consider shrinking those as well. diff --git a/components/style/logical_geometry.rs b/components/style/logical_geometry.rs deleted file mode 100644 index 9a823a5a24a..00000000000 --- a/components/style/logical_geometry.rs +++ /dev/null @@ -1,1522 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Geometry in flow-relative space. - -use crate::properties::style_structs; -use euclid::default::{Point2D, Rect, SideOffsets2D, Size2D}; -use euclid::num::Zero; -use std::cmp::{max, min}; -use std::fmt::{self, Debug, Error, Formatter}; -use std::ops::{Add, Sub}; -use unicode_bidi as bidi; - -pub enum BlockFlowDirection { - TopToBottom, - RightToLeft, - LeftToRight, -} - -pub enum InlineBaseDirection { - LeftToRight, - RightToLeft, -} - -// TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt() -bitflags!( - #[cfg_attr(feature = "servo", derive(MallocSizeOf, Serialize))] - #[repr(C)] - pub struct WritingMode: u8 { - /// A vertical writing mode; writing-mode is vertical-rl, - /// vertical-lr, sideways-lr, or sideways-rl. - const VERTICAL = 1 << 0; - /// The inline flow direction is reversed against the physical - /// direction (i.e. right-to-left or bottom-to-top); writing-mode is - /// sideways-lr or direction is rtl (but not both). - /// - /// (This bit can be derived from the others, but we store it for - /// convenience.) - const INLINE_REVERSED = 1 << 1; - /// A vertical writing mode whose block progression direction is left- - /// to-right; writing-mode is vertical-lr or sideways-lr. - /// - /// Never set without VERTICAL. - const VERTICAL_LR = 1 << 2; - /// The line-over/line-under sides are inverted with respect to the - /// block-start/block-end edge; writing-mode is vertical-lr. - /// - /// Never set without VERTICAL and VERTICAL_LR. - const LINE_INVERTED = 1 << 3; - /// direction is rtl. - const RTL = 1 << 4; - /// All text within a vertical writing mode is displayed sideways - /// and runs top-to-bottom or bottom-to-top; set in these cases: - /// - /// * writing-mode: sideways-rl; - /// * writing-mode: sideways-lr; - /// - /// Never set without VERTICAL. - const VERTICAL_SIDEWAYS = 1 << 5; - /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation; - /// set in these cases: - /// - /// * writing-mode: vertical-rl; text-orientation: sideways; - /// * writing-mode: vertical-lr; text-orientation: sideways; - /// - /// Never set without VERTICAL. - const TEXT_SIDEWAYS = 1 << 6; - /// Horizontal text within a vertical writing mode is displayed with each - /// glyph upright; set in these cases: - /// - /// * writing-mode: vertical-rl; text-orientation: upright; - /// * writing-mode: vertical-lr: text-orientation: upright; - /// - /// Never set without VERTICAL. - const UPRIGHT = 1 << 7; - } -); - -impl WritingMode { - /// Return a WritingMode bitflags from the relevant CSS properties. - pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self { - use crate::properties::longhands::direction::computed_value::T as Direction; - use crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode; - - let mut flags = WritingMode::empty(); - - let direction = inheritedbox_style.clone_direction(); - let writing_mode = inheritedbox_style.clone_writing_mode(); - - match direction { - Direction::Ltr => {}, - Direction::Rtl => { - flags.insert(WritingMode::RTL); - }, - } - - match writing_mode { - SpecifiedWritingMode::HorizontalTb => { - if direction == Direction::Rtl { - flags.insert(WritingMode::INLINE_REVERSED); - } - }, - SpecifiedWritingMode::VerticalRl => { - flags.insert(WritingMode::VERTICAL); - if direction == Direction::Rtl { - flags.insert(WritingMode::INLINE_REVERSED); - } - }, - SpecifiedWritingMode::VerticalLr => { - flags.insert(WritingMode::VERTICAL); - flags.insert(WritingMode::VERTICAL_LR); - flags.insert(WritingMode::LINE_INVERTED); - if direction == Direction::Rtl { - flags.insert(WritingMode::INLINE_REVERSED); - } - }, - #[cfg(feature = "gecko")] - SpecifiedWritingMode::SidewaysRl => { - flags.insert(WritingMode::VERTICAL); - flags.insert(WritingMode::VERTICAL_SIDEWAYS); - if direction == Direction::Rtl { - flags.insert(WritingMode::INLINE_REVERSED); - } - }, - #[cfg(feature = "gecko")] - SpecifiedWritingMode::SidewaysLr => { - flags.insert(WritingMode::VERTICAL); - flags.insert(WritingMode::VERTICAL_LR); - flags.insert(WritingMode::VERTICAL_SIDEWAYS); - if direction == Direction::Ltr { - flags.insert(WritingMode::INLINE_REVERSED); - } - }, - } - - #[cfg(feature = "gecko")] - { - use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation; - - // text-orientation only has an effect for vertical-rl and - // vertical-lr values of writing-mode. - match writing_mode { - SpecifiedWritingMode::VerticalRl | SpecifiedWritingMode::VerticalLr => { - match inheritedbox_style.clone_text_orientation() { - TextOrientation::Mixed => {}, - TextOrientation::Upright => { - flags.insert(WritingMode::UPRIGHT); - - // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright: - // - // > This value causes the used value of direction - // > to be ltr, and for the purposes of bidi - // > reordering, causes all characters to be treated - // > as strong LTR. - flags.remove(WritingMode::RTL); - flags.remove(WritingMode::INLINE_REVERSED); - }, - TextOrientation::Sideways => { - flags.insert(WritingMode::TEXT_SIDEWAYS); - }, - } - }, - _ => {}, - } - } - - flags - } - - /// Returns the `horizontal-tb` value. - pub fn horizontal_tb() -> Self { - Self::from_bits_truncate(0) - } - - #[inline] - pub fn is_vertical(&self) -> bool { - self.intersects(WritingMode::VERTICAL) - } - - #[inline] - pub fn is_horizontal(&self) -> bool { - !self.is_vertical() - } - - /// Assuming .is_vertical(), does the block direction go left to right? - #[inline] - pub fn is_vertical_lr(&self) -> bool { - self.intersects(WritingMode::VERTICAL_LR) - } - - /// Assuming .is_vertical(), does the inline direction go top to bottom? - #[inline] - pub fn is_inline_tb(&self) -> bool { - // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical - !self.intersects(WritingMode::INLINE_REVERSED) - } - - #[inline] - pub fn is_bidi_ltr(&self) -> bool { - !self.intersects(WritingMode::RTL) - } - - #[inline] - pub fn is_sideways(&self) -> bool { - self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS) - } - - #[inline] - pub fn is_upright(&self) -> bool { - self.intersects(WritingMode::UPRIGHT) - } - - /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical - /// - /// | Return | line-left is… | line-right is… | - /// |---------|---------------|----------------| - /// | `true` | inline-start | inline-end | - /// | `false` | inline-end | inline-start | - #[inline] - pub fn line_left_is_inline_start(&self) -> bool { - // https://drafts.csswg.org/css-writing-modes/#inline-start - // “For boxes with a used direction value of ltr, this means the line-left side. - // For boxes with a used direction value of rtl, this means the line-right side.” - self.is_bidi_ltr() - } - - #[inline] - pub fn inline_start_physical_side(&self) -> PhysicalSide { - match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) { - (false, _, true) => PhysicalSide::Left, - (false, _, false) => PhysicalSide::Right, - (true, true, _) => PhysicalSide::Top, - (true, false, _) => PhysicalSide::Bottom, - } - } - - #[inline] - pub fn inline_end_physical_side(&self) -> PhysicalSide { - match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) { - (false, _, true) => PhysicalSide::Right, - (false, _, false) => PhysicalSide::Left, - (true, true, _) => PhysicalSide::Bottom, - (true, false, _) => PhysicalSide::Top, - } - } - - #[inline] - pub fn block_start_physical_side(&self) -> PhysicalSide { - match (self.is_vertical(), self.is_vertical_lr()) { - (false, _) => PhysicalSide::Top, - (true, true) => PhysicalSide::Left, - (true, false) => PhysicalSide::Right, - } - } - - #[inline] - pub fn block_end_physical_side(&self) -> PhysicalSide { - match (self.is_vertical(), self.is_vertical_lr()) { - (false, _) => PhysicalSide::Bottom, - (true, true) => PhysicalSide::Right, - (true, false) => PhysicalSide::Left, - } - } - - #[inline] - fn physical_sides_to_corner( - block_side: PhysicalSide, - inline_side: PhysicalSide, - ) -> PhysicalCorner { - match (block_side, inline_side) { - (PhysicalSide::Top, PhysicalSide::Left) | (PhysicalSide::Left, PhysicalSide::Top) => { - PhysicalCorner::TopLeft - }, - (PhysicalSide::Top, PhysicalSide::Right) | (PhysicalSide::Right, PhysicalSide::Top) => { - PhysicalCorner::TopRight - }, - (PhysicalSide::Bottom, PhysicalSide::Right) | - (PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight, - (PhysicalSide::Bottom, PhysicalSide::Left) | - (PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft, - _ => unreachable!("block and inline sides must be orthogonal"), - } - } - - #[inline] - pub fn start_start_physical_corner(&self) -> PhysicalCorner { - WritingMode::physical_sides_to_corner( - self.block_start_physical_side(), - self.inline_start_physical_side(), - ) - } - - #[inline] - pub fn start_end_physical_corner(&self) -> PhysicalCorner { - WritingMode::physical_sides_to_corner( - self.block_start_physical_side(), - self.inline_end_physical_side(), - ) - } - - #[inline] - pub fn end_start_physical_corner(&self) -> PhysicalCorner { - WritingMode::physical_sides_to_corner( - self.block_end_physical_side(), - self.inline_start_physical_side(), - ) - } - - #[inline] - pub fn end_end_physical_corner(&self) -> PhysicalCorner { - WritingMode::physical_sides_to_corner( - self.block_end_physical_side(), - self.inline_end_physical_side(), - ) - } - - #[inline] - pub fn block_flow_direction(&self) -> BlockFlowDirection { - match (self.is_vertical(), self.is_vertical_lr()) { - (false, _) => BlockFlowDirection::TopToBottom, - (true, true) => BlockFlowDirection::LeftToRight, - (true, false) => BlockFlowDirection::RightToLeft, - } - } - - #[inline] - pub fn inline_base_direction(&self) -> InlineBaseDirection { - if self.intersects(WritingMode::RTL) { - InlineBaseDirection::RightToLeft - } else { - InlineBaseDirection::LeftToRight - } - } - - #[inline] - /// The default bidirectional embedding level for this writing mode. - /// - /// Returns bidi level 0 if the mode is LTR, or 1 otherwise. - pub fn to_bidi_level(&self) -> bidi::Level { - if self.is_bidi_ltr() { - bidi::Level::ltr() - } else { - bidi::Level::rtl() - } - } -} - -impl fmt::Display for WritingMode { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - if self.is_vertical() { - write!(formatter, "V")?; - if self.is_vertical_lr() { - write!(formatter, " LR")?; - } else { - write!(formatter, " RL")?; - } - if self.is_sideways() { - write!(formatter, " Sideways")?; - } - if self.intersects(WritingMode::LINE_INVERTED) { - write!(formatter, " Inverted")?; - } - } else { - write!(formatter, "H")?; - } - if self.is_bidi_ltr() { - write!(formatter, " LTR") - } else { - write!(formatter, " RTL") - } - } -} - -/// Wherever logical geometry is used, the writing mode is known based on context: -/// every method takes a `mode` parameter. -/// However, this context is easy to get wrong. -/// In debug builds only, logical geometry objects store their writing mode -/// (in addition to taking it as a parameter to methods) and check it. -/// In non-debug builds, make this storage zero-size and the checks no-ops. -#[cfg(not(debug_assertions))] -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -struct DebugWritingMode; - -#[cfg(debug_assertions)] -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -struct DebugWritingMode { - mode: WritingMode, -} - -#[cfg(not(debug_assertions))] -impl DebugWritingMode { - #[inline] - fn check(&self, _other: WritingMode) {} - - #[inline] - fn check_debug(&self, _other: DebugWritingMode) {} - - #[inline] - fn new(_mode: WritingMode) -> DebugWritingMode { - DebugWritingMode - } -} - -#[cfg(debug_assertions)] -impl DebugWritingMode { - #[inline] - fn check(&self, other: WritingMode) { - assert_eq!(self.mode, other) - } - - #[inline] - fn check_debug(&self, other: DebugWritingMode) { - assert_eq!(self.mode, other.mode) - } - - #[inline] - fn new(mode: WritingMode) -> DebugWritingMode { - DebugWritingMode { mode: mode } - } -} - -impl Debug for DebugWritingMode { - #[cfg(not(debug_assertions))] - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - write!(formatter, "?") - } - - #[cfg(debug_assertions)] - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - write!(formatter, "{}", self.mode) - } -} - -// Used to specify the logical direction. -#[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -pub enum Direction { - Inline, - Block, -} - -/// A 2D size in flow-relative dimensions -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -pub struct LogicalSize { - pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure - pub block: T, // block-size, a.k.a. logical height, a.k.a. extent - debug_writing_mode: DebugWritingMode, -} - -impl Debug for LogicalSize { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - write!( - formatter, - "LogicalSize({:?}, i{:?}×b{:?})", - self.debug_writing_mode, self.inline, self.block - ) - } -} - -// Can not implement the Zero trait: its zero() method does not have the `mode` parameter. -impl LogicalSize { - #[inline] - pub fn zero(mode: WritingMode) -> LogicalSize { - LogicalSize { - inline: Zero::zero(), - block: Zero::zero(), - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl LogicalSize { - #[inline] - pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize { - LogicalSize { - inline: inline, - block: block, - debug_writing_mode: DebugWritingMode::new(mode), - } - } - - #[inline] - pub fn from_physical(mode: WritingMode, size: Size2D) -> LogicalSize { - if mode.is_vertical() { - LogicalSize::new(mode, size.height, size.width) - } else { - LogicalSize::new(mode, size.width, size.height) - } - } -} - -impl LogicalSize { - #[inline] - pub fn width(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.block - } else { - self.inline - } - } - - #[inline] - pub fn set_width(&mut self, mode: WritingMode, width: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.block = width - } else { - self.inline = width - } - } - - #[inline] - pub fn height(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.inline - } else { - self.block - } - } - - #[inline] - pub fn set_height(&mut self, mode: WritingMode, height: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.inline = height - } else { - self.block = height - } - } - - #[inline] - pub fn to_physical(&self, mode: WritingMode) -> Size2D { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - Size2D::new(self.block, self.inline) - } else { - Size2D::new(self.inline, self.block) - } - } - - #[inline] - pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize { - if mode_from == mode_to { - self.debug_writing_mode.check(mode_from); - *self - } else { - LogicalSize::from_physical(mode_to, self.to_physical(mode_from)) - } - } -} - -impl> Add for LogicalSize { - type Output = LogicalSize; - - #[inline] - fn add(self, other: LogicalSize) -> LogicalSize { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalSize { - debug_writing_mode: self.debug_writing_mode, - inline: self.inline + other.inline, - block: self.block + other.block, - } - } -} - -// TODO(servo#30577) remove this once underlying bugs are fixed -impl> LogicalSize { - #[inline] - pub fn add_or_warn(self, other: LogicalSize) -> LogicalSize { - #[cfg(debug_assertions)] - if !(self.debug_writing_mode.mode == other.debug_writing_mode.mode) { - log::warn!("debug assertion failed! self.debug_writing_mode.mode == other.debug_writing_mode.mode"); - } - LogicalSize { - debug_writing_mode: self.debug_writing_mode, - inline: self.inline + other.inline, - block: self.block + other.block, - } - } -} - -impl> Sub for LogicalSize { - type Output = LogicalSize; - - #[inline] - fn sub(self, other: LogicalSize) -> LogicalSize { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalSize { - debug_writing_mode: self.debug_writing_mode, - inline: self.inline - other.inline, - block: self.block - other.block, - } - } -} - -/// A 2D point in flow-relative dimensions -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -pub struct LogicalPoint { - /// inline-axis coordinate - pub i: T, - /// block-axis coordinate - pub b: T, - debug_writing_mode: DebugWritingMode, -} - -impl Debug for LogicalPoint { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - write!( - formatter, - "LogicalPoint({:?} (i{:?}, b{:?}))", - self.debug_writing_mode, self.i, self.b - ) - } -} - -// Can not implement the Zero trait: its zero() method does not have the `mode` parameter. -impl LogicalPoint { - #[inline] - pub fn zero(mode: WritingMode) -> LogicalPoint { - LogicalPoint { - i: Zero::zero(), - b: Zero::zero(), - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl LogicalPoint { - #[inline] - pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint { - LogicalPoint { - i: i, - b: b, - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl> LogicalPoint { - #[inline] - pub fn from_physical( - mode: WritingMode, - point: Point2D, - container_size: Size2D, - ) -> LogicalPoint { - if mode.is_vertical() { - LogicalPoint { - i: if mode.is_inline_tb() { - point.y - } else { - container_size.height - point.y - }, - b: if mode.is_vertical_lr() { - point.x - } else { - container_size.width - point.x - }, - debug_writing_mode: DebugWritingMode::new(mode), - } - } else { - LogicalPoint { - i: if mode.is_bidi_ltr() { - point.x - } else { - container_size.width - point.x - }, - b: point.y, - debug_writing_mode: DebugWritingMode::new(mode), - } - } - } - - #[inline] - pub fn x(&self, mode: WritingMode, container_size: Size2D) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_vertical_lr() { - self.b - } else { - container_size.width - self.b - } - } else { - if mode.is_bidi_ltr() { - self.i - } else { - container_size.width - self.i - } - } - } - - #[inline] - pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.b = if mode.is_vertical_lr() { - x - } else { - container_size.width - x - } - } else { - self.i = if mode.is_bidi_ltr() { - x - } else { - container_size.width - x - } - } - } - - #[inline] - pub fn y(&self, mode: WritingMode, container_size: Size2D) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_inline_tb() { - self.i - } else { - container_size.height - self.i - } - } else { - self.b - } - } - - #[inline] - pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.i = if mode.is_inline_tb() { - y - } else { - container_size.height - y - } - } else { - self.b = y - } - } - - #[inline] - pub fn to_physical(&self, mode: WritingMode, container_size: Size2D) -> Point2D { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - Point2D::new( - if mode.is_vertical_lr() { - self.b - } else { - container_size.width - self.b - }, - if mode.is_inline_tb() { - self.i - } else { - container_size.height - self.i - }, - ) - } else { - Point2D::new( - if mode.is_bidi_ltr() { - self.i - } else { - container_size.width - self.i - }, - self.b, - ) - } - } - - // TODO(servo#30577) remove this once underlying bugs are fixed - #[inline] - pub fn to_physical_or_warn(&self, mode: WritingMode, container_size: Size2D) -> Point2D { - #[cfg(debug_assertions)] - if !(self.debug_writing_mode.mode == mode) { - log::warn!("debug assertion failed! self.debug_writing_mode.mode == mode"); - } - if mode.is_vertical() { - Point2D::new( - if mode.is_vertical_lr() { - self.b - } else { - container_size.width - self.b - }, - if mode.is_inline_tb() { - self.i - } else { - container_size.height - self.i - }, - ) - } else { - Point2D::new( - if mode.is_bidi_ltr() { - self.i - } else { - container_size.width - self.i - }, - self.b, - ) - } - } - - #[inline] - pub fn convert( - &self, - mode_from: WritingMode, - mode_to: WritingMode, - container_size: Size2D, - ) -> LogicalPoint { - if mode_from == mode_to { - self.debug_writing_mode.check(mode_from); - *self - } else { - LogicalPoint::from_physical( - mode_to, - self.to_physical(mode_from, container_size), - container_size, - ) - } - } -} - -impl> LogicalPoint { - /// This doesn’t really makes sense, - /// but happens when dealing with multiple origins. - #[inline] - pub fn add_point(&self, other: &LogicalPoint) -> LogicalPoint { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalPoint { - debug_writing_mode: self.debug_writing_mode, - i: self.i + other.i, - b: self.b + other.b, - } - } -} - -impl> Add> for LogicalPoint { - type Output = LogicalPoint; - - #[inline] - fn add(self, other: LogicalSize) -> LogicalPoint { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalPoint { - debug_writing_mode: self.debug_writing_mode, - i: self.i + other.inline, - b: self.b + other.block, - } - } -} - -impl> Sub> for LogicalPoint { - type Output = LogicalPoint; - - #[inline] - fn sub(self, other: LogicalSize) -> LogicalPoint { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalPoint { - debug_writing_mode: self.debug_writing_mode, - i: self.i - other.inline, - b: self.b - other.block, - } - } -} - -/// A "margin" in flow-relative dimensions -/// Represents the four sides of the margins, borders, or padding of a CSS box, -/// or a combination of those. -/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle. -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -pub struct LogicalMargin { - pub block_start: T, - pub inline_end: T, - pub block_end: T, - pub inline_start: T, - debug_writing_mode: DebugWritingMode, -} - -impl Debug for LogicalMargin { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - let writing_mode_string = if cfg!(debug_assertions) { - format!("{:?}, ", self.debug_writing_mode) - } else { - "".to_owned() - }; - - write!( - formatter, - "LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})", - writing_mode_string, - self.inline_start, - self.inline_end, - self.block_start, - self.block_end - ) - } -} - -impl LogicalMargin { - #[inline] - pub fn zero(mode: WritingMode) -> LogicalMargin { - LogicalMargin { - block_start: Zero::zero(), - inline_end: Zero::zero(), - block_end: Zero::zero(), - inline_start: Zero::zero(), - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl LogicalMargin { - #[inline] - pub fn new( - mode: WritingMode, - block_start: T, - inline_end: T, - block_end: T, - inline_start: T, - ) -> LogicalMargin { - LogicalMargin { - block_start, - inline_end, - block_end, - inline_start, - debug_writing_mode: DebugWritingMode::new(mode), - } - } - - #[inline] - pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D) -> LogicalMargin { - let block_start; - let inline_end; - let block_end; - let inline_start; - if mode.is_vertical() { - if mode.is_vertical_lr() { - block_start = offsets.left; - block_end = offsets.right; - } else { - block_start = offsets.right; - block_end = offsets.left; - } - if mode.is_inline_tb() { - inline_start = offsets.top; - inline_end = offsets.bottom; - } else { - inline_start = offsets.bottom; - inline_end = offsets.top; - } - } else { - block_start = offsets.top; - block_end = offsets.bottom; - if mode.is_bidi_ltr() { - inline_start = offsets.left; - inline_end = offsets.right; - } else { - inline_start = offsets.right; - inline_end = offsets.left; - } - } - LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start) - } -} - -impl LogicalMargin { - #[inline] - pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin { - LogicalMargin::new(mode, value, value, value, value) - } - - #[inline] - pub fn top(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_inline_tb() { - self.inline_start - } else { - self.inline_end - } - } else { - self.block_start - } - } - - #[inline] - pub fn set_top(&mut self, mode: WritingMode, top: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_inline_tb() { - self.inline_start = top - } else { - self.inline_end = top - } - } else { - self.block_start = top - } - } - - #[inline] - pub fn right(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_vertical_lr() { - self.block_end - } else { - self.block_start - } - } else { - if mode.is_bidi_ltr() { - self.inline_end - } else { - self.inline_start - } - } - } - - #[inline] - pub fn set_right(&mut self, mode: WritingMode, right: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_vertical_lr() { - self.block_end = right - } else { - self.block_start = right - } - } else { - if mode.is_bidi_ltr() { - self.inline_end = right - } else { - self.inline_start = right - } - } - } - - #[inline] - pub fn bottom(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_inline_tb() { - self.inline_end - } else { - self.inline_start - } - } else { - self.block_end - } - } - - #[inline] - pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_inline_tb() { - self.inline_end = bottom - } else { - self.inline_start = bottom - } - } else { - self.block_end = bottom - } - } - - #[inline] - pub fn left(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_vertical_lr() { - self.block_start - } else { - self.block_end - } - } else { - if mode.is_bidi_ltr() { - self.inline_start - } else { - self.inline_end - } - } - } - - #[inline] - pub fn set_left(&mut self, mode: WritingMode, left: T) { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - if mode.is_vertical_lr() { - self.block_start = left - } else { - self.block_end = left - } - } else { - if mode.is_bidi_ltr() { - self.inline_start = left - } else { - self.inline_end = left - } - } - } - - #[inline] - pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin { - if mode_from == mode_to { - self.debug_writing_mode.check(mode_from); - *self - } else { - LogicalMargin::from_physical(mode_to, self.to_physical(mode_from)) - } - } -} - -impl LogicalMargin { - #[inline] - pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D { - self.debug_writing_mode.check(mode); - let top; - let right; - let bottom; - let left; - if mode.is_vertical() { - if mode.is_vertical_lr() { - left = self.block_start.clone(); - right = self.block_end.clone(); - } else { - right = self.block_start.clone(); - left = self.block_end.clone(); - } - if mode.is_inline_tb() { - top = self.inline_start.clone(); - bottom = self.inline_end.clone(); - } else { - bottom = self.inline_start.clone(); - top = self.inline_end.clone(); - } - } else { - top = self.block_start.clone(); - bottom = self.block_end.clone(); - if mode.is_bidi_ltr() { - left = self.inline_start.clone(); - right = self.inline_end.clone(); - } else { - right = self.inline_start.clone(); - left = self.inline_end.clone(); - } - } - SideOffsets2D::new(top, right, bottom, left) - } -} - -impl LogicalMargin { - #[inline] - pub fn is_zero(&self) -> bool { - self.block_start == Zero::zero() && - self.inline_end == Zero::zero() && - self.block_end == Zero::zero() && - self.inline_start == Zero::zero() - } -} - -impl> LogicalMargin { - #[inline] - pub fn inline_start_end(&self) -> T { - self.inline_start + self.inline_end - } - - #[inline] - pub fn block_start_end(&self) -> T { - self.block_start + self.block_end - } - - #[inline] - pub fn start_end(&self, direction: Direction) -> T { - match direction { - Direction::Inline => self.inline_start + self.inline_end, - Direction::Block => self.block_start + self.block_end, - } - } - - #[inline] - pub fn top_bottom(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.inline_start_end() - } else { - self.block_start_end() - } - } - - #[inline] - pub fn left_right(&self, mode: WritingMode) -> T { - self.debug_writing_mode.check(mode); - if mode.is_vertical() { - self.block_start_end() - } else { - self.inline_start_end() - } - } -} - -impl> Add for LogicalMargin { - type Output = LogicalMargin; - - #[inline] - fn add(self, other: LogicalMargin) -> LogicalMargin { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalMargin { - debug_writing_mode: self.debug_writing_mode, - block_start: self.block_start + other.block_start, - inline_end: self.inline_end + other.inline_end, - block_end: self.block_end + other.block_end, - inline_start: self.inline_start + other.inline_start, - } - } -} - -impl> Sub for LogicalMargin { - type Output = LogicalMargin; - - #[inline] - fn sub(self, other: LogicalMargin) -> LogicalMargin { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalMargin { - debug_writing_mode: self.debug_writing_mode, - block_start: self.block_start - other.block_start, - inline_end: self.inline_end - other.inline_end, - block_end: self.block_end - other.block_end, - inline_start: self.inline_start - other.inline_start, - } - } -} - -/// A rectangle in flow-relative dimensions -#[derive(Clone, Copy, Eq, PartialEq)] -#[cfg_attr(feature = "servo", derive(Serialize))] -pub struct LogicalRect { - pub start: LogicalPoint, - pub size: LogicalSize, - debug_writing_mode: DebugWritingMode, -} - -impl Debug for LogicalRect { - fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> { - let writing_mode_string = if cfg!(debug_assertions) { - format!("{:?}, ", self.debug_writing_mode) - } else { - "".to_owned() - }; - - write!( - formatter, - "LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))", - writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b - ) - } -} - -impl LogicalRect { - #[inline] - pub fn zero(mode: WritingMode) -> LogicalRect { - LogicalRect { - start: LogicalPoint::zero(mode), - size: LogicalSize::zero(mode), - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl LogicalRect { - #[inline] - pub fn new( - mode: WritingMode, - inline_start: T, - block_start: T, - inline: T, - block: T, - ) -> LogicalRect { - LogicalRect { - start: LogicalPoint::new(mode, inline_start, block_start), - size: LogicalSize::new(mode, inline, block), - debug_writing_mode: DebugWritingMode::new(mode), - } - } - - #[inline] - pub fn from_point_size( - mode: WritingMode, - start: LogicalPoint, - size: LogicalSize, - ) -> LogicalRect { - start.debug_writing_mode.check(mode); - size.debug_writing_mode.check(mode); - LogicalRect { - start: start, - size: size, - debug_writing_mode: DebugWritingMode::new(mode), - } - } -} - -impl + Sub> LogicalRect { - #[inline] - pub fn from_physical( - mode: WritingMode, - rect: Rect, - container_size: Size2D, - ) -> LogicalRect { - let inline_start; - let block_start; - let inline; - let block; - if mode.is_vertical() { - inline = rect.size.height; - block = rect.size.width; - if mode.is_vertical_lr() { - block_start = rect.origin.x; - } else { - block_start = container_size.width - (rect.origin.x + rect.size.width); - } - if mode.is_inline_tb() { - inline_start = rect.origin.y; - } else { - inline_start = container_size.height - (rect.origin.y + rect.size.height); - } - } else { - inline = rect.size.width; - block = rect.size.height; - block_start = rect.origin.y; - if mode.is_bidi_ltr() { - inline_start = rect.origin.x; - } else { - inline_start = container_size.width - (rect.origin.x + rect.size.width); - } - } - LogicalRect { - start: LogicalPoint::new(mode, inline_start, block_start), - size: LogicalSize::new(mode, inline, block), - debug_writing_mode: DebugWritingMode::new(mode), - } - } - - #[inline] - pub fn inline_end(&self) -> T { - self.start.i + self.size.inline - } - - #[inline] - pub fn block_end(&self) -> T { - self.start.b + self.size.block - } - - #[inline] - pub fn to_physical(&self, mode: WritingMode, container_size: Size2D) -> Rect { - self.debug_writing_mode.check(mode); - let x; - let y; - let width; - let height; - if mode.is_vertical() { - width = self.size.block; - height = self.size.inline; - if mode.is_vertical_lr() { - x = self.start.b; - } else { - x = container_size.width - self.block_end(); - } - if mode.is_inline_tb() { - y = self.start.i; - } else { - y = container_size.height - self.inline_end(); - } - } else { - width = self.size.inline; - height = self.size.block; - y = self.start.b; - if mode.is_bidi_ltr() { - x = self.start.i; - } else { - x = container_size.width - self.inline_end(); - } - } - Rect { - origin: Point2D::new(x, y), - size: Size2D::new(width, height), - } - } - - #[inline] - pub fn convert( - &self, - mode_from: WritingMode, - mode_to: WritingMode, - container_size: Size2D, - ) -> LogicalRect { - if mode_from == mode_to { - self.debug_writing_mode.check(mode_from); - *self - } else { - LogicalRect::from_physical( - mode_to, - self.to_physical(mode_from, container_size), - container_size, - ) - } - } - - pub fn translate_by_size(&self, offset: LogicalSize) -> LogicalRect { - LogicalRect { - start: self.start + offset, - ..*self - } - } - - pub fn translate(&self, offset: &LogicalPoint) -> LogicalRect { - LogicalRect { - start: self.start + - LogicalSize { - inline: offset.i, - block: offset.b, - debug_writing_mode: offset.debug_writing_mode, - }, - size: self.size, - debug_writing_mode: self.debug_writing_mode, - } - } -} - -impl + Sub> LogicalRect { - #[inline] - pub fn union(&self, other: &LogicalRect) -> LogicalRect { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - - let inline_start = min(self.start.i, other.start.i); - let block_start = min(self.start.b, other.start.b); - LogicalRect { - start: LogicalPoint { - i: inline_start, - b: block_start, - debug_writing_mode: self.debug_writing_mode, - }, - size: LogicalSize { - inline: max(self.inline_end(), other.inline_end()) - inline_start, - block: max(self.block_end(), other.block_end()) - block_start, - debug_writing_mode: self.debug_writing_mode, - }, - debug_writing_mode: self.debug_writing_mode, - } - } -} - -impl + Sub> Add> for LogicalRect { - type Output = LogicalRect; - - #[inline] - fn add(self, other: LogicalMargin) -> LogicalRect { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalRect { - start: LogicalPoint { - // Growing a rectangle on the start side means pushing its - // start point on the negative direction. - i: self.start.i - other.inline_start, - b: self.start.b - other.block_start, - debug_writing_mode: self.debug_writing_mode, - }, - size: LogicalSize { - inline: self.size.inline + other.inline_start_end(), - block: self.size.block + other.block_start_end(), - debug_writing_mode: self.debug_writing_mode, - }, - debug_writing_mode: self.debug_writing_mode, - } - } -} - -impl + Sub> Sub> for LogicalRect { - type Output = LogicalRect; - - #[inline] - fn sub(self, other: LogicalMargin) -> LogicalRect { - self.debug_writing_mode - .check_debug(other.debug_writing_mode); - LogicalRect { - start: LogicalPoint { - // Shrinking a rectangle on the start side means pushing its - // start point on the positive direction. - i: self.start.i + other.inline_start, - b: self.start.b + other.block_start, - debug_writing_mode: self.debug_writing_mode, - }, - size: LogicalSize { - inline: self.size.inline - other.inline_start_end(), - block: self.size.block - other.block_start_end(), - debug_writing_mode: self.debug_writing_mode, - }, - debug_writing_mode: self.debug_writing_mode, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PhysicalSide { - Top, - Right, - Bottom, - Left, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PhysicalCorner { - TopLeft, - TopRight, - BottomRight, - BottomLeft, -} diff --git a/components/style/macros.rs b/components/style/macros.rs deleted file mode 100644 index 5f3a1ea463b..00000000000 --- a/components/style/macros.rs +++ /dev/null @@ -1,98 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Various macro helpers. - -macro_rules! exclusive_value { - (($value:ident, $set:expr) => $ident:path) => { - if $value.intersects($set) { - return Err(()); - } else { - $ident - } - }; -} - -#[cfg(feature = "gecko")] -macro_rules! impl_gecko_keyword_conversions { - ($name:ident, $utype:ty) => { - impl From<$utype> for $name { - fn from(bits: $utype) -> $name { - $name::from_gecko_keyword(bits) - } - } - - impl From<$name> for $utype { - fn from(v: $name) -> $utype { - v.to_gecko_keyword() - } - } - }; -} - -macro_rules! trivial_to_computed_value { - ($name:ty) => { - impl $crate::values::computed::ToComputedValue for $name { - type ComputedValue = $name; - - fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self { - self.clone() - } - - fn from_computed_value(other: &Self) -> Self { - other.clone() - } - } - }; -} - -/// A macro to parse an identifier, or return an `UnexpectedIdent` error -/// otherwise. -/// -/// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError` -/// doesn't make a lot of sense to me. -macro_rules! try_match_ident_ignore_ascii_case { - ($input:expr, $( $match_body:tt )*) => {{ - let location = $input.current_source_location(); - let ident = $input.expect_ident_cloned()?; - match_ignore_ascii_case! { &ident, - $( $match_body )* - _ => return Err(location.new_custom_error( - ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone()) - )) - } - }} -} - -#[cfg(feature = "servo")] -macro_rules! local_name { - ($s:tt) => { - $crate::values::GenericAtomIdent(html5ever::local_name!($s)) - }; -} - -#[cfg(feature = "servo")] -macro_rules! ns { - () => { - $crate::values::GenericAtomIdent(html5ever::ns!()) - }; - ($s:tt) => { - $crate::values::GenericAtomIdent(html5ever::ns!($s)) - }; -} - -#[cfg(feature = "gecko")] -macro_rules! local_name { - ($s:tt) => { - $crate::values::AtomIdent(atom!($s)) - }; -} - -/// Asserts the size of a type at compile time. -macro_rules! size_of_test { - ($t: ty, $expected_size: expr) => { - #[cfg(target_pointer_width = "64")] - const_assert_eq!(std::mem::size_of::<$t>(), $expected_size); - }; -} diff --git a/components/style/matching.rs b/components/style/matching.rs deleted file mode 100644 index bc9c797a5d2..00000000000 --- a/components/style/matching.rs +++ /dev/null @@ -1,1096 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! High-level interface to CSS selector matching. - -#![allow(unsafe_code)] -#![deny(missing_docs)] - -use crate::computed_value_flags::ComputedValueFlags; -use crate::context::{CascadeInputs, ElementCascadeInputs, QuirksMode}; -use crate::context::{SharedStyleContext, StyleContext}; -use crate::data::{ElementData, ElementStyles}; -use crate::dom::TElement; -#[cfg(feature = "servo")] -use crate::dom::TNode; -use crate::invalidation::element::restyle_hints::RestyleHint; -use crate::properties::longhands::display::computed_value::T as Display; -use crate::properties::ComputedValues; -use crate::properties::PropertyDeclarationBlock; -use crate::rule_tree::{CascadeLevel, StrongRuleNode}; -use crate::selector_parser::{PseudoElement, RestyleDamage}; -use crate::shared_lock::Locked; -use crate::style_resolver::ResolvedElementStyles; -use crate::style_resolver::{PseudoElementResolution, StyleResolverForElement}; -use crate::stylesheets::layer_rule::LayerOrder; -use crate::stylist::RuleInclusion; -use crate::traversal_flags::TraversalFlags; -use servo_arc::{Arc, ArcBorrow}; - -/// Represents the result of comparing an element's old and new style. -#[derive(Debug)] -pub struct StyleDifference { - /// The resulting damage. - pub damage: RestyleDamage, - - /// Whether any styles changed. - pub change: StyleChange, -} - -/// Represents whether or not the style of an element has changed. -#[derive(Clone, Copy, Debug)] -pub enum StyleChange { - /// The style hasn't changed. - Unchanged, - /// The style has changed. - Changed { - /// Whether only reset structs changed. - reset_only: bool, - }, -} - -/// Whether or not newly computed values for an element need to be cascaded to -/// children (or children might need to be re-matched, e.g., for container -/// queries). -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum ChildRestyleRequirement { - /// Old and new computed values were the same, or we otherwise know that - /// we won't bother recomputing style for children, so we can skip cascading - /// the new values into child elements. - CanSkipCascade = 0, - /// The same as `MustCascadeChildren`, but we only need to actually - /// recascade if the child inherits any explicit reset style. - MustCascadeChildrenIfInheritResetStyle = 1, - /// Old and new computed values were different, so we must cascade the - /// new values to children. - MustCascadeChildren = 2, - /// The same as `MustCascadeChildren`, but for the entire subtree. This is - /// used to handle root font-size updates needing to recascade the whole - /// document. - MustCascadeDescendants = 3, - /// We need to re-match the whole subttree. This is used to handle container - /// query relative unit changes for example. Container query size changes - /// also trigger re-match, but after layout. - MustMatchDescendants = 4, -} - -/// Determines which styles are being cascaded currently. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum CascadeVisitedMode { - /// Cascade the regular, unvisited styles. - Unvisited, - /// Cascade the styles used when an element's relevant link is visited. A - /// "relevant link" is the element being matched if it is a link or the - /// nearest ancestor link. - Visited, -} - -trait PrivateMatchMethods: TElement { - fn replace_single_rule_node( - context: &SharedStyleContext, - level: CascadeLevel, - layer_order: LayerOrder, - pdb: Option>>, - path: &mut StrongRuleNode, - ) -> bool { - let stylist = &context.stylist; - let guards = &context.guards; - - let mut important_rules_changed = false; - let new_node = stylist.rule_tree().update_rule_at_level( - level, - layer_order, - pdb, - path, - guards, - &mut important_rules_changed, - ); - if let Some(n) = new_node { - *path = n; - } - important_rules_changed - } - - /// Updates the rule nodes without re-running selector matching, using just - /// the rule tree, for a specific visited mode. - /// - /// Returns true if an !important rule was replaced. - fn replace_rules_internal( - &self, - replacements: RestyleHint, - context: &mut StyleContext, - cascade_visited: CascadeVisitedMode, - cascade_inputs: &mut ElementCascadeInputs, - ) -> bool { - debug_assert!( - replacements.intersects(RestyleHint::replacements()) && - (replacements & !RestyleHint::replacements()).is_empty() - ); - - let primary_rules = match cascade_visited { - CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(), - CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(), - }; - - let primary_rules = match primary_rules { - Some(r) => r, - None => return false, - }; - - if !context.shared.traversal_flags.for_animation_only() { - let mut result = false; - if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) { - let style_attribute = self.style_attribute(); - result |= Self::replace_single_rule_node( - context.shared, - CascadeLevel::same_tree_author_normal(), - LayerOrder::root(), - style_attribute, - primary_rules, - ); - result |= Self::replace_single_rule_node( - context.shared, - CascadeLevel::same_tree_author_important(), - LayerOrder::root(), - style_attribute, - primary_rules, - ); - // FIXME(emilio): Still a hack! - self.unset_dirty_style_attribute(); - } - return result; - } - - // Animation restyle hints are processed prior to other restyle - // hints in the animation-only traversal. - // - // Non-animation restyle hints will be processed in a subsequent - // normal traversal. - if replacements.intersects(RestyleHint::for_animations()) { - debug_assert!(context.shared.traversal_flags.for_animation_only()); - - if replacements.contains(RestyleHint::RESTYLE_SMIL) { - Self::replace_single_rule_node( - context.shared, - CascadeLevel::SMILOverride, - LayerOrder::root(), - self.smil_override(), - primary_rules, - ); - } - - if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) { - Self::replace_single_rule_node( - context.shared, - CascadeLevel::Transitions, - LayerOrder::root(), - self.transition_rule(&context.shared) - .as_ref() - .map(|a| a.borrow_arc()), - primary_rules, - ); - } - - if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) { - Self::replace_single_rule_node( - context.shared, - CascadeLevel::Animations, - LayerOrder::root(), - self.animation_rule(&context.shared) - .as_ref() - .map(|a| a.borrow_arc()), - primary_rules, - ); - } - } - - false - } - - /// If there is no transition rule in the ComputedValues, it returns None. - fn after_change_style( - &self, - context: &mut StyleContext, - primary_style: &Arc, - ) -> Option> { - let rule_node = primary_style.rules(); - let without_transition_rules = context - .shared - .stylist - .rule_tree() - .remove_transition_rule_if_applicable(rule_node); - if without_transition_rules == *rule_node { - // We don't have transition rule in this case, so return None to let - // the caller use the original ComputedValues. - return None; - } - - // FIXME(bug 868975): We probably need to transition visited style as - // well. - let inputs = CascadeInputs { - rules: Some(without_transition_rules), - visited_rules: primary_style.visited_rules().cloned(), - flags: primary_style.flags.for_cascade_inputs(), - }; - - // Actually `PseudoElementResolution` doesn't really matter. - let style = StyleResolverForElement::new( - *self, - context, - RuleInclusion::All, - PseudoElementResolution::IfApplicable, - ) - .cascade_style_and_visited_with_default_parents(inputs); - - Some(style.0) - } - - fn needs_animations_update( - &self, - context: &mut StyleContext, - old_style: Option<&ComputedValues>, - new_style: &ComputedValues, - pseudo_element: Option, - ) -> bool { - let new_ui_style = new_style.get_ui(); - let new_style_specifies_animations = new_ui_style.specifies_animations(); - - let has_animations = self.has_css_animations(&context.shared, pseudo_element); - if !new_style_specifies_animations && !has_animations { - return false; - } - - let old_style = match old_style { - Some(old) => old, - // If we have no old style but have animations, we may be a - // pseudo-element which was re-created without style changes. - // - // This can happen when we reframe the pseudo-element without - // restyling it (due to content insertion on a flex container or - // such, for example). See bug 1564366. - // - // FIXME(emilio): The really right fix for this is keeping the - // pseudo-element itself around on reframes, but that's a bit - // harder. If we do that we can probably remove quite a lot of the - // EffectSet complexity though, since right now it's stored on the - // parent element for pseudo-elements given we need to keep it - // around... - None => { - return new_style_specifies_animations || new_style.is_pseudo_style(); - }, - }; - - let old_ui_style = old_style.get_ui(); - - let keyframes_could_have_changed = context - .shared - .traversal_flags - .contains(TraversalFlags::ForCSSRuleChanges); - - // If the traversal is triggered due to changes in CSS rules changes, we - // need to try to update all CSS animations on the element if the - // element has or will have CSS animation style regardless of whether - // the animation is running or not. - // - // TODO: We should check which @keyframes were added/changed/deleted and - // update only animations corresponding to those @keyframes. - if keyframes_could_have_changed { - return true; - } - - // If the animations changed, well... - if !old_ui_style.animations_equals(new_ui_style) { - return true; - } - - let old_display = old_style.clone_display(); - let new_display = new_style.clone_display(); - - // If we were display: none, we may need to trigger animations. - if old_display == Display::None && new_display != Display::None { - return new_style_specifies_animations; - } - - // If we are becoming display: none, we may need to stop animations. - if old_display != Display::None && new_display == Display::None { - return has_animations; - } - - // We might need to update animations if writing-mode or direction - // changed, and any of the animations contained logical properties. - // - // We may want to be more granular, but it's probably not worth it. - if new_style.writing_mode != old_style.writing_mode { - return has_animations; - } - - false - } - - fn might_need_transitions_update( - &self, - context: &StyleContext, - old_style: Option<&ComputedValues>, - new_style: &ComputedValues, - pseudo_element: Option, - ) -> bool { - let old_style = match old_style { - Some(v) => v, - None => return false, - }; - - if !self.has_css_transitions(context.shared, pseudo_element) && - !new_style.get_ui().specifies_transitions() - { - return false; - } - - if old_style.clone_display().is_none() { - return false; - } - - return true; - } - - /// Create a SequentialTask for resolving descendants in a SMIL display - /// property animation if the display property changed from none. - #[cfg(feature = "gecko")] - fn handle_display_change_for_smil_if_needed( - &self, - context: &mut StyleContext, - old_values: Option<&ComputedValues>, - new_values: &ComputedValues, - restyle_hints: RestyleHint, - ) { - use crate::context::PostAnimationTasks; - - if !restyle_hints.intersects(RestyleHint::RESTYLE_SMIL) { - return; - } - - if new_values.is_display_property_changed_from_none(old_values) { - // When display value is changed from none to other, we need to - // traverse descendant elements in a subsequent normal - // traversal (we can't traverse them in this animation-only restyle - // since we have no way to know whether the decendants - // need to be traversed at the beginning of the animation-only - // restyle). - let task = crate::context::SequentialTask::process_post_animation( - *self, - PostAnimationTasks::DISPLAY_CHANGED_FROM_NONE_FOR_SMIL, - ); - context.thread_local.tasks.push(task); - } - } - - #[cfg(feature = "gecko")] - fn process_animations( - &self, - context: &mut StyleContext, - old_styles: &mut ElementStyles, - new_styles: &mut ResolvedElementStyles, - restyle_hint: RestyleHint, - important_rules_changed: bool, - ) { - use crate::context::UpdateAnimationsTasks; - - let new_values = new_styles.primary_style_mut(); - let old_values = &old_styles.primary; - if context.shared.traversal_flags.for_animation_only() { - self.handle_display_change_for_smil_if_needed( - context, - old_values.as_deref(), - new_values, - restyle_hint, - ); - return; - } - - // Bug 868975: These steps should examine and update the visited styles - // in addition to the unvisited styles. - - let mut tasks = UpdateAnimationsTasks::empty(); - - if old_values.as_deref().map_or_else( - || new_values.get_ui().specifies_scroll_timelines(), - |old| !old.get_ui().scroll_timelines_equals(new_values.get_ui()), - ) { - tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES); - } - - if old_values.as_deref().map_or_else( - || new_values.get_ui().specifies_view_timelines(), - |old| !old.get_ui().view_timelines_equals(new_values.get_ui()), - ) { - tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES); - } - - if self.needs_animations_update( - context, - old_values.as_deref(), - new_values, - /* pseudo_element = */ None, - ) { - tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS); - } - - let before_change_style = if self.might_need_transitions_update( - context, - old_values.as_deref(), - new_values, - /* pseudo_element = */ None, - ) { - let after_change_style = - if self.has_css_transitions(context.shared, /* pseudo_element = */ None) { - self.after_change_style(context, new_values) - } else { - None - }; - - // In order to avoid creating a SequentialTask for transitions which - // may not be updated, we check it per property to make sure Gecko - // side will really update transition. - let needs_transitions_update = { - // We borrow new_values here, so need to add a scope to make - // sure we release it before assigning a new value to it. - let after_change_style_ref = after_change_style.as_ref().unwrap_or(&new_values); - - self.needs_transitions_update(old_values.as_ref().unwrap(), after_change_style_ref) - }; - - if needs_transitions_update { - if let Some(values_without_transitions) = after_change_style { - *new_values = values_without_transitions; - } - tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS); - - // We need to clone old_values into SequentialTask, so we can - // use it later. - old_values.clone() - } else { - None - } - } else { - None - }; - - if self.has_animations(&context.shared) { - tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES); - if important_rules_changed { - tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS); - } - if new_values.is_display_property_changed_from_none(old_values.as_deref()) { - tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE); - } - } - - if !tasks.is_empty() { - let task = crate::context::SequentialTask::update_animations( - *self, - before_change_style, - tasks, - ); - context.thread_local.tasks.push(task); - } - } - - #[cfg(feature = "servo")] - fn process_animations( - &self, - context: &mut StyleContext, - old_styles: &mut ElementStyles, - new_resolved_styles: &mut ResolvedElementStyles, - _restyle_hint: RestyleHint, - _important_rules_changed: bool, - ) { - use crate::animation::AnimationSetKey; - use crate::dom::TDocument; - - let style_changed = self.process_animations_for_style( - context, - &mut old_styles.primary, - new_resolved_styles.primary_style_mut(), - /* pseudo_element = */ None, - ); - - // If we have modified animation or transitions, we recascade style for this node. - if style_changed { - let primary_style = new_resolved_styles.primary_style(); - let mut rule_node = primary_style.rules().clone(); - let declarations = context.shared.animations.get_all_declarations( - &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()), - context.shared.current_time_for_animations, - self.as_node().owner_doc().shared_lock(), - ); - Self::replace_single_rule_node( - &context.shared, - CascadeLevel::Transitions, - LayerOrder::root(), - declarations.transitions.as_ref().map(|a| a.borrow_arc()), - &mut rule_node, - ); - Self::replace_single_rule_node( - &context.shared, - CascadeLevel::Animations, - LayerOrder::root(), - declarations.animations.as_ref().map(|a| a.borrow_arc()), - &mut rule_node, - ); - - if rule_node != *primary_style.rules() { - let inputs = CascadeInputs { - rules: Some(rule_node), - visited_rules: primary_style.visited_rules().cloned(), - flags: primary_style.flags.for_cascade_inputs(), - }; - - new_resolved_styles.primary.style = StyleResolverForElement::new( - *self, - context, - RuleInclusion::All, - PseudoElementResolution::IfApplicable, - ) - .cascade_style_and_visited_with_default_parents(inputs); - } - } - - self.process_animations_for_pseudo( - context, - old_styles, - new_resolved_styles, - PseudoElement::Before, - ); - self.process_animations_for_pseudo( - context, - old_styles, - new_resolved_styles, - PseudoElement::After, - ); - } - - #[cfg(feature = "servo")] - fn process_animations_for_pseudo( - &self, - context: &mut StyleContext, - old_styles: &mut ElementStyles, - new_resolved_styles: &mut ResolvedElementStyles, - pseudo_element: PseudoElement, - ) { - use crate::animation::AnimationSetKey; - use crate::dom::TDocument; - - let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone()); - let mut style = match new_resolved_styles.pseudos.get(&pseudo_element) { - Some(style) => Arc::clone(style), - None => { - context - .shared - .animations - .cancel_all_animations_for_key(&key); - return; - }, - }; - - let mut old_style = old_styles.pseudos.get(&pseudo_element).cloned(); - self.process_animations_for_style( - context, - &mut old_style, - &mut style, - Some(pseudo_element.clone()), - ); - - let declarations = context.shared.animations.get_all_declarations( - &key, - context.shared.current_time_for_animations, - self.as_node().owner_doc().shared_lock(), - ); - if declarations.is_empty() { - return; - } - - let mut rule_node = style.rules().clone(); - Self::replace_single_rule_node( - &context.shared, - CascadeLevel::Transitions, - LayerOrder::root(), - declarations.transitions.as_ref().map(|a| a.borrow_arc()), - &mut rule_node, - ); - Self::replace_single_rule_node( - &context.shared, - CascadeLevel::Animations, - LayerOrder::root(), - declarations.animations.as_ref().map(|a| a.borrow_arc()), - &mut rule_node, - ); - if rule_node == *style.rules() { - return; - } - - let inputs = CascadeInputs { - rules: Some(rule_node), - visited_rules: style.visited_rules().cloned(), - flags: style.flags.for_cascade_inputs(), - }; - - let new_style = StyleResolverForElement::new( - *self, - context, - RuleInclusion::All, - PseudoElementResolution::IfApplicable, - ) - .cascade_style_and_visited_for_pseudo_with_default_parents( - inputs, - &pseudo_element, - &new_resolved_styles.primary, - ); - - new_resolved_styles - .pseudos - .set(&pseudo_element, new_style.0); - } - - #[cfg(feature = "servo")] - fn process_animations_for_style( - &self, - context: &mut StyleContext, - old_values: &mut Option>, - new_values: &mut Arc, - pseudo_element: Option, - ) -> bool { - use crate::animation::{AnimationSetKey, AnimationState}; - - // We need to call this before accessing the `ElementAnimationSet` from the - // map because this call will do a RwLock::read(). - let needs_animations_update = self.needs_animations_update( - context, - old_values.as_deref(), - new_values, - pseudo_element, - ); - - let might_need_transitions_update = self.might_need_transitions_update( - context, - old_values.as_deref(), - new_values, - pseudo_element, - ); - - let mut after_change_style = None; - if might_need_transitions_update { - after_change_style = self.after_change_style(context, new_values); - } - - let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element); - let shared_context = context.shared; - let mut animation_set = shared_context - .animations - .sets - .write() - .remove(&key) - .unwrap_or_default(); - - // Starting animations is expensive, because we have to recalculate the style - // for all the keyframes. We only want to do this if we think that there's a - // chance that the animations really changed. - if needs_animations_update { - let mut resolver = StyleResolverForElement::new( - *self, - context, - RuleInclusion::All, - PseudoElementResolution::IfApplicable, - ); - - animation_set.update_animations_for_new_style::( - *self, - &shared_context, - &new_values, - &mut resolver, - ); - } - - animation_set.update_transitions_for_new_style( - might_need_transitions_update, - &shared_context, - old_values.as_ref(), - after_change_style.as_ref().unwrap_or(new_values), - ); - - // We clear away any finished transitions, but retain animations, because they - // might still be used for proper calculation of `animation-fill-mode`. This - // should change the computed values in the style, so we don't need to mark - // this set as dirty. - animation_set - .transitions - .retain(|transition| transition.state != AnimationState::Finished); - - // If the ElementAnimationSet is empty, and don't store it in order to - // save memory and to avoid extra processing later. - let changed_animations = animation_set.dirty; - if !animation_set.is_empty() { - animation_set.dirty = false; - shared_context - .animations - .sets - .write() - .insert(key, animation_set); - } - - changed_animations - } - - /// Computes and applies non-redundant damage. - fn accumulate_damage_for( - &self, - shared_context: &SharedStyleContext, - damage: &mut RestyleDamage, - old_values: &ComputedValues, - new_values: &ComputedValues, - pseudo: Option<&PseudoElement>, - ) -> ChildRestyleRequirement { - debug!("accumulate_damage_for: {:?}", self); - debug_assert!(!shared_context - .traversal_flags - .contains(TraversalFlags::FinalAnimationTraversal)); - - let difference = self.compute_style_difference(old_values, new_values, pseudo); - - *damage |= difference.damage; - - debug!(" > style difference: {:?}", difference); - - // We need to cascade the children in order to ensure the correct - // propagation of inherited computed value flags. - if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() { - debug!( - " > flags changed: {:?} != {:?}", - old_values.flags, new_values.flags - ); - return ChildRestyleRequirement::MustCascadeChildren; - } - - match difference.change { - StyleChange::Unchanged => return ChildRestyleRequirement::CanSkipCascade, - StyleChange::Changed { reset_only } => { - // If inherited properties changed, the best we can do is - // cascade the children. - if !reset_only { - return ChildRestyleRequirement::MustCascadeChildren; - } - }, - } - - let old_display = old_values.clone_display(); - let new_display = new_values.clone_display(); - - if old_display != new_display { - // If we used to be a display: none element, and no longer are, our - // children need to be restyled because they're unstyled. - if old_display == Display::None { - return ChildRestyleRequirement::MustCascadeChildren; - } - // Blockification of children may depend on our display value, - // so we need to actually do the recascade. We could potentially - // do better, but it doesn't seem worth it. - if old_display.is_item_container() != new_display.is_item_container() { - return ChildRestyleRequirement::MustCascadeChildren; - } - // We may also need to blockify and un-blockify descendants if our - // display goes from / to display: contents, since the "layout - // parent style" changes. - if old_display.is_contents() || new_display.is_contents() { - return ChildRestyleRequirement::MustCascadeChildren; - } - // Line break suppression may also be affected if the display - // type changes from ruby to non-ruby. - #[cfg(feature = "gecko")] - { - if old_display.is_ruby_type() != new_display.is_ruby_type() { - return ChildRestyleRequirement::MustCascadeChildren; - } - } - } - - // Children with justify-items: auto may depend on our - // justify-items property value. - // - // Similarly, we could potentially do better, but this really - // seems not common enough to care about. - #[cfg(feature = "gecko")] - { - use crate::values::specified::align::AlignFlags; - - let old_justify_items = old_values.get_position().clone_justify_items(); - let new_justify_items = new_values.get_position().clone_justify_items(); - - let was_legacy_justify_items = - old_justify_items.computed.0.contains(AlignFlags::LEGACY); - - let is_legacy_justify_items = new_justify_items.computed.0.contains(AlignFlags::LEGACY); - - if is_legacy_justify_items != was_legacy_justify_items { - return ChildRestyleRequirement::MustCascadeChildren; - } - - if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed - { - return ChildRestyleRequirement::MustCascadeChildren; - } - } - - #[cfg(feature = "servo")] - { - // We may need to set or propagate the CAN_BE_FRAGMENTED bit - // on our children. - if old_values.is_multicol() != new_values.is_multicol() { - return ChildRestyleRequirement::MustCascadeChildren; - } - } - - // We could prove that, if our children don't inherit reset - // properties, we can stop the cascade. - ChildRestyleRequirement::MustCascadeChildrenIfInheritResetStyle - } -} - -impl PrivateMatchMethods for E {} - -/// The public API that elements expose for selector matching. -pub trait MatchMethods: TElement { - /// Returns the closest parent element that doesn't have a display: contents - /// style (and thus generates a box). - /// - /// This is needed to correctly handle blockification of flex and grid - /// items. - /// - /// Returns itself if the element has no parent. In practice this doesn't - /// happen because the root element is blockified per spec, but it could - /// happen if we decide to not blockify for roots of disconnected subtrees, - /// which is a kind of dubious behavior. - fn layout_parent(&self) -> Self { - let mut current = self.clone(); - loop { - current = match current.traversal_parent() { - Some(el) => el, - None => return current, - }; - - let is_display_contents = current - .borrow_data() - .unwrap() - .styles - .primary() - .is_display_contents(); - - if !is_display_contents { - return current; - } - } - } - - /// Updates the styles with the new ones, diffs them, and stores the restyle - /// damage. - fn finish_restyle( - &self, - context: &mut StyleContext, - data: &mut ElementData, - mut new_styles: ResolvedElementStyles, - important_rules_changed: bool, - ) -> ChildRestyleRequirement { - use std::cmp; - - self.process_animations( - context, - &mut data.styles, - &mut new_styles, - data.hint, - important_rules_changed, - ); - - // First of all, update the styles. - let old_styles = data.set_styles(new_styles); - - let new_primary_style = data.styles.primary.as_ref().unwrap(); - - let mut restyle_requirement = ChildRestyleRequirement::CanSkipCascade; - let is_root = new_primary_style - .flags - .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE); - let is_container = !new_primary_style - .get_box() - .clone_container_type() - .is_normal(); - if is_root || is_container { - let new_font_size = new_primary_style.get_font().clone_font_size(); - let old_font_size = old_styles - .primary - .as_ref() - .map(|s| s.get_font().clone_font_size()); - - if old_font_size != Some(new_font_size) { - if is_root { - let device = context.shared.stylist.device(); - debug_assert!(self.owner_doc_matches_for_testing(device)); - device.set_root_font_size(new_font_size.computed_size().into()); - if device.used_root_font_size() { - // If the root font-size changed since last time, and something - // in the document did use rem units, ensure we recascade the - // entire tree. - restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants; - } - } - - if is_container && old_font_size.is_some() { - // TODO(emilio): Maybe only do this if we were matched - // against relative font sizes? - // Also, maybe we should do this as well for font-family / - // etc changes (for ex/ch/ic units to work correctly)? We - // should probably do the optimization mentioned above if - // so. - restyle_requirement = ChildRestyleRequirement::MustMatchDescendants; - } - } - } - - if context.shared.stylist.quirks_mode() == QuirksMode::Quirks { - if self.is_html_document_body_element() { - // NOTE(emilio): We _could_ handle dynamic changes to it if it - // changes and before we reach our children the cascade stops, - // but we don't track right now whether we use the document body - // color, and nobody else handles that properly anyway. - let device = context.shared.stylist.device(); - - // Needed for the "inherit from body" quirk. - let text_color = new_primary_style.get_inherited_text().clone_color(); - device.set_body_text_color(text_color); - } - } - - // Don't accumulate damage if we're in the final animation traversal. - if context - .shared - .traversal_flags - .contains(TraversalFlags::FinalAnimationTraversal) - { - return ChildRestyleRequirement::MustCascadeChildren; - } - - // Also, don't do anything if there was no style. - let old_primary_style = match old_styles.primary { - Some(s) => s, - None => return ChildRestyleRequirement::MustCascadeChildren, - }; - - let old_container_type = old_primary_style.clone_container_type(); - let new_container_type = new_primary_style.clone_container_type(); - if old_container_type != new_container_type && !new_container_type.is_size_container_type() - { - // Stopped being a size container. Re-evaluate container queries and units on all our descendants. - // Changes into and between different size containment is handled in `UpdateContainerQueryStyles`. - restyle_requirement = ChildRestyleRequirement::MustMatchDescendants; - } else if old_container_type.is_size_container_type() && - !old_primary_style.is_display_contents() && - new_primary_style.is_display_contents() - { - // Also re-evaluate when a container gets 'display: contents', since size queries will now evaluate to unknown. - // Other displays like 'inline' will keep generating a box, so they are handled in `UpdateContainerQueryStyles`. - restyle_requirement = ChildRestyleRequirement::MustMatchDescendants; - } - - restyle_requirement = cmp::max( - restyle_requirement, - self.accumulate_damage_for( - context.shared, - &mut data.damage, - &old_primary_style, - new_primary_style, - None, - ), - ); - - if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() { - // This is the common case; no need to examine pseudos here. - return restyle_requirement; - } - - let pseudo_styles = old_styles - .pseudos - .as_array() - .iter() - .zip(data.styles.pseudos.as_array().iter()); - - for (i, (old, new)) in pseudo_styles.enumerate() { - match (old, new) { - (&Some(ref old), &Some(ref new)) => { - self.accumulate_damage_for( - context.shared, - &mut data.damage, - old, - new, - Some(&PseudoElement::from_eager_index(i)), - ); - }, - (&None, &None) => {}, - _ => { - // It's possible that we're switching from not having - // ::before/::after at all to having styles for them but not - // actually having a useful pseudo-element. Check for that - // case. - let pseudo = PseudoElement::from_eager_index(i); - let new_pseudo_should_exist = - new.as_ref().map_or(false, |s| pseudo.should_exist(s)); - let old_pseudo_should_exist = - old.as_ref().map_or(false, |s| pseudo.should_exist(s)); - if new_pseudo_should_exist != old_pseudo_should_exist { - data.damage |= RestyleDamage::reconstruct(); - return restyle_requirement; - } - }, - } - } - - restyle_requirement - } - - /// Updates the rule nodes without re-running selector matching, using just - /// the rule tree. - /// - /// Returns true if an !important rule was replaced. - fn replace_rules( - &self, - replacements: RestyleHint, - context: &mut StyleContext, - cascade_inputs: &mut ElementCascadeInputs, - ) -> bool { - let mut result = false; - result |= self.replace_rules_internal( - replacements, - context, - CascadeVisitedMode::Unvisited, - cascade_inputs, - ); - result |= self.replace_rules_internal( - replacements, - context, - CascadeVisitedMode::Visited, - cascade_inputs, - ); - result - } - - /// Given the old and new style of this element, and whether it's a - /// pseudo-element, compute the restyle damage used to determine which - /// kind of layout or painting operations we'll need. - fn compute_style_difference( - &self, - old_values: &ComputedValues, - new_values: &ComputedValues, - pseudo: Option<&PseudoElement>, - ) -> StyleDifference { - debug_assert!(pseudo.map_or(true, |p| p.is_eager())); - RestyleDamage::compute_style_difference(old_values, new_values) - } -} - -impl MatchMethods for E {} diff --git a/components/style/media_queries/media_list.rs b/components/style/media_queries/media_list.rs deleted file mode 100644 index 3c2ba9ee5c1..00000000000 --- a/components/style/media_queries/media_list.rs +++ /dev/null @@ -1,150 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A media query list: -//! -//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list - -use super::{Device, MediaQuery, Qualifier}; -use crate::context::QuirksMode; -use crate::error_reporting::ContextualParseError; -use crate::parser::ParserContext; -use crate::queries::condition::KleeneValue; -use crate::values::computed; -use cssparser::{Delimiter, Parser}; -use cssparser::{ParserInput, Token}; - -/// A type that encapsulates a media query list. -#[derive(Clone, MallocSizeOf, ToCss, ToShmem)] -#[css(comma, derive_debug)] -pub struct MediaList { - /// The list of media queries. - #[css(iterable)] - pub media_queries: Vec, -} - -impl MediaList { - /// Parse a media query list from CSS. - /// - /// Always returns a media query list. If any invalid media query is - /// found, the media query list is only filled with the equivalent of - /// "not all", see: - /// - /// - pub fn parse(context: &ParserContext, input: &mut Parser) -> Self { - if input.is_exhausted() { - return Self::empty(); - } - - let mut media_queries = vec![]; - loop { - let start_position = input.position(); - match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) { - Ok(mq) => { - media_queries.push(mq); - }, - Err(err) => { - media_queries.push(MediaQuery::never_matching()); - let location = err.location; - let error = ContextualParseError::InvalidMediaRule( - input.slice_from(start_position), - err, - ); - context.log_css_error(location, error); - }, - } - - match input.next() { - Ok(&Token::Comma) => {}, - Ok(_) => unreachable!(), - Err(_) => break, - } - } - - MediaList { media_queries } - } - - /// Create an empty MediaList. - pub fn empty() -> Self { - MediaList { - media_queries: vec![], - } - } - - /// Evaluate a whole `MediaList` against `Device`. - pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> bool { - // Check if it is an empty media query list or any queries match. - // https://drafts.csswg.org/mediaqueries-4/#mq-list - if self.media_queries.is_empty() { - return true; - } - - computed::Context::for_media_query_evaluation(device, quirks_mode, |context| { - self.media_queries.iter().any(|mq| { - let mut query_match = if mq.media_type.matches(device.media_type()) { - mq.condition - .as_ref() - .map_or(KleeneValue::True, |c| c.matches(context)) - } else { - KleeneValue::False - }; - - // Apply the logical NOT qualifier to the result - if matches!(mq.qualifier, Some(Qualifier::Not)) { - query_match = !query_match; - } - query_match.to_bool(/* unknown = */ false) - }) - }) - } - - /// Whether this `MediaList` contains no media queries. - pub fn is_empty(&self) -> bool { - self.media_queries.is_empty() - } - - /// Whether this `MediaList` depends on the viewport size. - pub fn is_viewport_dependent(&self) -> bool { - self.media_queries.iter().any(|q| q.is_viewport_dependent()) - } - - /// Append a new media query item to the media list. - /// - /// - /// Returns true if added, false if fail to parse the medium string. - pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool { - let mut input = ParserInput::new(new_medium); - let mut parser = Parser::new(&mut input); - let new_query = match MediaQuery::parse(&context, &mut parser) { - Ok(query) => query, - Err(_) => { - return false; - }, - }; - // This algorithm doesn't actually matches the current spec, - // but it matches the behavior of Gecko and Edge. - // See https://github.com/w3c/csswg-drafts/issues/697 - self.media_queries.retain(|query| query != &new_query); - self.media_queries.push(new_query); - true - } - - /// Delete a media query from the media list. - /// - /// - /// Returns true if found and deleted, false otherwise. - pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool { - let mut input = ParserInput::new(old_medium); - let mut parser = Parser::new(&mut input); - let old_query = match MediaQuery::parse(context, &mut parser) { - Ok(query) => query, - Err(_) => { - return false; - }, - }; - let old_len = self.media_queries.len(); - self.media_queries.retain(|query| query != &old_query); - old_len != self.media_queries.len() - } -} diff --git a/components/style/media_queries/media_query.rs b/components/style/media_queries/media_query.rs deleted file mode 100644 index c30a4453930..00000000000 --- a/components/style/media_queries/media_query.rs +++ /dev/null @@ -1,193 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A media query: -//! -//! https://drafts.csswg.org/mediaqueries/#typedef-media-query - -use crate::parser::ParserContext; -use crate::queries::{FeatureFlags, FeatureType, QueryCondition}; -use crate::str::string_as_ascii_lowercase; -use crate::values::CustomIdent; -use crate::Atom; -use cssparser::Parser; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ToCss}; - -/// -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] -pub enum Qualifier { - /// Hide a media query from legacy UAs: - /// - Only, - /// Negate a media query: - /// - Not, -} - -/// -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] -pub struct MediaType(pub CustomIdent); - -impl MediaType { - /// The `screen` media type. - pub fn screen() -> Self { - MediaType(CustomIdent(atom!("screen"))) - } - - /// The `print` media type. - pub fn print() -> Self { - MediaType(CustomIdent(atom!("print"))) - } - - fn parse(name: &str) -> Result { - // From https://drafts.csswg.org/mediaqueries/#mq-syntax: - // - // The production does not include the keywords only, not, and, or, and layer. - // - // Here we also perform the to-ascii-lowercase part of the serialization - // algorithm: https://drafts.csswg.org/cssom/#serializing-media-queries - match_ignore_ascii_case! { name, - "not" | "or" | "and" | "only" | "layer" => Err(()), - _ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))), - } - } -} - -/// A [media query][mq]. -/// -/// [mq]: https://drafts.csswg.org/mediaqueries/ -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] -pub struct MediaQuery { - /// The qualifier for this query. - pub qualifier: Option, - /// The media type for this query, that can be known, unknown, or "all". - pub media_type: MediaQueryType, - /// The condition that this media query contains. This cannot have `or` - /// in the first level. - pub condition: Option, -} - -impl ToCss for MediaQuery { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - if let Some(qual) = self.qualifier { - qual.to_css(dest)?; - dest.write_char(' ')?; - } - - match self.media_type { - MediaQueryType::All => { - // We need to print "all" if there's a qualifier, or there's - // just an empty list of expressions. - // - // Otherwise, we'd serialize media queries like "(min-width: - // 40px)" in "all (min-width: 40px)", which is unexpected. - if self.qualifier.is_some() || self.condition.is_none() { - dest.write_str("all")?; - } - }, - MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?, - } - - let condition = match self.condition { - Some(ref c) => c, - None => return Ok(()), - }; - - if self.media_type != MediaQueryType::All || self.qualifier.is_some() { - dest.write_str(" and ")?; - } - - condition.to_css(dest) - } -} - -impl MediaQuery { - /// Return a media query that never matches, used for when we fail to parse - /// a given media query. - pub fn never_matching() -> Self { - Self { - qualifier: Some(Qualifier::Not), - media_type: MediaQueryType::All, - condition: None, - } - } - - /// Returns whether this media query depends on the viewport. - pub fn is_viewport_dependent(&self) -> bool { - self.condition.as_ref().map_or(false, |c| { - return c - .cumulative_flags() - .contains(FeatureFlags::VIEWPORT_DEPENDENT); - }) - } - - /// Parse a media query given css input. - /// - /// Returns an error if any of the expressions is unknown. - pub fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let (qualifier, explicit_media_type) = input - .try_parse(|input| -> Result<_, ()> { - let qualifier = input.try_parse(Qualifier::parse).ok(); - let ident = input.expect_ident().map_err(|_| ())?; - let media_type = MediaQueryType::parse(&ident)?; - Ok((qualifier, Some(media_type))) - }) - .unwrap_or_default(); - - let condition = if explicit_media_type.is_none() { - Some(QueryCondition::parse(context, input, FeatureType::Media)?) - } else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() { - Some(QueryCondition::parse_disallow_or( - context, - input, - FeatureType::Media, - )?) - } else { - None - }; - - let media_type = explicit_media_type.unwrap_or(MediaQueryType::All); - Ok(Self { - qualifier, - media_type, - condition, - }) - } -} - -/// -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)] -pub enum MediaQueryType { - /// A media type that matches every device. - All, - /// A specific media type. - Concrete(MediaType), -} - -impl MediaQueryType { - fn parse(ident: &str) -> Result { - match_ignore_ascii_case! { ident, - "all" => return Ok(MediaQueryType::All), - _ => (), - }; - - // If parseable, accept this type as a concrete type. - MediaType::parse(ident).map(MediaQueryType::Concrete) - } - - /// Returns whether this media query type matches a MediaType. - pub fn matches(&self, other: MediaType) -> bool { - match *self { - MediaQueryType::All => true, - MediaQueryType::Concrete(ref known_type) => *known_type == other, - } - } -} diff --git a/components/style/media_queries/mod.rs b/components/style/media_queries/mod.rs deleted file mode 100644 index 833f6f53cb9..00000000000 --- a/components/style/media_queries/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! [Media queries][mq]. -//! -//! [mq]: https://drafts.csswg.org/mediaqueries/ - -mod media_list; -mod media_query; - -pub use self::media_list::MediaList; -pub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier}; - -#[cfg(feature = "gecko")] -pub use crate::gecko::media_queries::Device; -#[cfg(feature = "servo")] -pub use crate::servo::media_queries::Device; diff --git a/components/style/parallel.rs b/components/style/parallel.rs deleted file mode 100644 index 2d0e0c7ffcc..00000000000 --- a/components/style/parallel.rs +++ /dev/null @@ -1,197 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Implements parallel traversal over the DOM tree. -//! -//! This traversal is based on Rayon, and therefore its safety is largely -//! verified by the type system. -//! -//! The primary trickiness and fine print for the above relates to the -//! thread safety of the DOM nodes themselves. Accessing a DOM element -//! concurrently on multiple threads is actually mostly "safe", since all -//! the mutable state is protected by an AtomicRefCell, and so we'll -//! generally panic if something goes wrong. Still, we try to to enforce our -//! thread invariants at compile time whenever possible. As such, TNode and -//! TElement are not Send, so ordinary style system code cannot accidentally -//! share them with other threads. In the parallel traversal, we explicitly -//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may -//! be sent to other threads. This occurs in only a handful of places and is -//! easy to grep for. At the time of this writing, there is no other unsafe -//! code in the parallel traversal. - -#![deny(missing_docs)] - -use crate::context::{StyleContext, ThreadLocalStyleContext}; -use crate::dom::{OpaqueNode, SendNode, TElement}; -use crate::scoped_tls::ScopedTLS; -use crate::traversal::{DomTraversal, PerLevelTraversalData}; -use rayon; -use std::collections::VecDeque; - -/// The minimum stack size for a thread in the styling pool, in kilobytes. -#[cfg(feature = "gecko")] -pub const STYLE_THREAD_STACK_SIZE_KB: usize = 256; - -/// The minimum stack size for a thread in the styling pool, in kilobytes. -/// Servo requires a bigger stack in debug builds. -#[cfg(feature = "servo")] -pub const STYLE_THREAD_STACK_SIZE_KB: usize = 512; - -/// The stack margin. If we get this deep in the stack, we will skip recursive -/// optimizations to ensure that there is sufficient room for non-recursive work. -/// -/// We allocate large safety margins because certain OS calls can use very large -/// amounts of stack space [1]. Reserving a larger-than-necessary stack costs us -/// address space, but if we keep our safety margin big, we will generally avoid -/// committing those extra pages, and only use them in edge cases that would -/// otherwise cause crashes. -/// -/// When measured with 128KB stacks and 40KB margin, we could support 53 -/// levels of recursion before the limiter kicks in, on x86_64-Linux [2]. When -/// we doubled the stack size, we added it all to the safety margin, so we should -/// be able to get the same amount of recursion. -/// -/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1395708#c15 -/// [2] See Gecko bug 1376883 for more discussion on the measurements. -pub const STACK_SAFETY_MARGIN_KB: usize = 168; - -/// A callback to create our thread local context. This needs to be -/// out of line so we don't allocate stack space for the entire struct -/// in the caller. -#[inline(never)] -fn create_thread_local_context<'scope, E>(slot: &mut Option>) -where - E: TElement + 'scope, -{ - *slot = Some(ThreadLocalStyleContext::new()); -} - -// Sends one chunk of work to the thread-pool. -fn distribute_one_chunk<'a, 'scope, E, D>( - items: VecDeque>, - traversal_root: OpaqueNode, - work_unit_max: usize, - traversal_data: PerLevelTraversalData, - scope: &'a rayon::ScopeFifo<'scope>, - traversal: &'scope D, - tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext>, -) where - E: TElement + 'scope, - D: DomTraversal, -{ - scope.spawn_fifo(move |scope| { - #[cfg(feature = "gecko")] - gecko_profiler_label!(Layout, StyleComputation); - let mut tlc = tls.ensure(create_thread_local_context); - let mut context = StyleContext { - shared: traversal.shared_context(), - thread_local: &mut *tlc, - }; - style_trees( - &mut context, - items, - traversal_root, - work_unit_max, - static_prefs::pref!("layout.css.stylo-local-work-queue.in-worker") as usize, - traversal_data, - Some(scope), - traversal, - Some(tls), - ); - }) -} - -/// Distributes all items into the thread pool, in `work_unit_max` chunks. -fn distribute_work<'a, 'scope, E, D>( - mut items: VecDeque>, - traversal_root: OpaqueNode, - work_unit_max: usize, - traversal_data: PerLevelTraversalData, - scope: &'a rayon::ScopeFifo<'scope>, - traversal: &'scope D, - tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext>, -) where - E: TElement + 'scope, - D: DomTraversal, -{ - while items.len() > work_unit_max { - let rest = items.split_off(work_unit_max); - distribute_one_chunk( - items, - traversal_root, - work_unit_max, - traversal_data, - scope, - traversal, - tls, - ); - items = rest; - } - distribute_one_chunk( - items, - traversal_root, - work_unit_max, - traversal_data, - scope, - traversal, - tls, - ); -} - -/// Processes `discovered` items, possibly spawning work in other threads as needed. -#[inline] -pub fn style_trees<'a, 'scope, E, D>( - context: &mut StyleContext, - mut discovered: VecDeque>, - traversal_root: OpaqueNode, - work_unit_max: usize, - local_queue_size: usize, - mut traversal_data: PerLevelTraversalData, - scope: Option<&'a rayon::ScopeFifo<'scope>>, - traversal: &'scope D, - tls: Option<&'scope ScopedTLS<'scope, ThreadLocalStyleContext>>, -) where - E: TElement + 'scope, - D: DomTraversal, -{ - let mut nodes_remaining_at_current_depth = discovered.len(); - while let Some(node) = discovered.pop_front() { - let mut children_to_process = 0isize; - traversal.process_preorder(&traversal_data, context, *node, |n| { - children_to_process += 1; - discovered.push_back(unsafe { SendNode::new(n) }); - }); - - traversal.handle_postorder_traversal(context, traversal_root, *node, children_to_process); - - nodes_remaining_at_current_depth -= 1; - - // If we have enough children at the next depth in the DOM, spawn them to a different job - // relatively soon, while keeping always at least `local_queue_size` worth of work for - // ourselves. - let discovered_children = discovered.len() - nodes_remaining_at_current_depth; - if discovered_children >= work_unit_max && - discovered.len() >= local_queue_size + work_unit_max && - scope.is_some() - { - let kept_work = std::cmp::max(nodes_remaining_at_current_depth, local_queue_size); - let mut traversal_data_copy = traversal_data.clone(); - traversal_data_copy.current_dom_depth += 1; - distribute_work( - discovered.split_off(kept_work), - traversal_root, - work_unit_max, - traversal_data_copy, - scope.unwrap(), - traversal, - tls.unwrap(), - ); - } - - if nodes_remaining_at_current_depth == 0 { - traversal_data.current_dom_depth += 1; - nodes_remaining_at_current_depth = discovered.len(); - } - } -} diff --git a/components/style/parser.rs b/components/style/parser.rs deleted file mode 100644 index 8d8d408f53f..00000000000 --- a/components/style/parser.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! The context within which CSS code is parsed. - -use crate::context::QuirksMode; -use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; -use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData}; -use crate::use_counters::UseCounters; -use cssparser::{Parser, SourceLocation, UnicodeRange}; -use std::borrow::Cow; -use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator}; - -/// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko. -#[cfg(feature = "gecko")] -#[inline] -pub fn assert_parsing_mode_match() { - use crate::gecko_bindings::structs; - - macro_rules! check_parsing_modes { - ( $( $a:ident => $b:path ),*, ) => { - if cfg!(debug_assertions) { - let mut modes = ParsingMode::all(); - $( - assert_eq!(structs::$a as usize, $b.bits() as usize, stringify!($b)); - modes.remove($b); - )* - assert_eq!(modes, ParsingMode::empty(), "all ParsingMode bits should have an assertion"); - } - } - } - - check_parsing_modes! { - ParsingMode_Default => ParsingMode::DEFAULT, - ParsingMode_AllowUnitlessLength => ParsingMode::ALLOW_UNITLESS_LENGTH, - ParsingMode_AllowAllNumericValues => ParsingMode::ALLOW_ALL_NUMERIC_VALUES, - } -} - -/// The data that the parser needs from outside in order to parse a stylesheet. -pub struct ParserContext<'a> { - /// The `Origin` of the stylesheet, whether it's a user, author or - /// user-agent stylesheet. - pub stylesheet_origin: Origin, - /// The extra data we need for resolving url values. - pub url_data: &'a UrlExtraData, - /// The current rule types, if any. - pub rule_types: CssRuleTypes, - /// The mode to use when parsing. - pub parsing_mode: ParsingMode, - /// The quirks mode of this stylesheet. - pub quirks_mode: QuirksMode, - /// The active error reporter, or none if error reporting is disabled. - error_reporter: Option<&'a dyn ParseErrorReporter>, - /// The currently active namespaces. - pub namespaces: Cow<'a, Namespaces>, - /// The use counters we want to record while parsing style rules, if any. - pub use_counters: Option<&'a UseCounters>, -} - -impl<'a> ParserContext<'a> { - /// Create a parser context. - #[inline] - pub fn new( - stylesheet_origin: Origin, - url_data: &'a UrlExtraData, - rule_type: Option, - parsing_mode: ParsingMode, - quirks_mode: QuirksMode, - namespaces: Cow<'a, Namespaces>, - error_reporter: Option<&'a dyn ParseErrorReporter>, - use_counters: Option<&'a UseCounters>, - ) -> Self { - Self { - stylesheet_origin, - url_data, - rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(), - parsing_mode, - quirks_mode, - error_reporter, - namespaces, - use_counters, - } - } - - /// Temporarily sets the rule_type and executes the callback function, returning its result. - pub fn nest_for_rule( - &mut self, - rule_type: CssRuleType, - cb: impl FnOnce(&mut Self) -> R, - ) -> R { - let old_rule_types = self.rule_types; - self.rule_types.insert(rule_type); - let r = cb(self); - self.rule_types = old_rule_types; - r - } - - /// Whether we're in a @page rule. - #[inline] - pub fn in_page_rule(&self) -> bool { - self.rule_types.contains(CssRuleType::Page) - } - - /// Get the rule type, which assumes that one is available. - pub fn rule_types(&self) -> CssRuleTypes { - self.rule_types - } - - /// Returns whether CSS error reporting is enabled. - #[inline] - pub fn error_reporting_enabled(&self) -> bool { - self.error_reporter.is_some() - } - - /// Record a CSS parse error with this context’s error reporting. - pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) { - let error_reporter = match self.error_reporter { - Some(r) => r, - None => return, - }; - - error_reporter.report_error(self.url_data, location, error) - } - - /// Whether we're in a user-agent stylesheet. - #[inline] - pub fn in_ua_sheet(&self) -> bool { - self.stylesheet_origin == Origin::UserAgent - } - - /// Returns whether chrome-only rules should be parsed. - #[inline] - pub fn chrome_rules_enabled(&self) -> bool { - self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User - } - - /// Whether we're in a user-agent stylesheet or chrome rules are enabled. - #[inline] - pub fn in_ua_or_chrome_sheet(&self) -> bool { - self.in_ua_sheet() || self.chrome_rules_enabled() - } -} - -/// A trait to abstract parsing of a specified value given a `ParserContext` and -/// CSS input. -/// -/// This can be derived on keywords with `#[derive(Parse)]`. -/// -/// The derive code understands the following attributes on each of the variants: -/// -/// * `#[parse(aliases = "foo,bar")]` can be used to alias a value with another -/// at parse-time. -/// -/// * `#[parse(condition = "function")]` can be used to make the parsing of the -/// value conditional on `function`, which needs to fulfill -/// `fn(&ParserContext) -> bool`. -pub trait Parse: Sized { - /// Parse a value of this type. - /// - /// Returns an error on failure. - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result>; -} - -impl Parse for Vec -where - T: Parse + OneOrMoreSeparated, - ::S: Separator, -{ - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - ::S::parse(input, |i| T::parse(context, i)) - } -} - -impl Parse for Box -where - T: Parse, -{ - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - T::parse(context, input).map(Box::new) - } -} - -impl Parse for crate::OwnedStr { - fn parse<'i, 't>( - _: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - Ok(input.expect_string()?.as_ref().to_owned().into()) - } -} - -impl Parse for UnicodeRange { - fn parse<'i, 't>( - _: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - Ok(UnicodeRange::parse(input)?) - } -} diff --git a/components/style/piecewise_linear.rs b/components/style/piecewise_linear.rs deleted file mode 100644 index 84ccb7061c3..00000000000 --- a/components/style/piecewise_linear.rs +++ /dev/null @@ -1,293 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A piecewise linear function, following CSS linear easing -use crate::values::computed::Percentage; -use core::slice::Iter; -/// draft as in https://github.com/w3c/csswg-drafts/pull/6533. -use euclid::approxeq::ApproxEq; -use itertools::Itertools; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; - -use crate::values::CSSFloat; - -type ValueType = CSSFloat; -/// a single entry in a piecewise linear function. -#[allow(missing_docs)] -#[derive( - Clone, - Copy, - Debug, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToResolvedValue, - Serialize, - Deserialize, -)] -#[repr(C)] -pub struct PiecewiseLinearFunctionEntry { - pub x: ValueType, - pub y: ValueType, -} - -impl ToCss for PiecewiseLinearFunctionEntry { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write, - { - self.y.to_css(dest)?; - dest.write_char(' ')?; - Percentage(self.x).to_css(dest) - } -} - -/// Representation of a piecewise linear function, a series of linear functions. -#[derive( - Default, - Clone, - Debug, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToResolvedValue, - ToCss, - Serialize, - Deserialize, -)] -#[repr(C)] -#[css(comma)] -pub struct PiecewiseLinearFunction { - #[css(iterable)] - entries: crate::OwnedSlice, -} - -/// Parameters to define one linear stop. -pub type PiecewiseLinearFunctionBuildParameters = (CSSFloat, Option); - -impl PiecewiseLinearFunction { - /// Interpolate y value given x and two points. The linear function will be rooted at the asymptote. - fn interpolate( - x: ValueType, - prev: PiecewiseLinearFunctionEntry, - next: PiecewiseLinearFunctionEntry, - asymptote: &PiecewiseLinearFunctionEntry, - ) -> ValueType { - // Short circuit if the x is on prev or next. - // `next` point is preferred as per spec. - if x.approx_eq(&next.x) { - return next.y; - } - if x.approx_eq(&prev.x) { - return prev.y; - } - // Avoid division by zero. - if prev.x.approx_eq(&next.x) { - return next.y; - } - let slope = (next.y - prev.y) / (next.x - prev.x); - return slope * (x - asymptote.x) + asymptote.y; - } - - /// Get the y value of the piecewise linear function given the x value, as per - /// https://drafts.csswg.org/css-easing-2/#linear-easing-function-output - pub fn at(&self, x: ValueType) -> ValueType { - if !x.is_finite() { - return if x > 0.0 { 1.0 } else { 0.0 }; - } - if self.entries.is_empty() { - // Implied y = x, as per spec. - return x; - } - if self.entries.len() == 1 { - // Implied y = , as per spec. - return self.entries[0].y; - } - // Spec dictates the valid input domain is [0, 1]. Outside of this range, the output - // should be calculated as if the slopes at start and end extend to infinity. However, if the - // start/end have two points of the same position, the line should extend along the x-axis. - // The function doesn't have to cover the input domain, in which case the extension logic - // applies even if the input falls in the input domain. - // Also, we're guaranteed to have at least two elements at this point. - if x < self.entries[0].x { - return Self::interpolate(x, self.entries[0], self.entries[1], &self.entries[0]); - } - let mut rev_iter = self.entries.iter().rev(); - let last = rev_iter.next().unwrap(); - if x >= last.x { - let second_last = rev_iter.next().unwrap(); - return Self::interpolate(x, *second_last, *last, last); - } - - // Now we know the input sits within the domain explicitly defined by our function. - for (point_b, point_a) in self.entries.iter().rev().tuple_windows() { - // Need to let point A be the _last_ point where its x is less than the input x, - // hence the reverse traversal. - if x < point_a.x { - continue; - } - return Self::interpolate(x, *point_a, *point_b, point_a); - } - unreachable!("Input is supposed to be within the entries' min & max!"); - } - - /// Create the piecewise linear function from an iterator that generates the parameter tuple. - pub fn from_iter(iter: Iter) -> Self - where - Iter: Iterator + ExactSizeIterator, - { - let mut builder = PiecewiseLinearFunctionBuilder::with_capacity(iter.len()); - for (y, x_start) in iter { - builder = builder.push(y, x_start); - } - builder.build() - } - - #[allow(missing_docs)] - pub fn iter(&self) -> Iter { - self.entries.iter() - } -} - -/// Entry of a piecewise linear function while building, where the calculation of x value can be deferred. -#[derive(Clone, Copy)] -struct BuildEntry { - x: Option, - y: ValueType, -} - -/// Builder object to generate a linear function. -#[derive(Default)] -pub struct PiecewiseLinearFunctionBuilder { - largest_x: Option, - smallest_x: Option, - entries: Vec, -} - -impl PiecewiseLinearFunctionBuilder { - #[allow(missing_docs)] - pub fn new() -> Self { - PiecewiseLinearFunctionBuilder::default() - } - - /// Create a builder for a known amount of linear stop entries. - pub fn with_capacity(len: usize) -> Self { - PiecewiseLinearFunctionBuilder { - largest_x: None, - smallest_x: None, - entries: Vec::with_capacity(len), - } - } - - fn create_entry(&mut self, y: ValueType, x: Option) { - let x = match x { - Some(x) if x.is_finite() => x, - _ if self.entries.is_empty() => 0.0, // First x is 0 if not specified (Or not finite) - _ => { - self.entries.push(BuildEntry { x: None, y }); - return; - }, - }; - // Specified x value cannot regress, as per spec. - let x = match self.largest_x { - Some(largest_x) => x.max(largest_x), - None => x, - }; - self.largest_x = Some(x); - // Whatever we see the earliest is the smallest value. - if self.smallest_x.is_none() { - self.smallest_x = Some(x); - } - self.entries.push(BuildEntry { x: Some(x), y }); - } - - /// Add a new entry into the piecewise linear function with specified y value. - /// If the start x value is given, that is where the x value will be. Otherwise, - /// the x value is calculated later. If the end x value is specified, a flat segment - /// is generated. If start x value is not specified but end x is, it is treated as - /// start x. - pub fn push(mut self, y: CSSFloat, x_start: Option) -> Self { - self.create_entry(y, x_start); - self - } - - /// Finish building the piecewise linear function by resolving all undefined x values, - /// then return the result. - pub fn build(mut self) -> PiecewiseLinearFunction { - if self.entries.is_empty() { - return PiecewiseLinearFunction::default(); - } - if self.entries.len() == 1 { - // Don't bother resolving anything. - return PiecewiseLinearFunction { - entries: crate::OwnedSlice::from_slice(&[PiecewiseLinearFunctionEntry { - x: 0., - y: self.entries[0].y, - }]), - }; - } - // Guaranteed at least two elements. - // Start element's x value should've been assigned when the first value was pushed. - debug_assert!( - self.entries[0].x.is_some(), - "Expected an entry with x defined!" - ); - // Spec asserts that if the last entry does not have an x value, it is assigned the largest seen x value. - self.entries - .last_mut() - .unwrap() - .x - .get_or_insert(self.largest_x.filter(|x| x > &1.0).unwrap_or(1.0)); - // Now we have at least two elements with x values, with start & end x values guaranteed. - - let mut result = Vec::with_capacity(self.entries.len()); - result.push(PiecewiseLinearFunctionEntry { - x: self.entries[0].x.unwrap(), - y: self.entries[0].y, - }); - for (i, e) in self.entries.iter().enumerate().skip(1) { - if e.x.is_none() { - // Need to calculate x values by first finding an entry with the first - // defined x value (Guaranteed to exist as the list end has it defined). - continue; - } - // x is defined for this element. - let divisor = i - result.len() + 1; - // Any element(s) with undefined x to assign? - if divisor != 1 { - // Have at least one element in result at all times. - let start_x = result.last().unwrap().x; - let increment = (e.x.unwrap() - start_x) / divisor as ValueType; - // Grab every element with undefined x to this point, which starts at the end of the result - // array, and ending right before the current index. Then, assigned the evenly divided - // x values. - result.extend( - self.entries[result.len()..i] - .iter() - .enumerate() - .map(|(j, e)| { - debug_assert!(e.x.is_none(), "Expected an entry with x undefined!"); - PiecewiseLinearFunctionEntry { - x: increment * (j + 1) as ValueType + start_x, - y: e.y, - } - }), - ); - } - result.push(PiecewiseLinearFunctionEntry { - x: e.x.unwrap(), - y: e.y, - }); - } - debug_assert_eq!( - result.len(), - self.entries.len(), - "Should've mapped one-to-one!" - ); - PiecewiseLinearFunction { - entries: result.into(), - } - } -} diff --git a/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl b/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl deleted file mode 100644 index 9593025a473..00000000000 Binary files a/components/style/properties/Mako-1.1.2-py2.py3-none-any.whl and /dev/null differ diff --git a/components/style/properties/build.py b/components/style/properties/build.py deleted file mode 100644 index c03d9023a7e..00000000000 --- a/components/style/properties/build.py +++ /dev/null @@ -1,172 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at https://mozilla.org/MPL/2.0/. - -import json -import os.path -import re -import sys - -BASE = os.path.dirname(__file__.replace("\\", "/")) -sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl")) -sys.path.insert(0, BASE) # For importing `data.py` - -from mako import exceptions -from mako.lookup import TemplateLookup -from mako.template import Template - -import data - -RE_PYTHON_ADDR = re.compile(r"<.+? object at 0x[0-9a-fA-F]+>") - -OUT_DIR = os.environ.get("OUT_DIR", "") - -STYLE_STRUCT_LIST = [ - "background", - "border", - "box", - "column", - "counters", - "effects", - "font", - "inherited_box", - "inherited_svg", - "inherited_table", - "inherited_text", - "inherited_ui", - "list", - "margin", - "outline", - "page", - "padding", - "position", - "svg", - "table", - "text", - "ui", - "xul", -] - - -def main(): - usage = ( - "Usage: %s [ servo | gecko ] [ style-crate | geckolib