[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [\"main\"]\n  pull_request:\n  workflow_dispatch:\n  merge_group:\n    types: [checks_requested]\n\n\njobs:\n  linux-debug:\n    name: Linux (Debug)\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install Rust\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run Tests\n        run: cargo build --features servo\n        env:\n          RUST_BACKTRACE: 1\n\n  linux-release:\n    name: Linux (Release)\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install Rust\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run Tests\n        run: cargo build --release --features servo\n        env:\n          RUST_BACKTRACE: 1\n\n  macos-debug:\n    name: macOS (Debug)\n    runs-on: macos-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install Rust\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run Tests\n        run: cargo build --features servo\n        env:\n          RUST_BACKTRACE: 1\n\n  windows-debug:\n    name: Windows (Debug)\n    runs-on: windows-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Install Rust\n        uses: dtolnay/rust-toolchain@stable\n      - name: Run Tests\n        run: cargo build --features servo\n        env:\n          RUST_BACKTRACE: 1\n\n  build-result:\n    name: Result\n    runs-on: ubuntu-latest\n    if: ${{ always() }}\n    needs:\n      - linux-debug\n      - linux-release\n      - macos-debug\n      - windows-debug\n    steps:\n      - name: Success\n        if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}\n        run: exit 0\n      - name: Failure\n        if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}\n        run: exit 1\n\n"
  },
  {
    "path": ".github/workflows/mirror-to-release-branch.yml",
    "content": "name: 🪞 Mirror `main`\non:\n  push:\n    branches:\n      - main\n\njobs:\n  mirror:\n    name: Mirror\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n    - name: Get branch name\n      id: branch-name\n      run: |\n        first_commit=$(git log --pretty=\\%H --grep='Servo initial downstream commit')\n        upstream_base=\"$first_commit~\"\n        echo BRANCH_NAME=$(git log -n1 --pretty='%as' $upstream_base) >> $GITHUB_OUTPUT\n    - uses: google/mirror-branch-action@v1.0\n      name: Mirror to ${{ steps.branch-name.outputs.BRANCH_NAME }}\n      with:\n        github-token: ${{ secrets.GITHUB_TOKEN }}\n        source: main\n        dest: ${{ steps.branch-name.outputs.BRANCH_NAME }}\n"
  },
  {
    "path": ".github/workflows/sync-upstream.yml",
    "content": "name: Sync upstream with mozilla-central\n\non:\n  schedule:\n    - cron: '0 13 * * *'\n  workflow_dispatch:\n\njobs:\n  sync:\n    name: Sync\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 1\n      - uses: actions/cache@v3\n        with:\n          path: _cache/upstream\n          key: upstream\n      - run: |\n          ./sync.sh _filtered\n          git fetch -f --progress ./_filtered main:upstream\n          git push -fu --progress origin upstream\n"
  },
  {
    "path": ".gitignore",
    "content": "/_cache/\n/_filtered/\n/target/\n/style/properties/__pycache__/\nCargo.lock\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nresolver = \"2\"\nmembers = [\n    \"stylo_atoms\",\n    \"stylo_dom\",\n    \"malloc_size_of\",\n    \"rustfmt.toml\",\n    \"selectors\",\n    \"servo_arc\",\n    \"style\",\n    \"style_derive\",\n    \"stylo_static_prefs\",\n    \"style_traits\",\n    \"to_shmem\",\n    \"to_shmem_derive\",\n]\ndefault-members = [\"style\"]\n\n[workspace.package]\nversion = \"0.17.0\"\n\n[workspace.dependencies]\n# in-repo dependencies (separately versioned)\nservo_arc       = { version = \"0.4.3\",  path = \"./servo_arc\" }\nselectors       = { version = \"0.38.0\", path = \"./selectors\" }\nto_shmem        = { version = \"0.4.0\",  path = \"./to_shmem\", features = [\"servo\"] }\nto_shmem_derive = { version = \"0.1.0\",  path = \"./to_shmem_derive\" }\n\n# in-repo dependencies (main version)\nmalloc_size_of  = { version = \"0.17.0\", path = \"./malloc_size_of\", package = \"stylo_malloc_size_of\", features = [\"servo\"] }\nstatic_prefs    = { version = \"0.17.0\", path = \"./stylo_static_prefs\", package = \"stylo_static_prefs\" }\nstylo_atoms     = { version = \"0.17.0\", path = \"./stylo_atoms\" }\ndom             = { version = \"0.17.0\", path = \"./stylo_dom\", package = \"stylo_dom\" }\nstyle_traits    = { version = \"0.17.0\", path = \"./style_traits\", features = [\"servo\"], package = \"stylo_traits\"}\nstyle_derive    = { version = \"0.17.0\", path = \"./style_derive\", package = \"stylo_derive\"}\nstylo           = { version = \"0.17.0\", path = \"./style\" }\n"
  },
  {
    "path": "README.md",
    "content": "Stylo\n=====\n\n**High-Performance CSS Style Engine**\n\n[![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions)\n[![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo)\n[![Docs](https://docs.rs/stylo/badge.svg)](https://docs.rs/stylo)\n![Crates.io License](https://img.shields.io/crates/l/stylo)\n\nStylo is a high-performance, browser-grade CSS style engine written in Rust that powers [Servo](https://servo.org) and [Firefox](https://firefox.com). This repo contains Servo’s downstream version of Stylo. The upstream version lives in mozilla-central with the rest of the Gecko/Firefox codebase.\n\nCoordination of Stylo development happens:\n\n- Here in Github Issues\n- In the [#stylo](https://servo.zulipchat.com/#narrow/channel/417109-stylo) channel of the [Servo Zulip](https://servo.zulipchat.com/)\n- In the [#layout](https://chat.mozilla.org/#/room/#layout:mozilla.org) room of the Mozilla Matrix instance (matrix.mozilla.org)\n\n## High-Level Documentation\n\n- This [Mozilla Hacks article](https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo) contains a high-level overview of the Stylo architecture.\n- There is a [chapter](https://book.servo.org/architecture/style.html) in the Servo Book (although it is a little out of date).\n\n## Branches\n\nThe branches are as follows:\n\n- [**upstream**](https://github.com/servo/style/tree/upstream) has upstream [mozilla-central](https://searchfox.org/mozilla-central/source/servo) filtered to the paths we care about ([style.paths](style.paths)), but is otherwise unmodified.\n- [**main**](https://github.com/servo/style/tree/ci) adds our downstream patches, plus the scripts and workflows for syncing with mozilla-central on top of **upstream**.\n\n> [!WARNING]\n> This repo syncs from upstream by creating a new branch and then rebasing our changes on top of it. This means that `git pull` will not work across syncs (you will need to use `git fetch`, `git reset` and `git rebase`).\n\nMore information on the syncing process is available in [SYNCING.md](SYNCING.md)\n\n## Crates\n\nA guide to the crates contained within this repo\n\n### Stylo Crates\n\nThese crates are largely implementation details of Stylo, although you may need to use some of them directly if you use Stylo.\n\n| Directory          | Crate                                                                                                               | Notes                                                                                                             |\n| ---                | ---                                                                                                                 | ---                                                                                                               |\n| style              | [![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo)                           | The main Stylo crate containing the entire CSS engine                                                             |\n| style_traits       | [![Crates.io](https://img.shields.io/crates/v/stylo_traits.svg)](https://crates.io/crates/stylo_traits)             | Types and traits which allow other code to interoperate with Stylo without depending on the main crate directly.  |\n| stylo_dom          | [![Crates.io](https://img.shields.io/crates/v/stylo_dom.svg)](https://crates.io/crates/stylo_dom)                   | Similar to stylo_traits (but much smaller)                                                                        |\n| stylo_atoms        | [![Crates.io](https://img.shields.io/crates/v/stylo_atoms.svg)](https://crates.io/crates/stylo_atoms)               | [Atoms](https://docs.rs/string_cache/latest/string_cache/struct.Atom.html) for CSS and HTML event related strings |\n| stylo_static_prefs | [![Crates.io](https://img.shields.io/crates/v/stylo_static_prefs.svg)](https://crates.io/crates/stylo_static_prefs) | Configuration for Stylo. Can be used to set runtime preferences (enabling/disabling properties, etc)              |\n| style_derive       | [![Crates.io](https://img.shields.io/crates/v/stylo_derive.svg)](https://crates.io/crates/stylo_derive)             | Internal derive macro for stylo crate                                                                             |\n\n### Standalone Crates\n\nThese crates form part of Stylo but are also be useful standalone.\n\n| Directory | Crate                                                                                             | Notes                   |\n| ---       | ---                                                                                               | ---                     |\n| selectors | [![Crates.io](https://img.shields.io/crates/v/selectors.svg)](https://crates.io/crates/selectors) | CSS Selector matching   |\n| servo_arc | [![Crates.io](https://img.shields.io/crates/v/servo_arc.svg)](https://crates.io/crates/servo_arc) | A variant on `std::Arc` |\n\nYou may also be interested in the `cssparser` crate which lives in the [servo/rust-cssparser](https://github.com/servo/rust-cssparser) repo.\n\n### Support Crates\n\nLow-level crates which could technically be used standalone but are unlikely to be generally useful in practice.\n\n| Directory       | Crate                                                                                                                   | Notes                                                       |\n| ---             | ---                                                                                                                     | ---                                                         |\n| malloc_size_of  | [![Crates.io](https://img.shields.io/crates/v/stylo_malloc_size_of.svg)](https://crates.io/crates/stylo_malloc_size_of) | Heap size measurement for Stylo values                      |\n| to_shmem        | [![Crates.io](https://img.shields.io/crates/v/to_shmem.svg)](https://crates.io/crates/to_shmem)                         | Internal utility crate for sharing memory across processes. |\n| to_shmem_derive | [![Crates.io](https://img.shields.io/crates/v/to_shmem_derive.svg)](https://crates.io/crates/to_shmem_derive)           | Internal derive macro for to_shmem crate                    |\n\n## Building Servo Against a Local Copy of Stylo\n\nAssuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`:\n\n```toml\n[patch.\"https://github.com/servo/stylo\"]\nselectors = { path = \"../stylo/selectors\" }\nservo_arc = { path = \"../stylo/servo_arc\" }\nstylo = { path = \"../stylo/style\" }\nstylo_atoms = { path = \"../stylo/stylo_atoms\" }\nstylo_dom = { path = \"../stylo/stylo_dom\" }\nstylo_malloc_size_of = { path = \"../stylo/malloc_size_of\" }\nstylo_static_prefs = { path = \"../stylo/stylo_static_prefs\" }\nstylo_traits = { path = \"../stylo/style_traits\" }\n```\n\n## Releases\n\nReleases are made every time this repository rebases its changes on top of the latest version of upstream Stylo. There are a lot of crates here. In order to publish them, they must be done in order. One order that works is:\n\n- selectors\n- stylo_static_prefs\n- stylo_atoms\n- stylo_malloc_size_of\n- stylo_dom\n- stylo_derive\n- stylo_traits\n- stylo\n\n## License\n\nStylo is licensed under MPL 2.0\n"
  },
  {
    "path": "SYNCING.md",
    "content": "# Syncing\n\nThis file documents the process of syncing this repository with the upstream copy of Stylo in mozilla-central.\n\n## Syncing `upstream` with mozilla-central\n\nStart by generating a filtered copy of mozilla-central. This will cache the raw mozilla-central in `_cache/upstream`, storing the result in `_filtered`:\n\n```sh\n$ ./sync.sh _filtered\n```\n\nIf `_filtered` already exists, you will need to delete it and try again:\n\n```sh\n$ rm -Rf _filtered\n```\n\nNow overwrite our `upstream` with those commits and push:\n\n```sh\n$ git fetch -f --progress ./_filtered main:upstream\n$ git push -fu --progress origin upstream\n```\n\n## Rebasing `main` onto `upstream`\n\nStart by fetching `upstream` into your local repo:\n\n```sh\n$ git fetch -f origin upstream:upstream\n```\n\nIn general, the filtering process is deterministic, yielding the same commit hashes each time, so we can rebase normally:\n\n```sh\n$ git rebase upstream\n```\n\nBut if the filtering config changes or Mozilla moves to GitHub, the commit hashes on `upstream` may change. In this case, we need to tell git where the old upstream ends and our own commits start (notice the `~`):\n\n```sh\n$ git log --pretty=\\%H --grep='Servo initial downstream commit'\ne62d7f0090941496e392e1dc91df103a38e3f488\n\n$ git rebase --onto upstream e62d7f0090941496e392e1dc91df103a38e3f488~\nSuccessfully rebased and updated refs/heads/main.\n```\n\n`start-rebase.sh` takes care of this automatically, but you should still use `git rebase` for subsequent steps like `--continue` and `--abort`:\n\n```sh\n$ ./start-rebase.sh upstream\n$ ./start-rebase.sh upstream -i     # interactive\n$ git rebase --continue             # not ./start-rebase.sh --continue\n$ git rebase --abort                # not ./start-rebase.sh --abort\n```\n\nOr if we aren’t ready to rebase onto the tip of upstream:\n\n```sh\n$ ./start-rebase.sh upstream~10 -i\n```\n"
  },
  {
    "path": "commit-from-merge.sh",
    "content": "#!/bin/sh\n# Usage: commit-from-merge.sh <path/to/servo/servo> <merge commit> [extra git-commit(1) arguments ...]\n# Given a merge commit made by bors, runs git-commit(1) with your local changes\n# while borrowing the author name/email from the right-hand parent of the merge,\n# and the author date from the committer date of the merge.\nset -eu\n\nlookup_repo=$1; shift\nmerge_commit=$1; shift\nauthor_name_email=$(git -C \"$lookup_repo\" log -n1 --pretty='%aN <%aE>' \"$merge_commit\"\\^2)\ncommitter_date=$(git -C \"$lookup_repo\" log -n1 --pretty='%cd' \"$merge_commit\")\n\nset -- git commit --author=\"$author_name_email\" --date=\"$committer_date\" \"$@\"\necho \"$@\"\n\"$@\"\n"
  },
  {
    "path": "commit-from-squashed.sh",
    "content": "#!/bin/sh\n# Usage: commit-from-squashed.sh <squashed commit> [extra git-commit(1) arguments ...]\n# Given a squashed commit made by the GitHub merge queue, runs git-commit(1) with your local changes\n# while borrowing our author name/email from that commit, our author date from its committer date,\n# and our commit message from that commit.\nset -eu\n\nsquashed_commit=$1; shift\ncommitter_date=$(git log -n1 --pretty='%cd' \"$squashed_commit\")\n\n# -c is equivalent to --author=$(...'%aN <%aE>') -m $(...'%B'), but allows editing\nset -- git commit -c \"$squashed_commit\" --date=\"$committer_date\" \"$@\"\necho \"$@\"\n\"$@\"\n"
  },
  {
    "path": "malloc_size_of/Cargo.toml",
    "content": "[package]\nname = \"stylo_malloc_size_of\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/servo/stylo\"\ndescription = \"An allocator-agnostic crate for measuring the heap size of a value\"\nedition = \"2021\"\n\n[lib]\npath = \"lib.rs\"\n\n[features]\ngecko = [\"thin-vec/gecko-ffi\"]\nservo = [\"string_cache\"]\n\n[dependencies]\napp_units = \"0.7\"\ncssparser = \"0.37\"\neuclid = \"0.22\"\nselectors = { workspace = true }\nservo_arc = { workspace = true }\nsmallbitvec = \"2.3.0\"\nsmallvec = \"1.13\"\nstring_cache = { version = \"0.9\", optional = true }\nthin-vec = { version = \"0.2.13\" }\nvoid = \"1.0.2\"\n"
  },
  {
    "path": "malloc_size_of/LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "malloc_size_of/LICENSE-MIT",
    "content": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "malloc_size_of/lib.rs",
    "content": "// Copyright 2016-2017 The Servo Project Developers. See the COPYRIGHT\n// file at the top-level directory of this distribution and at\n// http://rust-lang.org/COPYRIGHT.\n//\n// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\n// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\n// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\n// option. This file may not be copied, modified, or distributed\n// except according to those terms.\n\n//! A crate for measuring the heap usage of data structures in a way that\n//! integrates with Firefox's memory reporting, particularly the use of\n//! mozjemalloc and DMD. In particular, it has the following features.\n//! - It isn't bound to a particular heap allocator.\n//! - It provides traits for both \"shallow\" and \"deep\" measurement, which gives\n//!   flexibility in the cases where the traits can't be used.\n//! - It allows for measuring blocks even when only an interior pointer can be\n//!   obtained for heap allocations, e.g. `HashSet` and `HashMap`. (This relies\n//!   on the heap allocator having suitable support, which mozjemalloc has.)\n//! - It allows handling of types like `Rc` and `Arc` by providing traits that\n//!   are different to the ones for non-graph structures.\n//!\n//! Suggested uses are as follows.\n//! - When possible, use the `MallocSizeOf` trait. (Deriving support is\n//!   provided by the `malloc_size_of_derive` crate.)\n//! - If you need an additional synchronization argument, provide a function\n//!   that is like the standard trait method, but with the extra argument.\n//! - If you need multiple measurements for a type, provide a function named\n//!   `add_size_of` that takes a mutable reference to a struct that contains\n//!   the multiple measurement fields.\n//! - When deep measurement (via `MallocSizeOf`) cannot be implemented for a\n//!   type, shallow measurement (via `MallocShallowSizeOf`) in combination with\n//!   iteration can be a useful substitute.\n//! - `Rc` and `Arc` are always tricky, which is why `MallocSizeOf` is not (and\n//!   should not be) implemented for them.\n//! - If an `Rc` or `Arc` is known to be a \"primary\" reference and can always\n//!   be measured, it should be measured via the `MallocUnconditionalSizeOf`\n//!   trait.\n//! - If an `Rc` or `Arc` should be measured only if it hasn't been seen\n//!   before, it should be measured via the `MallocConditionalSizeOf` trait.\n//! - Using universal function call syntax is a good idea when measuring boxed\n//!   fields in structs, because it makes it clear that the Box is being\n//!   measured as well as the thing it points to. E.g.\n//!   `<Box<_> as MallocSizeOf>::size_of(field, ops)`.\n//!\n//!   Note: WebRender has a reduced fork of this crate, so that we can avoid\n//!   publishing this crate on crates.io.\n\nuse std::hash::{BuildHasher, Hash};\nuse std::mem::size_of;\nuse std::ops::Range;\nuse std::ops::{Deref, DerefMut};\nuse std::os::raw::c_void;\nuse void::Void;\n\n/// A C function that takes a pointer to a heap allocation and returns its size.\ntype VoidPtrToSizeFn = unsafe extern \"C\" fn(ptr: *const c_void) -> usize;\n\n/// A closure implementing a stateful predicate on pointers.\ntype VoidPtrToBoolFnMut = dyn FnMut(*const c_void) -> bool;\n\n/// Operations used when measuring heap usage of data structures.\npub struct MallocSizeOfOps {\n    /// A function that returns the size of a heap allocation.\n    size_of_op: VoidPtrToSizeFn,\n\n    /// Like `size_of_op`, but can take an interior pointer. Optional because\n    /// not all allocators support this operation. If it's not provided, some\n    /// memory measurements will actually be computed estimates rather than\n    /// real and accurate measurements.\n    enclosing_size_of_op: Option<VoidPtrToSizeFn>,\n\n    /// Check if a pointer has been seen before, and remember it for next time.\n    /// Useful when measuring `Rc`s and `Arc`s. Optional, because many places\n    /// don't need it.\n    have_seen_ptr_op: Option<Box<VoidPtrToBoolFnMut>>,\n}\n\nimpl MallocSizeOfOps {\n    pub fn new(\n        size_of: VoidPtrToSizeFn,\n        malloc_enclosing_size_of: Option<VoidPtrToSizeFn>,\n        have_seen_ptr: Option<Box<VoidPtrToBoolFnMut>>,\n    ) -> Self {\n        MallocSizeOfOps {\n            size_of_op: size_of,\n            enclosing_size_of_op: malloc_enclosing_size_of,\n            have_seen_ptr_op: have_seen_ptr,\n        }\n    }\n\n    /// Check if an allocation is empty. This relies on knowledge of how Rust\n    /// handles empty allocations, which may change in the future.\n    fn is_empty<T: ?Sized>(ptr: *const T) -> bool {\n        // The correct condition is this:\n        //   `ptr as usize <= ::std::mem::align_of::<T>()`\n        // But we can't call align_of() on a ?Sized T. So we approximate it\n        // with the following. 256 is large enough that it should always be\n        // larger than the required alignment, but small enough that it is\n        // always in the first page of memory and therefore not a legitimate\n        // address.\n        return ptr as *const usize as usize <= 256;\n    }\n\n    /// Call `size_of_op` on `ptr`, first checking that the allocation isn't\n    /// empty, because some types (such as `Vec`) utilize empty allocations.\n    pub unsafe fn malloc_size_of<T: ?Sized>(&self, ptr: *const T) -> usize {\n        if MallocSizeOfOps::is_empty(ptr) {\n            0\n        } else {\n            (self.size_of_op)(ptr as *const c_void)\n        }\n    }\n\n    /// Is an `enclosing_size_of_op` available?\n    pub fn has_malloc_enclosing_size_of(&self) -> bool {\n        self.enclosing_size_of_op.is_some()\n    }\n\n    /// Call `enclosing_size_of_op`, which must be available, on `ptr`, which\n    /// must not be empty.\n    pub unsafe fn malloc_enclosing_size_of<T>(&self, ptr: *const T) -> usize {\n        assert!(!MallocSizeOfOps::is_empty(ptr));\n        (self.enclosing_size_of_op.unwrap())(ptr as *const c_void)\n    }\n\n    /// Call `have_seen_ptr_op` on `ptr`.\n    pub fn have_seen_ptr<T>(&mut self, ptr: *const T) -> bool {\n        let have_seen_ptr_op = self\n            .have_seen_ptr_op\n            .as_mut()\n            .expect(\"missing have_seen_ptr_op\");\n        have_seen_ptr_op(ptr as *const c_void)\n    }\n}\n\n/// Trait for measuring the \"deep\" heap usage of a data structure. This is the\n/// most commonly-used of the traits.\npub trait MallocSizeOf {\n    /// Measure the heap usage of all descendant heap-allocated structures, but\n    /// not the space taken up by the value itself.\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\n/// Trait for measuring the \"shallow\" heap usage of a container.\npub trait MallocShallowSizeOf {\n    /// Measure the heap usage of immediate heap-allocated descendant\n    /// structures, but not the space taken up by the value itself. Anything\n    /// beyond the immediate descendants must be measured separately, using\n    /// iteration.\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\n/// Like `MallocSizeOf`, but with a different name so it cannot be used\n/// accidentally with derive(MallocSizeOf). For use with types like `Rc` and\n/// `Arc` when appropriate (e.g. when measuring a \"primary\" reference).\npub trait MallocUnconditionalSizeOf {\n    /// Measure the heap usage of all heap-allocated descendant structures, but\n    /// not the space taken up by the value itself.\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\n/// `MallocUnconditionalSizeOf` combined with `MallocShallowSizeOf`.\npub trait MallocUnconditionalShallowSizeOf {\n    /// `unconditional_size_of` combined with `shallow_size_of`.\n    fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\n/// Like `MallocSizeOf`, but only measures if the value hasn't already been\n/// measured. For use with types like `Rc` and `Arc` when appropriate (e.g.\n/// when there is no \"primary\" reference).\npub trait MallocConditionalSizeOf {\n    /// Measure the heap usage of all heap-allocated descendant structures, but\n    /// not the space taken up by the value itself, and only if that heap usage\n    /// hasn't already been measured.\n    fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\n/// `MallocConditionalSizeOf` combined with `MallocShallowSizeOf`.\npub trait MallocConditionalShallowSizeOf {\n    /// `conditional_size_of` combined with `shallow_size_of`.\n    fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize;\n}\n\nimpl MallocSizeOf for String {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        unsafe { ops.malloc_size_of(self.as_ptr()) }\n    }\n}\n\nimpl<'a, T: ?Sized> MallocSizeOf for &'a T {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        // Zero makes sense for a non-owning reference.\n        0\n    }\n}\n\nimpl<T: ?Sized> MallocShallowSizeOf for Box<T> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        unsafe { ops.malloc_size_of(&**self) }\n    }\n}\n\nimpl<T: MallocSizeOf + ?Sized> MallocSizeOf for Box<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.shallow_size_of(ops) + (**self).size_of(ops)\n    }\n}\n\nimpl MallocSizeOf for () {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        0\n    }\n}\n\nimpl<T1, T2> MallocSizeOf for (T1, T2)\nwhere\n    T1: MallocSizeOf,\n    T2: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.0.size_of(ops) + self.1.size_of(ops)\n    }\n}\n\nimpl<T1, T2, T3> MallocSizeOf for (T1, T2, T3)\nwhere\n    T1: MallocSizeOf,\n    T2: MallocSizeOf,\n    T3: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops)\n    }\n}\n\nimpl<T1, T2, T3, T4> MallocSizeOf for (T1, T2, T3, T4)\nwhere\n    T1: MallocSizeOf,\n    T2: MallocSizeOf,\n    T3: MallocSizeOf,\n    T4: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.0.size_of(ops) + self.1.size_of(ops) + self.2.size_of(ops) + self.3.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for Option<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if let Some(val) = self.as_ref() {\n            val.size_of(ops)\n        } else {\n            0\n        }\n    }\n}\n\nimpl<T: MallocSizeOf, E: MallocSizeOf> MallocSizeOf for Result<T, E> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        match *self {\n            Ok(ref x) => x.size_of(ops),\n            Err(ref e) => e.size_of(ops),\n        }\n    }\n}\n\nimpl<T: MallocSizeOf + Copy> MallocSizeOf for std::cell::Cell<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.get().size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for std::cell::RefCell<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.borrow().size_of(ops)\n    }\n}\n\nimpl<'a, B: ?Sized + ToOwned> MallocSizeOf for std::borrow::Cow<'a, B>\nwhere\n    B::Owned: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        match *self {\n            std::borrow::Cow::Borrowed(_) => 0,\n            std::borrow::Cow::Owned(ref b) => b.size_of(ops),\n        }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for [T] {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n        for elem in self.iter() {\n            n += elem.size_of(ops);\n        }\n        n\n    }\n}\n\nimpl<T> MallocShallowSizeOf for Vec<T> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        unsafe { ops.malloc_size_of(self.as_ptr()) }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for Vec<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.shallow_size_of(ops);\n        for elem in self.iter() {\n            n += elem.size_of(ops);\n        }\n        n\n    }\n}\n\nimpl<T> MallocShallowSizeOf for std::collections::VecDeque<T> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if ops.has_malloc_enclosing_size_of() {\n            if let Some(front) = self.front() {\n                // The front element is an interior pointer.\n                unsafe { ops.malloc_enclosing_size_of(&*front) }\n            } else {\n                // This assumes that no memory is allocated when the VecDeque is empty.\n                0\n            }\n        } else {\n            // An estimate.\n            self.capacity() * size_of::<T>()\n        }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for std::collections::VecDeque<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.shallow_size_of(ops);\n        for elem in self.iter() {\n            n += elem.size_of(ops);\n        }\n        n\n    }\n}\n\nimpl<A: smallvec::Array> MallocShallowSizeOf for smallvec::SmallVec<A> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if self.spilled() {\n            unsafe { ops.malloc_size_of(self.as_ptr()) }\n        } else {\n            0\n        }\n    }\n}\n\nimpl<A> MallocSizeOf for smallvec::SmallVec<A>\nwhere\n    A: smallvec::Array,\n    A::Item: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.shallow_size_of(ops);\n        for elem in self.iter() {\n            n += elem.size_of(ops);\n        }\n        n\n    }\n}\n\nimpl<T> MallocShallowSizeOf for thin_vec::ThinVec<T> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if self.capacity() == 0 {\n            // If it's the singleton we might not be a heap pointer.\n            return 0;\n        }\n\n        assert_eq!(\n            std::mem::size_of::<Self>(),\n            std::mem::size_of::<*const ()>()\n        );\n        unsafe { ops.malloc_size_of(*(self as *const Self as *const *const ())) }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocSizeOf for thin_vec::ThinVec<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.shallow_size_of(ops);\n        for elem in self.iter() {\n            n += elem.size_of(ops);\n        }\n        n\n    }\n}\n\nmacro_rules! malloc_size_of_hash_set {\n    ($ty:ty) => {\n        impl<T, S> MallocShallowSizeOf for $ty\n        where\n            T: Eq + Hash,\n            S: BuildHasher,\n        {\n            fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n                if ops.has_malloc_enclosing_size_of() {\n                    // The first value from the iterator gives us an interior pointer.\n                    // `ops.malloc_enclosing_size_of()` then gives us the storage size.\n                    // This assumes that the `HashSet`'s contents (values and hashes)\n                    // are all stored in a single contiguous heap allocation.\n                    self.iter()\n                        .next()\n                        .map_or(0, |t| unsafe { ops.malloc_enclosing_size_of(t) })\n                } else {\n                    // An estimate.\n                    self.capacity() * (size_of::<T>() + size_of::<usize>())\n                }\n            }\n        }\n\n        impl<T, S> MallocSizeOf for $ty\n        where\n            T: Eq + Hash + MallocSizeOf,\n            S: BuildHasher,\n        {\n            fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n                let mut n = self.shallow_size_of(ops);\n                for t in self.iter() {\n                    n += t.size_of(ops);\n                }\n                n\n            }\n        }\n    };\n}\n\nmalloc_size_of_hash_set!(std::collections::HashSet<T, S>);\n\nmacro_rules! malloc_size_of_hash_map {\n    ($ty:ty) => {\n        impl<K, V, S> MallocShallowSizeOf for $ty\n        where\n            K: Eq + Hash,\n            S: BuildHasher,\n        {\n            fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n                // See the implementation for std::collections::HashSet for details.\n                if ops.has_malloc_enclosing_size_of() {\n                    self.values()\n                        .next()\n                        .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) })\n                } else {\n                    self.capacity() * (size_of::<V>() + size_of::<K>() + size_of::<usize>())\n                }\n            }\n        }\n\n        impl<K, V, S> MallocSizeOf for $ty\n        where\n            K: Eq + Hash + MallocSizeOf,\n            V: MallocSizeOf,\n            S: BuildHasher,\n        {\n            fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n                let mut n = self.shallow_size_of(ops);\n                for (k, v) in self.iter() {\n                    n += k.size_of(ops);\n                    n += v.size_of(ops);\n                }\n                n\n            }\n        }\n    };\n}\n\nmalloc_size_of_hash_map!(std::collections::HashMap<K, V, S>);\n\nimpl<K, V> MallocShallowSizeOf for std::collections::BTreeMap<K, V>\nwhere\n    K: Eq + Hash,\n{\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if ops.has_malloc_enclosing_size_of() {\n            self.values()\n                .next()\n                .map_or(0, |v| unsafe { ops.malloc_enclosing_size_of(v) })\n        } else {\n            self.len() * (size_of::<V>() + size_of::<K>() + size_of::<usize>())\n        }\n    }\n}\n\nimpl<K, V> MallocSizeOf for std::collections::BTreeMap<K, V>\nwhere\n    K: Eq + Hash + MallocSizeOf,\n    V: MallocSizeOf,\n{\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.shallow_size_of(ops);\n        for (k, v) in self.iter() {\n            n += k.size_of(ops);\n            n += v.size_of(ops);\n        }\n        n\n    }\n}\n\n// PhantomData is always 0.\nimpl<T> MallocSizeOf for std::marker::PhantomData<T> {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        0\n    }\n}\n\n// XXX: we don't want MallocSizeOf to be defined for Rc and Arc. If negative\n// trait bounds are ever allowed, this code should be uncommented.\n// (We do have a compile-fail test for this:\n// rc_arc_must_not_derive_malloc_size_of.rs)\n//impl<T> !MallocSizeOf for Arc<T> { }\n//impl<T> !MallocShallowSizeOf for Arc<T> { }\n\nimpl<T> MallocUnconditionalShallowSizeOf for servo_arc::Arc<T> {\n    fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        unsafe { ops.malloc_size_of(self.heap_ptr()) }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocUnconditionalSizeOf for servo_arc::Arc<T> {\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.unconditional_shallow_size_of(ops) + (**self).size_of(ops)\n    }\n}\n\nimpl<T> MallocConditionalShallowSizeOf for servo_arc::Arc<T> {\n    fn conditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if ops.have_seen_ptr(self.heap_ptr()) {\n            0\n        } else {\n            self.unconditional_shallow_size_of(ops)\n        }\n    }\n}\n\nimpl<T: MallocSizeOf> MallocConditionalSizeOf for servo_arc::Arc<T> {\n    fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if ops.have_seen_ptr(self.heap_ptr()) {\n            0\n        } else {\n            self.unconditional_size_of(ops)\n        }\n    }\n}\n\n/// If a mutex is stored directly as a member of a data type that is being measured,\n/// it is the unique owner of its contents and deserves to be measured.\n///\n/// If a mutex is stored inside of an Arc value as a member of a data type that is being measured,\n/// the Arc will not be automatically measured so there is no risk of overcounting the mutex's\n/// contents.\nimpl<T: MallocSizeOf> MallocSizeOf for std::sync::Mutex<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        (*self.lock().unwrap()).size_of(ops)\n    }\n}\n\nimpl MallocSizeOf for smallbitvec::SmallBitVec {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        if let Some(ptr) = self.heap_ptr() {\n            unsafe { ops.malloc_size_of(ptr) }\n        } else {\n            0\n        }\n    }\n}\n\nimpl<T: MallocSizeOf, Unit> MallocSizeOf for euclid::Length<T, Unit> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.0.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Scale<T, Src, Dst> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.0.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, U> MallocSizeOf for euclid::Point2D<T, U> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.x.size_of(ops) + self.y.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, U> MallocSizeOf for euclid::Rect<T, U> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.origin.size_of(ops) + self.size.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, U> MallocSizeOf for euclid::SideOffsets2D<T, U> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.top.size_of(ops)\n            + self.right.size_of(ops)\n            + self.bottom.size_of(ops)\n            + self.left.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, U> MallocSizeOf for euclid::Size2D<T, U> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.width.size_of(ops) + self.height.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Transform2D<T, Src, Dst> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.m11.size_of(ops)\n            + self.m12.size_of(ops)\n            + self.m21.size_of(ops)\n            + self.m22.size_of(ops)\n            + self.m31.size_of(ops)\n            + self.m32.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, Src, Dst> MallocSizeOf for euclid::Transform3D<T, Src, Dst> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.m11.size_of(ops)\n            + self.m12.size_of(ops)\n            + self.m13.size_of(ops)\n            + self.m14.size_of(ops)\n            + self.m21.size_of(ops)\n            + self.m22.size_of(ops)\n            + self.m23.size_of(ops)\n            + self.m24.size_of(ops)\n            + self.m31.size_of(ops)\n            + self.m32.size_of(ops)\n            + self.m33.size_of(ops)\n            + self.m34.size_of(ops)\n            + self.m41.size_of(ops)\n            + self.m42.size_of(ops)\n            + self.m43.size_of(ops)\n            + self.m44.size_of(ops)\n    }\n}\n\nimpl<T: MallocSizeOf, U> MallocSizeOf for euclid::Vector2D<T, U> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.x.size_of(ops) + self.y.size_of(ops)\n    }\n}\n\nimpl MallocSizeOf for selectors::parser::AncestorHashes {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let selectors::parser::AncestorHashes { ref packed_hashes } = *self;\n        packed_hashes.size_of(ops)\n    }\n}\n\nimpl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf\n    for selectors::parser::Selector<Impl>\nwhere\n    Impl::NonTSPseudoClass: MallocSizeOf,\n    Impl::PseudoElement: MallocSizeOf,\n{\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n\n        // It's OK to measure this ThinArc directly because it's the\n        // \"primary\" reference. (The secondary references are on the\n        // Stylist.)\n        n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) };\n        for component in self.iter_raw_match_order() {\n            n += component.size_of(ops);\n        }\n\n        n\n    }\n}\n\nimpl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf\n    for selectors::parser::SelectorList<Impl>\nwhere\n    Impl::NonTSPseudoClass: MallocSizeOf,\n    Impl::PseudoElement: MallocSizeOf,\n{\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n\n        // It's OK to measure this ThinArc directly because it's the \"primary\" reference. (The\n        // secondary references are on the Stylist.)\n        n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) };\n        if self.len() > 1 {\n            for selector in self.slice().iter() {\n                n += selector.size_of(ops);\n            }\n        }\n        n\n    }\n}\n\nimpl<Impl: selectors::parser::SelectorImpl> MallocUnconditionalSizeOf\n    for selectors::parser::Component<Impl>\nwhere\n    Impl::NonTSPseudoClass: MallocSizeOf,\n    Impl::PseudoElement: MallocSizeOf,\n{\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        use selectors::parser::Component;\n\n        match self {\n            Component::AttributeOther(ref attr_selector) => attr_selector.size_of(ops),\n            Component::Negation(ref components) => components.unconditional_size_of(ops),\n            Component::NonTSPseudoClass(ref pseudo) => (*pseudo).size_of(ops),\n            Component::Slotted(ref selector) | Component::Host(Some(ref selector)) => {\n                selector.unconditional_size_of(ops)\n            },\n            Component::Is(ref list) | Component::Where(ref list) => list.unconditional_size_of(ops),\n            Component::Has(ref relative_selectors) => relative_selectors.size_of(ops),\n            Component::NthOf(ref nth_of_data) => nth_of_data.size_of(ops),\n            Component::PseudoElement(ref pseudo) => (*pseudo).size_of(ops),\n            Component::Combinator(..)\n            | Component::ExplicitAnyNamespace\n            | Component::ExplicitNoNamespace\n            | Component::DefaultNamespace(..)\n            | Component::Namespace(..)\n            | Component::ExplicitUniversalType\n            | Component::LocalName(..)\n            | Component::ID(..)\n            | Component::Part(..)\n            | Component::Class(..)\n            | Component::AttributeInNoNamespaceExists { .. }\n            | Component::AttributeInNoNamespace { .. }\n            | Component::Root\n            | Component::Empty\n            | Component::Scope\n            | Component::ImplicitScope\n            | Component::ParentSelector\n            | Component::Nth(..)\n            | Component::Host(None)\n            | Component::RelativeSelectorAnchor\n            | Component::Invalid(..) => 0,\n        }\n    }\n}\n\nimpl<Impl: selectors::parser::SelectorImpl> MallocSizeOf\n    for selectors::attr::AttrSelectorWithOptionalNamespace<Impl>\n{\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        0\n    }\n}\n\nimpl MallocSizeOf for selectors::parser::AnPlusB {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        0\n    }\n}\n\nimpl MallocSizeOf for Void {\n    #[inline]\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        void::unreachable(*self)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Static: string_cache::StaticAtomSet> MallocSizeOf for string_cache::Atom<Static> {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        0\n    }\n}\n\n/// For use on types where size_of() returns 0.\n#[macro_export]\nmacro_rules! malloc_size_of_is_0(\n    ($($ty:ty),+) => (\n        $(\n            impl $crate::MallocSizeOf for $ty {\n                #[inline(always)]\n                fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {\n                    0\n                }\n            }\n        )+\n    );\n    ($($ty:ident<$($gen:ident),+>),+) => (\n        $(\n        impl<$($gen: $crate::MallocSizeOf),+> $crate::MallocSizeOf for $ty<$($gen),+> {\n            #[inline(always)]\n            fn size_of(&self, _: &mut $crate::MallocSizeOfOps) -> usize {\n                0\n            }\n        }\n        )+\n    );\n);\n\nmalloc_size_of_is_0!(bool, char, str);\nmalloc_size_of_is_0!(u8, u16, u32, u64, u128, usize);\nmalloc_size_of_is_0!(i8, i16, i32, i64, i128, isize);\nmalloc_size_of_is_0!(f32, f64);\n\nmalloc_size_of_is_0!(std::sync::atomic::AtomicBool);\nmalloc_size_of_is_0!(std::sync::atomic::AtomicIsize);\nmalloc_size_of_is_0!(std::sync::atomic::AtomicU32);\nmalloc_size_of_is_0!(std::sync::atomic::AtomicUsize);\nmalloc_size_of_is_0!(std::num::NonZeroUsize);\nmalloc_size_of_is_0!(std::num::NonZeroU64);\n\nmalloc_size_of_is_0!(Range<u8>, Range<u16>, Range<u32>, Range<u64>, Range<usize>);\nmalloc_size_of_is_0!(Range<i8>, Range<i16>, Range<i32>, Range<i64>, Range<isize>);\nmalloc_size_of_is_0!(Range<f32>, Range<f64>);\n\nmalloc_size_of_is_0!(app_units::Au);\n\nmalloc_size_of_is_0!(\n    cssparser::TokenSerializationType,\n    cssparser::SourceLocation,\n    cssparser::SourcePosition,\n    cssparser::UnicodeRange\n);\n\nmalloc_size_of_is_0!(selectors::OpaqueElement);\n\n/// Measurable that defers to inner value and used to verify MallocSizeOf implementation in a\n/// struct.\n#[derive(Clone)]\npub struct Measurable<T: MallocSizeOf>(pub T);\n\nimpl<T: MallocSizeOf> Deref for Measurable<T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        &self.0\n    }\n}\n\nimpl<T: MallocSizeOf> DerefMut for Measurable<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.0\n    }\n}\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "match_block_trailing_comma = true\nreorder_imports = true\n"
  },
  {
    "path": "selectors/CHANGES.md",
    "content": "- `parser.rs` no longer wraps values in quotes (`\"...\"`) but expects their `to_css` impl to already wrap it ([Gecko Bug 1854809](https://bugzilla.mozilla.org/show_bug.cgi?id=1854809))\n"
  },
  {
    "path": "selectors/Cargo.toml",
    "content": "[package]\nname = \"selectors\"\nversion = \"0.38.0\"\nauthors = [\"The Servo Project Developers\"]\ndocumentation = \"https://docs.rs/selectors/\"\ndescription = \"CSS Selectors matching for Rust\"\nrepository = \"https://github.com/servo/stylo\"\nreadme = \"README.md\"\nkeywords = [\"css\", \"selectors\"]\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nbuild = \"build.rs\"\n\n[lib]\nname = \"selectors\"\npath = \"lib.rs\"\n\n[features]\nbench = []\nto_shmem = [\"dep:to_shmem\", \"dep:to_shmem_derive\"]\n\n[dependencies]\nbitflags = \"2\"\ncssparser = \"0.37\"\nderive_more = { version = \"2\", features = [\"add\", \"add_assign\"] }\nrustc-hash = \"2.1.1\"\nlog = \"0.4\"\nphf = \"0.13\"\nprecomputed-hash = \"0.1\"\nservo_arc = { workspace = true }\nsmallvec = \"1.0\"\nto_shmem = { workspace = true, optional = true }\nto_shmem_derive = { workspace = true, optional = true }\nnew_debug_unreachable = \"1\"\n\n[build-dependencies]\nphf_codegen = \"0.13\"\n"
  },
  {
    "path": "selectors/README.md",
    "content": "rust-selectors\n==============\n\n* [![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions)\n* [Documentation](https://docs.rs/selectors)\n* [crates.io](https://crates.io/crates/selectors)\n\nCSS Selectors library for Rust.\nIncludes parsing and serilization of selectors,\nas well as matching against a generic tree of elements.\nPseudo-elements and most pseudo-classes are generic as well.\n\n**Warning:** breaking changes are made to this library fairly frequently\n(13 times in 2016, for example).\nHowever you can use this crate without updating it that often,\nold versions stay available on crates.io and Cargo will only automatically update\nto versions that are numbered as compatible.\n\nTo see how to use this library with your own tree representation,\nsee [Kuchiki’s `src/select.rs`](https://github.com/kuchiki-rs/kuchiki/blob/master/src/select.rs).\n(Note however that Kuchiki is not always up to date with the latest rust-selectors version,\nso that code may need to be tweaked.)\nIf you don’t already have a tree data structure,\nconsider using [Kuchiki](https://github.com/kuchiki-rs/kuchiki) itself.\n"
  },
  {
    "path": "selectors/attr.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::parser::SelectorImpl;\nuse cssparser::ToCss;\nuse std::fmt;\n\n#[cfg(feature = \"to_shmem\")]\nuse to_shmem_derive::ToShmem;\n\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct AttrSelectorWithOptionalNamespace<Impl: SelectorImpl> {\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    pub namespace: Option<NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>>,\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    pub local_name: Impl::LocalName,\n    pub local_name_lower: Impl::LocalName,\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>,\n}\n\nimpl<Impl: SelectorImpl> AttrSelectorWithOptionalNamespace<Impl> {\n    pub fn namespace(&self) -> Option<NamespaceConstraint<&Impl::NamespaceUrl>> {\n        self.namespace.as_ref().map(|ns| match ns {\n            NamespaceConstraint::Any => NamespaceConstraint::Any,\n            NamespaceConstraint::Specific((_, ref url)) => NamespaceConstraint::Specific(url),\n        })\n    }\n}\n\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum NamespaceConstraint<NamespaceUrl> {\n    Any,\n\n    /// Empty string for no namespace\n    Specific(NamespaceUrl),\n}\n\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum ParsedAttrSelectorOperation<AttrValue> {\n    Exists,\n    WithValue {\n        operator: AttrSelectorOperator,\n        case_sensitivity: ParsedCaseSensitivity,\n        value: AttrValue,\n    },\n}\n\n#[derive(Clone, Eq, PartialEq)]\npub enum AttrSelectorOperation<AttrValue> {\n    Exists,\n    WithValue {\n        operator: AttrSelectorOperator,\n        case_sensitivity: CaseSensitivity,\n        value: AttrValue,\n    },\n}\n\nimpl<AttrValue> AttrSelectorOperation<AttrValue> {\n    pub fn eval_str(&self, element_attr_value: &str) -> bool\n    where\n        AttrValue: AsRef<str>,\n    {\n        match *self {\n            AttrSelectorOperation::Exists => true,\n            AttrSelectorOperation::WithValue {\n                operator,\n                case_sensitivity,\n                ref value,\n            } => operator.eval_str(element_attr_value, value.as_ref(), case_sensitivity),\n        }\n    }\n}\n\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum AttrSelectorOperator {\n    Equal,\n    Includes,\n    DashMatch,\n    Prefix,\n    Substring,\n    Suffix,\n}\n\nimpl ToCss for AttrSelectorOperator {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        // https://drafts.csswg.org/cssom/#serializing-selectors\n        // See \"attribute selector\".\n        dest.write_str(match *self {\n            AttrSelectorOperator::Equal => \"=\",\n            AttrSelectorOperator::Includes => \"~=\",\n            AttrSelectorOperator::DashMatch => \"|=\",\n            AttrSelectorOperator::Prefix => \"^=\",\n            AttrSelectorOperator::Substring => \"*=\",\n            AttrSelectorOperator::Suffix => \"$=\",\n        })\n    }\n}\n\nimpl AttrSelectorOperator {\n    pub fn eval_str(\n        self,\n        element_attr_value: &str,\n        attr_selector_value: &str,\n        case_sensitivity: CaseSensitivity,\n    ) -> bool {\n        let e = element_attr_value.as_bytes();\n        let s = attr_selector_value.as_bytes();\n        let case = case_sensitivity;\n        match self {\n            AttrSelectorOperator::Equal => case.eq(e, s),\n            AttrSelectorOperator::Prefix => {\n                !s.is_empty() && e.len() >= s.len() && case.eq(&e[..s.len()], s)\n            },\n            AttrSelectorOperator::Suffix => {\n                !s.is_empty() && e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)\n            },\n            AttrSelectorOperator::Substring => {\n                !s.is_empty() && case.contains(element_attr_value, attr_selector_value)\n            },\n            AttrSelectorOperator::Includes => {\n                !s.is_empty()\n                    && element_attr_value\n                        .split(SELECTOR_WHITESPACE)\n                        .any(|part| case.eq(part.as_bytes(), s))\n            },\n            AttrSelectorOperator::DashMatch => {\n                case.eq(e, s) || (e.get(s.len()) == Some(&b'-') && case.eq(&e[..s.len()], s))\n            },\n        }\n    }\n}\n\n/// The definition of whitespace per CSS Selectors Level 3 § 4.\npub static SELECTOR_WHITESPACE: &[char] = &[' ', '\\t', '\\n', '\\r', '\\x0C'];\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum ParsedCaseSensitivity {\n    /// 's' was specified.\n    ExplicitCaseSensitive,\n    /// 'i' was specified.\n    AsciiCaseInsensitive,\n    /// No flags were specified and HTML says this is a case-sensitive attribute.\n    CaseSensitive,\n    /// No flags were specified and HTML says this is a case-insensitive attribute.\n    AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum CaseSensitivity {\n    CaseSensitive,\n    AsciiCaseInsensitive,\n}\n\nimpl CaseSensitivity {\n    pub fn eq(self, a: &[u8], b: &[u8]) -> bool {\n        match self {\n            CaseSensitivity::CaseSensitive => a == b,\n            CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),\n        }\n    }\n\n    pub fn contains(self, haystack: &str, needle: &str) -> bool {\n        match self {\n            CaseSensitivity::CaseSensitive => haystack.contains(needle),\n            CaseSensitivity::AsciiCaseInsensitive => {\n                if let Some((&n_first_byte, n_rest)) = needle.as_bytes().split_first() {\n                    haystack.bytes().enumerate().any(|(i, byte)| {\n                        if !byte.eq_ignore_ascii_case(&n_first_byte) {\n                            return false;\n                        }\n                        let after_this_byte = &haystack.as_bytes()[i + 1..];\n                        match after_this_byte.get(..n_rest.len()) {\n                            None => false,\n                            Some(haystack_slice) => haystack_slice.eq_ignore_ascii_case(n_rest),\n                        }\n                    })\n                } else {\n                    // any_str.contains(\"\") == true,\n                    // though these cases should be handled with *NeverMatches and never go here.\n                    true\n                }\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "selectors/bloom.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Counting and non-counting Bloom filters tuned for use as ancestor filters\n//! for selector matching.\n\nuse std::fmt::{self, Debug};\n\n// The top 8 bits of the 32-bit hash value are not used by the bloom filter.\n// Consumers may rely on this to pack hashes more efficiently.\npub const BLOOM_HASH_MASK: u32 = 0x00ffffff;\nconst KEY_SIZE: usize = 12;\n\nconst ARRAY_SIZE: usize = 1 << KEY_SIZE;\nconst KEY_MASK: u32 = (1 << KEY_SIZE) - 1;\n\n/// A counting Bloom filter with 8-bit counters.\npub type BloomFilter = CountingBloomFilter<BloomStorageU8>;\n\n/// A counting Bloom filter with parameterized storage to handle\n/// counters of different sizes.  For now we assume that having two hash\n/// functions is enough, but we may revisit that decision later.\n///\n/// The filter uses an array with 2**KeySize entries.\n///\n/// Assuming a well-distributed hash function, a Bloom filter with\n/// array size M containing N elements and\n/// using k hash function has expected false positive rate exactly\n///\n/// $  (1 - (1 - 1/M)^{kN})^k  $\n///\n/// because each array slot has a\n///\n/// $  (1 - 1/M)^{kN}  $\n///\n/// chance of being 0, and the expected false positive rate is the\n/// probability that all of the k hash functions will hit a nonzero\n/// slot.\n///\n/// For reasonable assumptions (M large, kN large, which should both\n/// hold if we're worried about false positives) about M and kN this\n/// becomes approximately\n///\n/// $$  (1 - \\exp(-kN/M))^k   $$\n///\n/// For our special case of k == 2, that's $(1 - \\exp(-2N/M))^2$,\n/// or in other words\n///\n/// $$    N/M = -0.5 * \\ln(1 - \\sqrt(r))   $$\n///\n/// where r is the false positive rate.  This can be used to compute\n/// the desired KeySize for a given load N and false positive rate r.\n///\n/// If N/M is assumed small, then the false positive rate can\n/// further be approximated as 4*N^2/M^2.  So increasing KeySize by\n/// 1, which doubles M, reduces the false positive rate by about a\n/// factor of 4, and a false positive rate of 1% corresponds to\n/// about M/N == 20.\n///\n/// What this means in practice is that for a few hundred keys using a\n/// KeySize of 12 gives false positive rates on the order of 0.25-4%.\n///\n/// Similarly, using a KeySize of 10 would lead to a 4% false\n/// positive rate for N == 100 and to quite bad false positive\n/// rates for larger N.\n#[derive(Clone, Default)]\npub struct CountingBloomFilter<S>\nwhere\n    S: BloomStorage,\n{\n    storage: S,\n}\n\nimpl<S> CountingBloomFilter<S>\nwhere\n    S: BloomStorage,\n{\n    /// Creates a new bloom filter.\n    #[inline]\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    #[inline]\n    pub fn clear(&mut self) {\n        self.storage = Default::default();\n    }\n\n    // Slow linear accessor to make sure the bloom filter is zeroed. This should\n    // never be used in release builds.\n    #[cfg(debug_assertions)]\n    pub fn is_zeroed(&self) -> bool {\n        self.storage.is_zeroed()\n    }\n\n    #[cfg(not(debug_assertions))]\n    pub fn is_zeroed(&self) -> bool {\n        unreachable!()\n    }\n\n    /// Inserts an item with a particular hash into the bloom filter.\n    #[inline]\n    pub fn insert_hash(&mut self, hash: u32) {\n        self.storage.adjust_first_slot(hash, true);\n        self.storage.adjust_second_slot(hash, true);\n    }\n\n    /// Removes an item with a particular hash from the bloom filter.\n    #[inline]\n    pub fn remove_hash(&mut self, hash: u32) {\n        self.storage.adjust_first_slot(hash, false);\n        self.storage.adjust_second_slot(hash, false);\n    }\n\n    /// Check whether the filter might contain an item with the given hash.\n    /// This can sometimes return true even if the item is not in the filter,\n    /// but will never return false for items that are actually in the filter.\n    #[inline]\n    pub fn might_contain_hash(&self, hash: u32) -> bool {\n        !self.storage.first_slot_is_empty(hash) && !self.storage.second_slot_is_empty(hash)\n    }\n}\n\nimpl<S> Debug for CountingBloomFilter<S>\nwhere\n    S: BloomStorage,\n{\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let mut slots_used = 0;\n        for i in 0..ARRAY_SIZE {\n            if !self.storage.slot_is_empty(i) {\n                slots_used += 1;\n            }\n        }\n        write!(f, \"BloomFilter({}/{})\", slots_used, ARRAY_SIZE)\n    }\n}\n\npub trait BloomStorage: Clone + Default {\n    fn slot_is_empty(&self, index: usize) -> bool;\n    fn adjust_slot(&mut self, index: usize, increment: bool);\n    fn is_zeroed(&self) -> bool;\n\n    #[inline]\n    fn first_slot_is_empty(&self, hash: u32) -> bool {\n        self.slot_is_empty(Self::first_slot_index(hash))\n    }\n\n    #[inline]\n    fn second_slot_is_empty(&self, hash: u32) -> bool {\n        self.slot_is_empty(Self::second_slot_index(hash))\n    }\n\n    #[inline]\n    fn adjust_first_slot(&mut self, hash: u32, increment: bool) {\n        self.adjust_slot(Self::first_slot_index(hash), increment)\n    }\n\n    #[inline]\n    fn adjust_second_slot(&mut self, hash: u32, increment: bool) {\n        self.adjust_slot(Self::second_slot_index(hash), increment)\n    }\n\n    #[inline]\n    fn first_slot_index(hash: u32) -> usize {\n        hash1(hash) as usize\n    }\n\n    #[inline]\n    fn second_slot_index(hash: u32) -> usize {\n        hash2(hash) as usize\n    }\n}\n\n/// Storage class for a CountingBloomFilter that has 8-bit counters.\npub struct BloomStorageU8 {\n    counters: [u8; ARRAY_SIZE],\n}\n\nimpl BloomStorage for BloomStorageU8 {\n    #[inline]\n    fn adjust_slot(&mut self, index: usize, increment: bool) {\n        let slot = &mut self.counters[index];\n        if *slot != 0xff {\n            // full\n            if increment {\n                *slot += 1;\n            } else {\n                *slot -= 1;\n            }\n        }\n    }\n\n    #[inline]\n    fn slot_is_empty(&self, index: usize) -> bool {\n        self.counters[index] == 0\n    }\n\n    #[inline]\n    fn is_zeroed(&self) -> bool {\n        self.counters.iter().all(|x| *x == 0)\n    }\n}\n\nimpl Default for BloomStorageU8 {\n    fn default() -> Self {\n        BloomStorageU8 {\n            counters: [0; ARRAY_SIZE],\n        }\n    }\n}\n\nimpl Clone for BloomStorageU8 {\n    fn clone(&self) -> Self {\n        BloomStorageU8 {\n            counters: self.counters,\n        }\n    }\n}\n\n/// Storage class for a CountingBloomFilter that has 1-bit counters.\npub struct BloomStorageBool {\n    counters: [u8; ARRAY_SIZE / 8],\n}\n\nimpl BloomStorage for BloomStorageBool {\n    #[inline]\n    fn adjust_slot(&mut self, index: usize, increment: bool) {\n        let bit = 1 << (index % 8);\n        let byte = &mut self.counters[index / 8];\n\n        // Since we have only one bit for storage, decrementing it\n        // should never do anything.  Assert against an accidental\n        // decrementing of a bit that was never set.\n        assert!(\n            increment || (*byte & bit) != 0,\n            \"should not decrement if slot is already false\"\n        );\n\n        if increment {\n            *byte |= bit;\n        }\n    }\n\n    #[inline]\n    fn slot_is_empty(&self, index: usize) -> bool {\n        let bit = 1 << (index % 8);\n        (self.counters[index / 8] & bit) == 0\n    }\n\n    #[inline]\n    fn is_zeroed(&self) -> bool {\n        self.counters.iter().all(|x| *x == 0)\n    }\n}\n\nimpl Default for BloomStorageBool {\n    fn default() -> Self {\n        BloomStorageBool {\n            counters: [0; ARRAY_SIZE / 8],\n        }\n    }\n}\n\nimpl Clone for BloomStorageBool {\n    fn clone(&self) -> Self {\n        BloomStorageBool {\n            counters: self.counters,\n        }\n    }\n}\n\n#[inline]\nfn hash1(hash: u32) -> u32 {\n    hash & KEY_MASK\n}\n\n#[inline]\nfn hash2(hash: u32) -> u32 {\n    (hash >> KEY_SIZE) & KEY_MASK\n}\n\n#[test]\nfn create_and_insert_some_stuff() {\n    use rustc_hash::FxHasher;\n    use std::hash::{Hash, Hasher};\n    use std::mem::transmute;\n\n    fn hash_as_str(i: usize) -> u32 {\n        let mut hasher = FxHasher::default();\n        let s = i.to_string();\n        s.hash(&mut hasher);\n        let hash: u64 = hasher.finish();\n        (hash >> 32) as u32 ^ (hash as u32)\n    }\n\n    let mut bf = BloomFilter::new();\n\n    // Statically assert that ARRAY_SIZE is a multiple of 8, which\n    // BloomStorageBool relies on.\n    unsafe {\n        transmute::<[u8; ARRAY_SIZE % 8], [u8; 0]>([]);\n    }\n\n    for i in 0_usize..1000 {\n        bf.insert_hash(hash_as_str(i));\n    }\n\n    for i in 0_usize..1000 {\n        assert!(bf.might_contain_hash(hash_as_str(i)));\n    }\n\n    let false_positives = (1001_usize..2000)\n        .filter(|i| bf.might_contain_hash(hash_as_str(*i)))\n        .count();\n\n    assert!(false_positives < 190, \"{} is not < 190\", false_positives); // 19%.\n\n    for i in 0_usize..100 {\n        bf.remove_hash(hash_as_str(i));\n    }\n\n    for i in 100_usize..1000 {\n        assert!(bf.might_contain_hash(hash_as_str(i)));\n    }\n\n    let false_positives = (0_usize..100)\n        .filter(|i| bf.might_contain_hash(hash_as_str(*i)))\n        .count();\n\n    assert!(false_positives < 20, \"{} is not < 20\", false_positives); // 20%.\n\n    bf.clear();\n\n    for i in 0_usize..2000 {\n        assert!(!bf.might_contain_hash(hash_as_str(i)));\n    }\n}\n\n#[cfg(feature = \"bench\")]\n#[cfg(test)]\nmod bench {\n    extern crate test;\n    use super::BloomFilter;\n\n    #[derive(Default)]\n    struct HashGenerator(u32);\n\n    impl HashGenerator {\n        fn next(&mut self) -> u32 {\n            // Each hash is split into two twelve-bit segments, which are used\n            // as an index into an array. We increment each by 64 so that we\n            // hit the next cache line, and then another 1 so that our wrapping\n            // behavior leads us to different entries each time.\n            //\n            // Trying to simulate cold caches is rather difficult with the cargo\n            // benchmarking setup, so it may all be moot depending on the number\n            // of iterations that end up being run. But we might as well.\n            self.0 += (65) + (65 << super::KEY_SIZE);\n            self.0\n        }\n    }\n\n    #[bench]\n    fn create_insert_1000_remove_100_lookup_100(b: &mut test::Bencher) {\n        b.iter(|| {\n            let mut gen1 = HashGenerator::default();\n            let mut gen2 = HashGenerator::default();\n            let mut bf = BloomFilter::new();\n            for _ in 0_usize..1000 {\n                bf.insert_hash(gen1.next());\n            }\n            for _ in 0_usize..100 {\n                bf.remove_hash(gen2.next());\n            }\n            for _ in 100_usize..200 {\n                test::black_box(bf.might_contain_hash(gen2.next()));\n            }\n        });\n    }\n\n    #[bench]\n    fn might_contain_10(b: &mut test::Bencher) {\n        let bf = BloomFilter::new();\n        let mut gen = HashGenerator::default();\n        b.iter(|| {\n            for _ in 0..10 {\n                test::black_box(bf.might_contain_hash(gen.next()));\n            }\n        });\n    }\n\n    #[bench]\n    fn clear(b: &mut test::Bencher) {\n        let mut bf = Box::new(BloomFilter::new());\n        b.iter(|| test::black_box(&mut bf).clear());\n    }\n\n    #[bench]\n    fn insert_10(b: &mut test::Bencher) {\n        let mut bf = BloomFilter::new();\n        let mut gen = HashGenerator::default();\n        b.iter(|| {\n            for _ in 0..10 {\n                test::black_box(bf.insert_hash(gen.next()));\n            }\n        });\n    }\n\n    #[bench]\n    fn remove_10(b: &mut test::Bencher) {\n        let mut bf = BloomFilter::new();\n        let mut gen = HashGenerator::default();\n        // Note: this will underflow, and that's ok.\n        b.iter(|| {\n            for _ in 0..10 {\n                bf.remove_hash(gen.next())\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "selectors/build.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nextern crate phf_codegen;\n\nuse std::env;\nuse std::fs::File;\nuse std::io::{BufWriter, Write};\nuse std::path::Path;\n\nfn main() {\n    let path = Path::new(&env::var_os(\"OUT_DIR\").unwrap())\n        .join(\"ascii_case_insensitive_html_attributes.rs\");\n    let mut file = BufWriter::new(File::create(&path).unwrap());\n\n    let mut set = phf_codegen::Set::new();\n    for name in ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES.split_whitespace() {\n        set.entry(name);\n    }\n    write!(\n        &mut file,\n        \"{{ static SET: ::phf::Set<&'static str> = {}; &SET }}\",\n        set.build(),\n    )\n    .unwrap();\n}\n\n/// <https://html.spec.whatwg.org/multipage/#selectors>\nstatic ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES: &str = r#\"\n    accept\n    accept-charset\n    align\n    alink\n    axis\n    bgcolor\n    charset\n    checked\n    clear\n    codetype\n    color\n    compact\n    declare\n    defer\n    dir\n    direction\n    disabled\n    enctype\n    face\n    frame\n    hreflang\n    http-equiv\n    lang\n    language\n    link\n    media\n    method\n    multiple\n    nohref\n    noresize\n    noshade\n    nowrap\n    readonly\n    rel\n    rev\n    rules\n    scope\n    scrolling\n    selected\n    shape\n    target\n    text\n    type\n    valign\n    valuetype\n    vlink\n\"#;\n"
  },
  {
    "path": "selectors/builder.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Helper module to build up a selector safely and efficiently.\n//!\n//! Our selector representation is designed to optimize matching, and has\n//! several requirements:\n//! * All simple selectors and combinators are stored inline in the same buffer as Component\n//!   instances.\n//! * We store the top-level compound selectors from right to left, i.e. in matching order.\n//! * We store the simple selectors for each combinator from left to right, so that we match the\n//!   cheaper simple selectors first.\n//!\n//! For example, the selector:\n//!\n//!   .bar:hover > .baz:nth-child(2) + .qux\n//!\n//! Gets stored as:\n//!\n//!   [.qux,  + , .baz, :nth-child(2),  > , .bar, :hover]\n//!\n//! Meeting all these constraints without extra memmove traffic during parsing is non-trivial. This\n//! module encapsulates those details and presents an easy-to-use API for the parser.\n\nuse crate::parser::{\n    Combinator, Component, ParseRelative, RelativeSelector, Selector, SelectorData, SelectorImpl,\n};\nuse crate::sink::Push;\nuse bitflags::bitflags;\nuse derive_more::{Add, AddAssign};\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::cmp;\nuse std::slice;\n\n#[cfg(feature = \"to_shmem\")]\nuse to_shmem_derive::ToShmem;\n\n/// Top-level SelectorBuilder struct. This should be stack-allocated by the consumer and never\n/// moved (because it contains a lot of inline data that would be slow to memmove).\n///\n/// After instantiation, callers may call the push_simple_selector() and push_combinator() methods\n/// to append selector data as it is encountered (from left to right). Once the process is\n/// complete, callers should invoke build(), which transforms the contents of the SelectorBuilder\n/// into a heap- allocated Selector and leaves the builder in a drained state.\n#[derive(Debug)]\npub struct SelectorBuilder<Impl: SelectorImpl> {\n    /// The entire sequence of components. We make this large because the result of parsing a\n    /// selector is fed into a new Arc-ed allocation, so any spilled vec would be a wasted\n    /// allocation. Also, Components are large enough that we don't have much cache locality\n    /// benefit from reserving stack space for fewer of them.\n    components: SmallVec<[Component<Impl>; 32]>,\n    last_compound_start: Option<usize>,\n}\n\nimpl<Impl: SelectorImpl> Push<Component<Impl>> for SelectorBuilder<Impl> {\n    fn push(&mut self, value: Component<Impl>) {\n        self.push_simple_selector(value);\n    }\n}\n\nimpl<Impl: SelectorImpl> SelectorBuilder<Impl> {\n    /// Pushes a simple selector onto the current compound selector.\n    #[inline(always)]\n    pub fn push_simple_selector(&mut self, ss: Component<Impl>) {\n        debug_assert!(!ss.is_combinator());\n        self.components.push(ss);\n    }\n\n    /// Completes the current compound selector and starts a new one, delimited by the given\n    /// combinator.\n    #[inline(always)]\n    pub fn push_combinator(&mut self, c: Combinator) {\n        self.reverse_last_compound();\n        self.components.push(Component::Combinator(c));\n        self.last_compound_start = Some(self.components.len());\n    }\n\n    fn reverse_last_compound(&mut self) {\n        let start = self.last_compound_start.unwrap_or(0);\n        self.components[start..].reverse();\n    }\n\n    /// Returns true if combinators have ever been pushed to this builder.\n    #[inline(always)]\n    pub fn has_combinators(&self) -> bool {\n        self.last_compound_start.is_some()\n    }\n\n    /// Consumes the builder, producing a Selector.\n    #[inline(always)]\n    pub fn build(&mut self, parse_relative: ParseRelative) -> SelectorData<Impl> {\n        // Compute the specificity and flags.\n        let sf = specificity_and_flags(\n            self.components.iter(),\n            /* for_nesting_parent = */ false,\n        );\n        self.build_with_specificity_and_flags(sf, parse_relative)\n    }\n\n    /// Builds with an explicit SpecificityAndFlags. This is separated from build() so that unit\n    /// tests can pass an explicit specificity.\n    #[inline(always)]\n    pub(crate) fn build_with_specificity_and_flags(\n        &mut self,\n        mut spec: SpecificityAndFlags,\n        parse_relative: ParseRelative,\n    ) -> SelectorData<Impl> {\n        let implicit_addition = match parse_relative {\n            ParseRelative::ForNesting if !spec.flags.intersects(SelectorFlags::HAS_PARENT) => {\n                Some((Component::ParentSelector, SelectorFlags::HAS_PARENT))\n            },\n            ParseRelative::ForScope\n                if !spec\n                    .flags\n                    .intersects(SelectorFlags::HAS_SCOPE | SelectorFlags::HAS_PARENT) =>\n            {\n                Some((Component::ImplicitScope, SelectorFlags::HAS_SCOPE))\n            },\n            _ => None,\n        };\n        let implicit_selector_and_combinator;\n        let implicit_selector = if let Some((component, flag)) = implicit_addition {\n            spec.flags.insert(flag);\n            implicit_selector_and_combinator =\n                [Component::Combinator(Combinator::Descendant), component];\n            &implicit_selector_and_combinator[..]\n        } else {\n            &[]\n        };\n\n        // As an optimization, for a selector without combinators, we can just keep the order\n        // as-is.\n        if self.last_compound_start.is_none() {\n            return Arc::from_header_and_iter(\n                spec,\n                ExactChain(self.components.drain(..), implicit_selector.iter().cloned()),\n            );\n        }\n\n        self.reverse_last_compound();\n        Arc::from_header_and_iter(\n            spec,\n            ExactChain(\n                self.components.drain(..).rev(),\n                implicit_selector.iter().cloned(),\n            ),\n        )\n    }\n}\n\nimpl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {\n    #[inline(always)]\n    fn default() -> Self {\n        SelectorBuilder {\n            components: SmallVec::new(),\n            last_compound_start: None,\n        }\n    }\n}\n\n// This is effectively a Chain<>, but Chain isn't an ExactSizeIterator, see\n// https://github.com/rust-lang/rust/issues/34433\nstruct ExactChain<A, B>(A, B);\n\nimpl<A, B, Item> ExactSizeIterator for ExactChain<A, B>\nwhere\n    A: ExactSizeIterator<Item = Item>,\n    B: ExactSizeIterator<Item = Item>,\n{\n    fn len(&self) -> usize {\n        self.0.len() + self.1.len()\n    }\n}\n\nimpl<A, B, Item> Iterator for ExactChain<A, B>\nwhere\n    A: ExactSizeIterator<Item = Item>,\n    B: ExactSizeIterator<Item = Item>,\n{\n    type Item = Item;\n\n    #[inline(always)]\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next().or_else(|| self.1.next())\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let len = self.len();\n        (len, Some(len))\n    }\n}\n\n/// Flags that indicate at which point of parsing a selector are we.\n#[derive(Clone, Copy, Default, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub(crate) struct SelectorFlags(u8);\n\nbitflags! {\n    impl SelectorFlags: u8 {\n        const HAS_PSEUDO = 1 << 0;\n        const HAS_SLOTTED = 1 << 1;\n        const HAS_PART = 1 << 2;\n        const HAS_PARENT = 1 << 3;\n        const HAS_HOST = 1 << 4;\n        const HAS_SCOPE = 1 << 5;\n    }\n}\n\nimpl core::fmt::Debug for SelectorFlags {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n        if self.is_empty() {\n            write!(f, \"{:#x}\", Self::empty().bits())\n        } else {\n            bitflags::parser::to_writer(self, f)\n        }\n    }\n}\n\nimpl SelectorFlags {\n    /// When you nest a pseudo-element with something like:\n    ///\n    ///   ::before { & { .. } }\n    ///\n    /// It is not supposed to work, because :is(::before) is invalid. We can't propagate the\n    /// pseudo-flags from inner to outer selectors, to avoid breaking our invariants.\n    pub(crate) fn forbidden_for_nesting() -> Self {\n        Self::HAS_PSEUDO | Self::HAS_SLOTTED | Self::HAS_PART\n    }\n}\n\n#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub struct SpecificityAndFlags {\n    /// There are two free bits here, since we use ten bits for each specificity\n    /// kind (id, class, element).\n    pub(crate) specificity: u32,\n    /// There's padding after this field due to the size of the flags.\n    pub(crate) flags: SelectorFlags,\n}\n\nconst MAX_10BIT: u32 = (1u32 << 10) - 1;\n\n#[derive(Add, AddAssign, Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]\npub(crate) struct Specificity {\n    id_selectors: u32,\n    class_like_selectors: u32,\n    element_selectors: u32,\n}\n\nimpl Specificity {\n    // Return the specficity of a single class-like selector.\n    #[inline]\n    pub fn single_class_like() -> Self {\n        Specificity {\n            id_selectors: 0,\n            class_like_selectors: 1,\n            element_selectors: 0,\n        }\n    }\n}\n\nimpl From<u32> for Specificity {\n    #[inline]\n    fn from(value: u32) -> Specificity {\n        assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);\n        Specificity {\n            id_selectors: value >> 20,\n            class_like_selectors: (value >> 10) & MAX_10BIT,\n            element_selectors: value & MAX_10BIT,\n        }\n    }\n}\n\nimpl From<Specificity> for u32 {\n    #[inline]\n    fn from(specificity: Specificity) -> u32 {\n        cmp::min(specificity.id_selectors, MAX_10BIT) << 20\n            | cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10\n            | cmp::min(specificity.element_selectors, MAX_10BIT)\n    }\n}\n\nfn specificity_and_flags<Impl>(\n    iter: slice::Iter<Component<Impl>>,\n    for_nesting_parent: bool,\n) -> SpecificityAndFlags\nwhere\n    Impl: SelectorImpl,\n{\n    fn component_specificity<Impl>(\n        simple_selector: &Component<Impl>,\n        specificity: &mut Specificity,\n        flags: &mut SelectorFlags,\n        for_nesting_parent: bool,\n    ) where\n        Impl: SelectorImpl,\n    {\n        match *simple_selector {\n            Component::Combinator(..) => {},\n            Component::ParentSelector => flags.insert(SelectorFlags::HAS_PARENT),\n            Component::Part(..) => {\n                flags.insert(SelectorFlags::HAS_PART);\n                if !for_nesting_parent {\n                    specificity.element_selectors += 1\n                }\n            },\n            Component::PseudoElement(ref pseudo) => {\n                use crate::parser::PseudoElement;\n                flags.insert(SelectorFlags::HAS_PSEUDO);\n                if !for_nesting_parent {\n                    specificity.element_selectors += pseudo.specificity_count();\n                }\n            },\n            Component::LocalName(..) => specificity.element_selectors += 1,\n            Component::Slotted(ref selector) => {\n                flags.insert(SelectorFlags::HAS_SLOTTED);\n                if !for_nesting_parent {\n                    specificity.element_selectors += 1;\n                    // Note that due to the way ::slotted works we only compete with\n                    // other ::slotted rules, so the above rule doesn't really\n                    // matter, but we do it still for consistency with other\n                    // pseudo-elements.\n                    //\n                    // See: https://github.com/w3c/csswg-drafts/issues/1915\n                    *specificity += Specificity::from(selector.specificity());\n                }\n                flags.insert(selector.flags());\n            },\n            Component::Host(ref selector) => {\n                flags.insert(SelectorFlags::HAS_HOST);\n                specificity.class_like_selectors += 1;\n                if let Some(ref selector) = *selector {\n                    // See: https://github.com/w3c/csswg-drafts/issues/1915\n                    *specificity += Specificity::from(selector.specificity());\n                    flags.insert(selector.flags());\n                }\n            },\n            Component::ID(..) => {\n                specificity.id_selectors += 1;\n            },\n            Component::Class(..)\n            | Component::AttributeInNoNamespace { .. }\n            | Component::AttributeInNoNamespaceExists { .. }\n            | Component::AttributeOther(..)\n            | Component::Root\n            | Component::Empty\n            | Component::Nth(..)\n            | Component::NonTSPseudoClass(..) => {\n                specificity.class_like_selectors += 1;\n            },\n            Component::Scope | Component::ImplicitScope => {\n                flags.insert(SelectorFlags::HAS_SCOPE);\n                if matches!(*simple_selector, Component::Scope) {\n                    specificity.class_like_selectors += 1;\n                }\n            },\n            Component::NthOf(ref nth_of_data) => {\n                // https://drafts.csswg.org/selectors/#specificity-rules:\n                //\n                //     The specificity of the :nth-last-child() pseudo-class,\n                //     like the :nth-child() pseudo-class, combines the\n                //     specificity of a regular pseudo-class with that of its\n                //     selector argument S.\n                specificity.class_like_selectors += 1;\n                let sf = selector_list_specificity_and_flags(\n                    nth_of_data.selectors().iter(),\n                    for_nesting_parent,\n                );\n                *specificity += Specificity::from(sf.specificity);\n                flags.insert(sf.flags);\n            },\n            // https://drafts.csswg.org/selectors/#specificity-rules:\n            //\n            //     The specificity of an :is(), :not(), or :has() pseudo-class\n            //     is replaced by the specificity of the most specific complex\n            //     selector in its selector list argument.\n            Component::Where(ref list)\n            | Component::Negation(ref list)\n            | Component::Is(ref list) => {\n                let sf = selector_list_specificity_and_flags(\n                    list.slice().iter(),\n                    /* nested = */ true,\n                );\n                if !matches!(*simple_selector, Component::Where(..)) {\n                    *specificity += Specificity::from(sf.specificity);\n                }\n                flags.insert(sf.flags);\n            },\n            Component::Has(ref relative_selectors) => {\n                let sf = relative_selector_list_specificity_and_flags(\n                    relative_selectors,\n                    for_nesting_parent,\n                );\n                *specificity += Specificity::from(sf.specificity);\n                flags.insert(sf.flags);\n            },\n            Component::ExplicitUniversalType\n            | Component::ExplicitAnyNamespace\n            | Component::ExplicitNoNamespace\n            | Component::DefaultNamespace(..)\n            | Component::Namespace(..)\n            | Component::RelativeSelectorAnchor\n            | Component::Invalid(..) => {\n                // Does not affect specificity\n            },\n        }\n    }\n\n    let mut specificity = Default::default();\n    let mut flags = Default::default();\n    for simple_selector in iter {\n        component_specificity(\n            &simple_selector,\n            &mut specificity,\n            &mut flags,\n            for_nesting_parent,\n        );\n    }\n    SpecificityAndFlags {\n        specificity: specificity.into(),\n        flags,\n    }\n}\n\n/// Finds the maximum specificity of elements in the list and returns it.\npub(crate) fn selector_list_specificity_and_flags<'a, Impl: SelectorImpl>(\n    itr: impl Iterator<Item = &'a Selector<Impl>>,\n    for_nesting_parent: bool,\n) -> SpecificityAndFlags {\n    let mut specificity = 0;\n    let mut flags = SelectorFlags::empty();\n    for selector in itr {\n        let selector_flags = selector.flags();\n        let selector_specificity = if for_nesting_parent\n            && selector_flags.intersects(SelectorFlags::forbidden_for_nesting())\n        {\n            // In this case we need to re-compute the specificity.\n            specificity_and_flags(selector.iter_raw_match_order(), for_nesting_parent).specificity\n        } else {\n            selector.specificity()\n        };\n        specificity = std::cmp::max(specificity, selector_specificity);\n        flags.insert(selector.flags());\n    }\n    SpecificityAndFlags { specificity, flags }\n}\n\npub(crate) fn relative_selector_list_specificity_and_flags<Impl: SelectorImpl>(\n    list: &[RelativeSelector<Impl>],\n    for_nesting_parent: bool,\n) -> SpecificityAndFlags {\n    selector_list_specificity_and_flags(list.iter().map(|rel| &rel.selector), for_nesting_parent)\n}\n"
  },
  {
    "path": "selectors/context.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::attr::CaseSensitivity;\nuse crate::bloom::BloomFilter;\nuse crate::nth_index_cache::{NthIndexCache, NthIndexCacheInner};\nuse crate::parser::{Selector, SelectorImpl};\nuse crate::relative_selector::cache::RelativeSelectorCache;\nuse crate::relative_selector::filter::RelativeSelectorFilterMap;\nuse crate::tree::{Element, OpaqueElement};\n\n/// What kind of selector matching mode we should use.\n///\n/// There are two modes of selector matching. The difference is only noticeable\n/// in presence of pseudo-elements.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum MatchingMode {\n    /// Don't ignore any pseudo-element selectors.\n    Normal,\n\n    /// Ignores any stateless pseudo-element selectors in the rightmost sequence\n    /// of simple selectors.\n    ///\n    /// This is useful, for example, to match against ::before when you aren't a\n    /// pseudo-element yourself.\n    ///\n    /// For example, in presence of `::before:hover`, it would never match, but\n    /// `::before` would be ignored as in \"matching\".\n    ///\n    /// It's required for all the selectors you match using this mode to have a\n    /// pseudo-element.\n    ForStatelessPseudoElement,\n}\n\n/// The mode to use when matching unvisited and visited links.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum VisitedHandlingMode {\n    /// All links are matched as if they are unvisted.\n    AllLinksUnvisited,\n    /// All links are matched as if they are visited and unvisited (both :link\n    /// and :visited match).\n    ///\n    /// This is intended to be used from invalidation code, to be conservative\n    /// about whether we need to restyle a link.\n    AllLinksVisitedAndUnvisited,\n    /// A element's \"relevant link\" is the element being matched if it is a link\n    /// or the nearest ancestor link. The relevant link is matched as though it\n    /// is visited, and all other links are matched as if they are unvisited.\n    RelevantLinkVisited,\n}\n\nimpl VisitedHandlingMode {\n    #[inline]\n    pub fn matches_visited(&self) -> bool {\n        matches!(\n            *self,\n            VisitedHandlingMode::RelevantLinkVisited\n                | VisitedHandlingMode::AllLinksVisitedAndUnvisited\n        )\n    }\n\n    #[inline]\n    pub fn matches_unvisited(&self) -> bool {\n        matches!(\n            *self,\n            VisitedHandlingMode::AllLinksUnvisited\n                | VisitedHandlingMode::AllLinksVisitedAndUnvisited\n        )\n    }\n}\n\n/// Whether we need to set selector invalidation flags on elements for this\n/// match request.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum NeedsSelectorFlags {\n    No,\n    Yes,\n}\n\n/// Whether we're matching in the contect of invalidation.\n#[derive(Clone, Copy, PartialEq)]\npub enum MatchingForInvalidation {\n    No,\n    Yes,\n    YesForComparison,\n}\n\nimpl MatchingForInvalidation {\n    /// Are we matching for invalidation?\n    pub fn is_for_invalidation(&self) -> bool {\n        matches!(*self, Self::Yes | Self::YesForComparison)\n    }\n}\n\n/// Which quirks mode is this document in.\n///\n/// See: https://quirks.spec.whatwg.org/\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub enum QuirksMode {\n    /// Quirks mode.\n    Quirks,\n    /// Limited quirks mode.\n    LimitedQuirks,\n    /// No quirks mode.\n    NoQuirks,\n}\n\nimpl QuirksMode {\n    #[inline]\n    pub fn classes_and_ids_case_sensitivity(self) -> CaseSensitivity {\n        match self {\n            QuirksMode::NoQuirks | QuirksMode::LimitedQuirks => CaseSensitivity::CaseSensitive,\n            QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,\n        }\n    }\n}\n\n/// Set of caches (And cache-likes) that speed up expensive selector matches.\n#[derive(Default)]\npub struct SelectorCaches {\n    /// A cache to speed up nth-index-like selectors.\n    pub nth_index: NthIndexCache,\n    /// A cache to speed up relative selector matches. See module documentation.\n    pub relative_selector: RelativeSelectorCache,\n    /// A map of bloom filters to fast-reject relative selector matches.\n    pub relative_selector_filter_map: RelativeSelectorFilterMap,\n}\n\n/// Data associated with the matching process for a element.  This context is\n/// used across many selectors for an element, so it's not appropriate for\n/// transient data that applies to only a single selector.\npub struct MatchingContext<'a, Impl>\nwhere\n    Impl: SelectorImpl,\n{\n    /// Input with the matching mode we should use when matching selectors.\n    matching_mode: MatchingMode,\n    /// Input with the bloom filter used to fast-reject selectors.\n    pub bloom_filter: Option<&'a BloomFilter>,\n    /// The element which is going to match :scope pseudo-class. It can be\n    /// either one :scope element, or the scoping element.\n    ///\n    /// Note that, although in theory there can be multiple :scope elements,\n    /// in current specs, at most one is specified, and when there is one,\n    /// scoping element is not relevant anymore, so we use a single field for\n    /// them.\n    ///\n    /// When this is None, :scope will match the root element.\n    ///\n    /// See https://drafts.csswg.org/selectors-4/#scope-pseudo\n    pub scope_element: Option<OpaqueElement>,\n\n    /// The current shadow host we're collecting :host rules for.\n    pub current_host: Option<OpaqueElement>,\n\n    /// Controls how matching for links is handled.\n    visited_handling: VisitedHandlingMode,\n\n    /// Whether we're currently matching a featureless element.\n    pub featureless: bool,\n\n    /// The current nesting level of selectors that we're matching.\n    nesting_level: usize,\n\n    /// Whether we're inside a negation or not.\n    in_negation: bool,\n\n    /// An optional hook function for checking whether a pseudo-element\n    /// should match when matching_mode is ForStatelessPseudoElement.\n    pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>,\n\n    /// Extra implementation-dependent matching data.\n    pub extra_data: Impl::ExtraMatchingData<'a>,\n\n    /// The current element we're anchoring on for evaluating the relative selector.\n    current_relative_selector_anchor: Option<OpaqueElement>,\n\n    quirks_mode: QuirksMode,\n    needs_selector_flags: NeedsSelectorFlags,\n\n    /// Whether we're matching in the context of invalidation.\n    matching_for_invalidation: MatchingForInvalidation,\n\n    /// Whether we're matching in the context of revalidation.\n    matching_for_revalidation: bool,\n\n    /// Caches to speed up expensive selector matches.\n    pub selector_caches: &'a mut SelectorCaches,\n\n    classes_and_ids_case_sensitivity: CaseSensitivity,\n    _impl: ::std::marker::PhantomData<Impl>,\n}\n\nimpl<'a, Impl> MatchingContext<'a, Impl>\nwhere\n    Impl: SelectorImpl,\n{\n    /// Constructs a new `MatchingContext`.\n    pub fn new(\n        matching_mode: MatchingMode,\n        bloom_filter: Option<&'a BloomFilter>,\n        selector_caches: &'a mut SelectorCaches,\n        quirks_mode: QuirksMode,\n        needs_selector_flags: NeedsSelectorFlags,\n        matching_for_invalidation: MatchingForInvalidation,\n    ) -> Self {\n        Self::new_internal(\n            matching_mode,\n            bloom_filter,\n            selector_caches,\n            VisitedHandlingMode::AllLinksUnvisited,\n            quirks_mode,\n            needs_selector_flags,\n            matching_for_invalidation,\n            false,\n        )\n    }\n\n    /// Constructs a new `MatchingContext` for revalidation.\n    pub fn new_for_revalidation(\n        bloom_filter: Option<&'a BloomFilter>,\n        selector_caches: &'a mut SelectorCaches,\n        quirks_mode: QuirksMode,\n        needs_selector_flags: NeedsSelectorFlags,\n    ) -> Self {\n        Self::new_internal(\n            // NB: `MatchingMode` doesn't really matter, given we don't share style\n            // between pseudos.\n            MatchingMode::Normal,\n            bloom_filter,\n            selector_caches,\n            VisitedHandlingMode::AllLinksUnvisited,\n            quirks_mode,\n            needs_selector_flags,\n            MatchingForInvalidation::No,\n            true,\n        )\n    }\n\n    /// Constructs a new `MatchingContext` for use in visited matching.\n    pub fn new_for_visited(\n        matching_mode: MatchingMode,\n        bloom_filter: Option<&'a BloomFilter>,\n        selector_caches: &'a mut SelectorCaches,\n        visited_handling: VisitedHandlingMode,\n        quirks_mode: QuirksMode,\n        needs_selector_flags: NeedsSelectorFlags,\n        matching_for_invalidation: MatchingForInvalidation,\n    ) -> Self {\n        Self::new_internal(\n            matching_mode,\n            bloom_filter,\n            selector_caches,\n            visited_handling,\n            quirks_mode,\n            needs_selector_flags,\n            matching_for_invalidation,\n            false,\n        )\n    }\n\n    fn new_internal(\n        matching_mode: MatchingMode,\n        bloom_filter: Option<&'a BloomFilter>,\n        selector_caches: &'a mut SelectorCaches,\n        visited_handling: VisitedHandlingMode,\n        quirks_mode: QuirksMode,\n        needs_selector_flags: NeedsSelectorFlags,\n        matching_for_invalidation: MatchingForInvalidation,\n        matching_for_revalidation: bool,\n    ) -> Self {\n        Self {\n            matching_mode,\n            bloom_filter,\n            visited_handling,\n            quirks_mode,\n            classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),\n            needs_selector_flags,\n            matching_for_invalidation,\n            matching_for_revalidation,\n            scope_element: None,\n            current_host: None,\n            featureless: false,\n            nesting_level: 0,\n            in_negation: false,\n            pseudo_element_matching_fn: None,\n            extra_data: Default::default(),\n            current_relative_selector_anchor: None,\n            selector_caches,\n            _impl: ::std::marker::PhantomData,\n        }\n    }\n\n    // Grab a reference to the appropriate cache.\n    #[inline]\n    pub fn nth_index_cache(\n        &mut self,\n        is_of_type: bool,\n        is_from_end: bool,\n        selectors: &[Selector<Impl>],\n    ) -> &mut NthIndexCacheInner {\n        self.selector_caches\n            .nth_index\n            .get(is_of_type, is_from_end, selectors)\n    }\n\n    /// Whether we're matching a nested selector.\n    #[inline]\n    pub fn is_nested(&self) -> bool {\n        self.nesting_level != 0\n    }\n\n    /// Whether we're matching inside a :not(..) selector.\n    #[inline]\n    pub fn in_negation(&self) -> bool {\n        self.in_negation\n    }\n\n    /// The quirks mode of the document.\n    #[inline]\n    pub fn quirks_mode(&self) -> QuirksMode {\n        self.quirks_mode\n    }\n\n    /// The matching-mode for this selector-matching operation.\n    #[inline]\n    pub fn matching_mode(&self) -> MatchingMode {\n        self.matching_mode\n    }\n\n    /// Whether we need to set selector flags.\n    #[inline]\n    pub fn needs_selector_flags(&self) -> bool {\n        self.needs_selector_flags == NeedsSelectorFlags::Yes\n    }\n\n    /// Whether or not we're matching to invalidate.\n    #[inline]\n    pub fn matching_for_invalidation(&self) -> bool {\n        self.matching_for_invalidation.is_for_invalidation()\n    }\n\n    /// Whether or not we're matching to revalidate.\n    #[inline]\n    pub fn matching_for_revalidation(&self) -> bool {\n        self.matching_for_revalidation\n    }\n\n    /// Whether or not we're comparing for invalidation, if we are matching for invalidation.\n    #[inline]\n    pub fn matching_for_invalidation_comparison(&self) -> Option<bool> {\n        match self.matching_for_invalidation {\n            MatchingForInvalidation::No => None,\n            MatchingForInvalidation::Yes => Some(false),\n            MatchingForInvalidation::YesForComparison => Some(true),\n        }\n    }\n\n    /// Run the given matching function for before/after invalidation comparison.\n    #[inline]\n    pub fn for_invalidation_comparison<F, R>(&mut self, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        debug_assert!(\n            self.matching_for_invalidation(),\n            \"Not matching for invalidation?\"\n        );\n        let prev = self.matching_for_invalidation;\n        self.matching_for_invalidation = MatchingForInvalidation::YesForComparison;\n        let result = f(self);\n        self.matching_for_invalidation = prev;\n        result\n    }\n\n    /// The case-sensitivity for class and ID selectors\n    #[inline]\n    pub fn classes_and_ids_case_sensitivity(&self) -> CaseSensitivity {\n        self.classes_and_ids_case_sensitivity\n    }\n\n    /// Runs F with a deeper nesting level.\n    #[inline]\n    pub fn nest<F, R>(&mut self, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        self.nesting_level += 1;\n        let result = f(self);\n        self.nesting_level -= 1;\n        result\n    }\n\n    /// Runs F with a deeper nesting level, and marking ourselves in a negation,\n    /// for a :not(..) selector, for example.\n    #[inline]\n    pub fn nest_for_negation<F, R>(&mut self, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let old_in_negation = self.in_negation;\n        self.in_negation = !self.in_negation;\n        let result = self.nest(f);\n        self.in_negation = old_in_negation;\n        result\n    }\n\n    #[inline]\n    pub fn visited_handling(&self) -> VisitedHandlingMode {\n        self.visited_handling\n    }\n\n    /// Runs F with a different featureless element flag.\n    #[inline]\n    pub fn with_featureless<F, R>(&mut self, featureless: bool, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let orig = self.featureless;\n        self.featureless = featureless;\n        let result = f(self);\n        self.featureless = orig;\n        result\n    }\n\n    /// Returns whether the currently matching element is acting as a featureless element (e.g.,\n    /// because we've crossed a shadow boundary). This is used to implement the :host selector\n    /// rules properly.\n    #[inline]\n    pub fn featureless(&self) -> bool {\n        self.featureless\n    }\n\n    /// Runs F with a different VisitedHandlingMode.\n    #[inline]\n    pub fn with_visited_handling_mode<F, R>(\n        &mut self,\n        handling_mode: VisitedHandlingMode,\n        f: F,\n    ) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let original_handling_mode = self.visited_handling;\n        self.visited_handling = handling_mode;\n        let result = f(self);\n        self.visited_handling = original_handling_mode;\n        result\n    }\n\n    /// Runs F with a given shadow host which is the root of the tree whose\n    /// rules we're matching.\n    #[inline]\n    pub fn with_shadow_host<F, E, R>(&mut self, host: Option<E>, f: F) -> R\n    where\n        E: Element,\n        F: FnOnce(&mut Self) -> R,\n    {\n        let original_host = self.current_host.take();\n        self.current_host = host.map(|h| h.opaque());\n        let result = f(self);\n        self.current_host = original_host;\n        result\n    }\n\n    /// Returns the current shadow host whose shadow root we're matching rules\n    /// against.\n    #[inline]\n    pub fn shadow_host(&self) -> Option<OpaqueElement> {\n        self.current_host\n    }\n\n    /// Runs F with a deeper nesting level, with the given element as the anchor,\n    /// for a :has(...) selector, for example.\n    #[inline]\n    pub fn nest_for_relative_selector<F, R>(&mut self, anchor: OpaqueElement, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        debug_assert!(\n            self.current_relative_selector_anchor.is_none(),\n            \"Nesting should've been rejected at parse time\"\n        );\n        self.current_relative_selector_anchor = Some(anchor);\n        let result = self.nest(f);\n        self.current_relative_selector_anchor = None;\n        result\n    }\n\n    /// Runs F with a deeper nesting level, with the given element as the scope.\n    #[inline]\n    pub fn nest_for_scope<F, R>(&mut self, scope: Option<OpaqueElement>, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let original_scope_element = self.scope_element;\n        self.scope_element = scope;\n        let result = f(self);\n        self.scope_element = original_scope_element;\n        result\n    }\n\n    /// Runs F with a deeper nesting level, with the given element as the scope, for\n    /// matching `scope-start` and/or `scope-end` conditions.\n    #[inline]\n    pub fn nest_for_scope_condition<F, R>(&mut self, scope: Option<OpaqueElement>, f: F) -> R\n    where\n        F: FnOnce(&mut Self) -> R,\n    {\n        let original_matching_mode = self.matching_mode;\n        // We may as well be matching for a pseudo-element inside `@scope`, but\n        // the scope-defining selectors wouldn't be matching them.\n        self.matching_mode = MatchingMode::Normal;\n        let result = self.nest_for_scope(scope, f);\n        self.matching_mode = original_matching_mode;\n        result\n    }\n\n    /// Returns the current anchor element to evaluate the relative selector against.\n    #[inline]\n    pub fn relative_selector_anchor(&self) -> Option<OpaqueElement> {\n        self.current_relative_selector_anchor\n    }\n}\n"
  },
  {
    "path": "selectors/kleene_value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Kleen logic: https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics\n\n/// A \"trilean\" value based on Kleen logic.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum KleeneValue {\n    /// False\n    False = 0,\n    /// True\n    True = 1,\n    /// Either true or false, but we’re not sure which yet.\n    Unknown,\n}\n\nimpl From<bool> for KleeneValue {\n    fn from(b: bool) -> Self {\n        if b {\n            Self::True\n        } else {\n            Self::False\n        }\n    }\n}\n\nimpl KleeneValue {\n    /// Turns this Kleene value to a bool, taking the unknown value as an\n    /// argument.\n    pub fn to_bool(self, unknown: bool) -> bool {\n        match self {\n            Self::True => true,\n            Self::False => false,\n            Self::Unknown => unknown,\n        }\n    }\n\n    /// Return true if any result of f() is definitely true.\n    /// Otherwise, return the `or` of all values.\n    /// Returns false if empty, like that of `Iterator`.\n    #[inline(always)]\n    pub fn any<T>(iter: impl Iterator<Item = T>, f: impl FnMut(T) -> Self) -> Self {\n        Self::any_value(iter, Self::True, Self::False, |a, b| a | b, f)\n    }\n\n    /// Return false if any results of f() is definitely false.\n    /// Otherwise, return the `and` of all values.\n    /// Returns true if empty, opposite of `Iterator`.\n    #[inline(always)]\n    pub fn any_false<T>(iter: impl Iterator<Item = T>, f: impl FnMut(T) -> Self) -> Self {\n        Self::any_value(iter, Self::False, Self::True, |a, b| a & b, f)\n    }\n\n    #[inline(always)]\n    fn any_value<T>(\n        iter: impl Iterator<Item = T>,\n        value: Self,\n        on_empty: Self,\n        op: impl Fn(Self, Self) -> Self,\n        mut f: impl FnMut(T) -> Self,\n    ) -> Self {\n        let mut result = None;\n        for item in iter {\n            let r = f(item);\n            if r == value {\n                return r;\n            }\n            if let Some(v) = result.as_mut() {\n                *v = op(*v, r);\n            } else {\n                result = Some(r);\n            }\n        }\n        result.unwrap_or(on_empty)\n    }\n}\n\nimpl std::ops::Not for KleeneValue {\n    type Output = Self;\n\n    fn not(self) -> Self {\n        match self {\n            Self::True => Self::False,\n            Self::False => Self::True,\n            Self::Unknown => Self::Unknown,\n        }\n    }\n}\n\n// Implements the logical and operation.\nimpl std::ops::BitAnd for KleeneValue {\n    type Output = Self;\n\n    fn bitand(self, other: Self) -> Self {\n        if self == Self::False || other == Self::False {\n            return Self::False;\n        }\n        if self == Self::Unknown || other == Self::Unknown {\n            return Self::Unknown;\n        }\n        Self::True\n    }\n}\n\n// Implements the logical or operation.\nimpl std::ops::BitOr for KleeneValue {\n    type Output = Self;\n\n    fn bitor(self, other: Self) -> Self {\n        if self == Self::True || other == Self::True {\n            return Self::True;\n        }\n        if self == Self::Unknown || other == Self::Unknown {\n            return Self::Unknown;\n        }\n        Self::False\n    }\n}\n\nimpl std::ops::BitOrAssign for KleeneValue {\n    fn bitor_assign(&mut self, other: Self) {\n        *self = *self | other;\n    }\n}\n\nimpl std::ops::BitAndAssign for KleeneValue {\n    fn bitand_assign(&mut self, other: Self) {\n        *self = *self & other;\n    }\n}\n"
  },
  {
    "path": "selectors/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n// Make |cargo bench| work.\n#![cfg_attr(feature = \"bench\", feature(test))]\n\npub mod attr;\npub mod bloom;\nmod builder;\npub mod context;\npub mod kleene_value;\npub mod matching;\nmod nth_index_cache;\npub mod parser;\npub mod relative_selector;\npub mod sink;\nmod tree;\npub mod visitor;\n\npub use crate::nth_index_cache::NthIndexCache;\npub use crate::parser::{Parser, SelectorImpl, SelectorList};\npub use crate::tree::{Element, OpaqueElement};\n"
  },
  {
    "path": "selectors/matching.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::attr::{\n    AttrSelectorOperation, AttrSelectorWithOptionalNamespace, CaseSensitivity, NamespaceConstraint,\n    ParsedAttrSelectorOperation, ParsedCaseSensitivity,\n};\nuse crate::bloom::{BloomFilter, BLOOM_HASH_MASK};\nuse crate::kleene_value::KleeneValue;\nuse crate::parser::{\n    AncestorHashes, Combinator, Component, LocalName, MatchesFeaturelessHost, NthSelectorData,\n    RelativeSelectorMatchHint,\n};\nuse crate::parser::{\n    NonTSPseudoClass, RelativeSelector, Selector, SelectorImpl, SelectorIter, SelectorList,\n};\nuse crate::relative_selector::cache::RelativeSelectorCachedMatch;\nuse crate::tree::Element;\nuse bitflags::bitflags;\nuse debug_unreachable::debug_unreachable;\nuse log::debug;\nuse smallvec::SmallVec;\nuse std::borrow::Borrow;\n\npub use crate::context::*;\n\n// The bloom filter for descendant CSS selectors will have a <1% false\n// positive rate until it has this many selectors in it, then it will\n// rapidly increase.\npub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;\n\nbitflags! {\n    /// Set of flags that are set on either the element or its parent (depending\n    /// on the flag) if the element could potentially match a selector.\n    #[derive(Clone, Copy)]\n    pub struct ElementSelectorFlags: usize {\n        /// When a child is added or removed from the parent, all the children\n        /// must be restyled, because they may match :nth-last-child,\n        /// :last-of-type, :nth-last-of-type, or :only-of-type.\n        const HAS_SLOW_SELECTOR = 1 << 0;\n\n        /// When a child is added or removed from the parent, any later\n        /// children must be restyled, because they may match :nth-child,\n        /// :first-of-type, or :nth-of-type.\n        const HAS_SLOW_SELECTOR_LATER_SIBLINGS = 1 << 1;\n\n        /// HAS_SLOW_SELECTOR* was set by the presence of :nth (But not of).\n        const HAS_SLOW_SELECTOR_NTH = 1 << 2;\n\n        /// When a DOM mutation occurs on a child that might be matched by\n        /// :nth-last-child(.. of <selector list>), earlier children must be\n        /// restyled, and HAS_SLOW_SELECTOR will be set (which normally\n        /// indicates that all children will be restyled).\n        ///\n        /// Similarly, when a DOM mutation occurs on a child that might be\n        /// matched by :nth-child(.. of <selector list>), later children must be\n        /// restyled, and HAS_SLOW_SELECTOR_LATER_SIBLINGS will be set.\n        const HAS_SLOW_SELECTOR_NTH_OF = 1 << 3;\n\n        /// When a child is added or removed from the parent, the first and\n        /// last children must be restyled, because they may match :first-child,\n        /// :last-child, or :only-child.\n        const HAS_EDGE_CHILD_SELECTOR = 1 << 4;\n\n        /// The element has an empty selector, so when a child is appended we\n        /// might need to restyle the parent completely.\n        const HAS_EMPTY_SELECTOR = 1 << 5;\n\n        /// The element may anchor a relative selector.\n        const ANCHORS_RELATIVE_SELECTOR = 1 << 6;\n\n        /// The element may anchor a relative selector that is not the subject\n        /// of the whole selector.\n        const ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT = 1 << 7;\n\n        /// The element is reached by a relative selector search in the sibling direction.\n        const RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING = 1 << 8;\n\n        /// The element is reached by a relative selector search in the ancestor direction.\n        const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR = 1 << 9;\n\n        // The element is reached by a relative selector search in both sibling and ancestor directions.\n        const RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING =\n            Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING.bits() |\n            Self::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR.bits();\n    }\n}\n\nimpl ElementSelectorFlags {\n    /// Returns the subset of flags that apply to the element.\n    pub fn for_self(self) -> ElementSelectorFlags {\n        self & (ElementSelectorFlags::HAS_EMPTY_SELECTOR\n            | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR\n            | ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT\n            | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING\n            | ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)\n    }\n\n    /// Returns the subset of flags that apply to the parent.\n    pub fn for_parent(self) -> ElementSelectorFlags {\n        self & (ElementSelectorFlags::HAS_SLOW_SELECTOR\n            | ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS\n            | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH\n            | ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF\n            | ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR)\n    }\n}\n\n/// Holds per-compound-selector data.\nstruct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {\n    shared: &'a mut MatchingContext<'b, Impl>,\n    rightmost: SubjectOrPseudoElement,\n    quirks_data: Option<SelectorIter<'a, Impl>>,\n}\n\n#[inline(always)]\npub fn matches_selector_list<E>(\n    selector_list: &SelectorList<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n) -> bool\nwhere\n    E: Element,\n{\n    // This is pretty much any(..) but manually inlined because the compiler\n    // refuses to do so from querySelector / querySelectorAll.\n    for selector in selector_list.slice() {\n        let matches = matches_selector(selector, 0, None, element, context);\n        if matches {\n            return true;\n        }\n    }\n\n    false\n}\n\n/// Given the ancestor hashes from a selector, see if the current element,\n/// represented by the bloom filter, has a chance of matching at all.\n#[inline(always)]\npub fn selector_may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool {\n    // Check the first three hashes. Note that we can check for zero before\n    // masking off the high bits, since if any of the first three hashes is\n    // zero the fourth will be as well. We also take care to avoid the\n    // special-case complexity of the fourth hash until we actually reach it,\n    // because we usually don't.\n    //\n    // To be clear: this is all extremely hot.\n    for i in 0..3 {\n        let packed = hashes.packed_hashes[i];\n        if packed == 0 {\n            // No more hashes left - unable to fast-reject.\n            return true;\n        }\n\n        if !bf.might_contain_hash(packed & BLOOM_HASH_MASK) {\n            // Hooray! We fast-rejected on this hash.\n            return false;\n        }\n    }\n\n    // Now do the slighty-more-complex work of synthesizing the fourth hash,\n    // and check it against the filter if it exists.\n    let fourth = hashes.fourth_hash();\n    fourth == 0 || bf.might_contain_hash(fourth)\n}\n\n/// A result of selector matching, includes 3 failure types,\n///\n///   NotMatchedAndRestartFromClosestLaterSibling\n///   NotMatchedAndRestartFromClosestDescendant\n///   NotMatchedGlobally\n///\n/// When NotMatchedGlobally appears, stop selector matching completely since\n/// the succeeding selectors never matches.\n/// It is raised when\n///   Child combinator cannot find the candidate element.\n///   Descendant combinator cannot find the candidate element.\n///\n/// When NotMatchedAndRestartFromClosestDescendant appears, the selector\n/// matching does backtracking and restarts from the closest Descendant\n/// combinator.\n/// It is raised when\n///   NextSibling combinator cannot find the candidate element.\n///   LaterSibling combinator cannot find the candidate element.\n///   Child combinator doesn't match on the found element.\n///\n/// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector\n/// matching does backtracking and restarts from the closest LaterSibling\n/// combinator.\n/// It is raised when\n///   NextSibling combinator doesn't match on the found element.\n///\n/// For example, when the selector \"d1 d2 a\" is provided and we cannot *find*\n/// an appropriate ancestor element for \"d1\", this selector matching raises\n/// NotMatchedGlobally since even if \"d2\" is moved to more upper element, the\n/// candidates for \"d1\" becomes less than before and d1 .\n///\n/// The next example is siblings. When the selector \"b1 + b2 ~ d1 a\" is\n/// provided and we cannot *find* an appropriate brother element for b1,\n/// the selector matching raises NotMatchedAndRestartFromClosestDescendant.\n/// The selectors (\"b1 + b2 ~\") doesn't match and matching restart from \"d1\".\n///\n/// The additional example is child and sibling. When the selector\n/// \"b1 + c1 > b2 ~ d1 a\" is provided and the selector \"b1\" doesn't match on\n/// the element, this \"b1\" raises NotMatchedAndRestartFromClosestLaterSibling.\n/// However since the selector \"c1\" raises\n/// NotMatchedAndRestartFromClosestDescendant. So the selector\n/// \"b1 + c1 > b2 ~ \" doesn't match and restart matching from \"d1\".\n///\n/// There is also the unknown result, which is used during invalidation when\n/// specific selector is being tested for before/after comparison. More specifically,\n/// selectors that are too expensive to correctly compute during invalidation may\n/// return unknown, as the computation will be thrown away and only to be recomputed\n/// during styling. For most cases, the unknown result can be treated as matching.\n/// This is because a compound of selectors acts like &&, and unknown && matched\n/// == matched and unknown && not-matched == not-matched. However, some selectors,\n/// like `:is()`, behave like || i.e. `:is(.a, .b)` == a || b. Treating unknown\n/// == matching then causes these selectors to always return matching, which undesired\n/// for before/after comparison. Coercing to not-matched doesn't work since each\n/// inner selector may have compounds: e.g. Toggling `.foo` in `:is(.foo:has(..))`\n/// with coersion to not-matched would result in an invalid before/after comparison\n/// of not-matched/not-matched.\n#[derive(Clone, Copy, Eq, PartialEq)]\nenum SelectorMatchingResult {\n    Matched,\n    NotMatchedAndRestartFromClosestLaterSibling,\n    NotMatchedAndRestartFromClosestDescendant,\n    NotMatchedGlobally,\n    Unknown,\n}\n\nimpl From<SelectorMatchingResult> for KleeneValue {\n    #[inline]\n    fn from(value: SelectorMatchingResult) -> Self {\n        match value {\n            SelectorMatchingResult::Matched => KleeneValue::True,\n            SelectorMatchingResult::Unknown => KleeneValue::Unknown,\n            SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling\n            | SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant\n            | SelectorMatchingResult::NotMatchedGlobally => KleeneValue::False,\n        }\n    }\n}\n\n/// Matches a selector, fast-rejecting against a bloom filter.\n///\n/// We accept an offset to allow consumers to represent and match against\n/// partial selectors (indexed from the right). We use this API design, rather\n/// than having the callers pass a SelectorIter, because creating a SelectorIter\n/// requires dereferencing the selector to get the length, which adds an\n/// unnecessary cache miss for cases when we can fast-reject with AncestorHashes\n/// (which the caller can store inline with the selector pointer).\n#[inline(always)]\npub fn matches_selector<E>(\n    selector: &Selector<E::Impl>,\n    offset: usize,\n    hashes: Option<&AncestorHashes>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n) -> bool\nwhere\n    E: Element,\n{\n    let result = matches_selector_kleene(selector, offset, hashes, element, context);\n    if cfg!(debug_assertions) && result == KleeneValue::Unknown {\n        debug_assert!(\n            context\n                .matching_for_invalidation_comparison()\n                .unwrap_or(false),\n            \"How did we return unknown?\"\n        );\n    }\n    result.to_bool(true)\n}\n\n/// Same as matches_selector, but returns the Kleene value as-is.\n#[inline(always)]\npub fn matches_selector_kleene<E>(\n    selector: &Selector<E::Impl>,\n    offset: usize,\n    hashes: Option<&AncestorHashes>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    // Use the bloom filter to fast-reject.\n    if let Some(hashes) = hashes {\n        if let Some(filter) = context.bloom_filter {\n            if !selector_may_match(hashes, filter) {\n                return KleeneValue::False;\n            }\n        }\n    }\n    matches_complex_selector(\n        selector.iter_from(offset),\n        element,\n        context,\n        if selector.is_rightmost(offset) {\n            SubjectOrPseudoElement::Yes\n        } else {\n            SubjectOrPseudoElement::No\n        },\n    )\n}\n\n/// Whether a compound selector matched, and whether it was the rightmost\n/// selector inside the complex selector.\npub enum CompoundSelectorMatchingResult {\n    /// The selector was fully matched.\n    FullyMatched,\n    /// The compound selector matched, and the next combinator offset is\n    /// `next_combinator_offset`.\n    Matched { next_combinator_offset: usize },\n    /// The selector didn't match.\n    NotMatched,\n}\n\nfn complex_selector_early_reject_by_local_name<E: Element>(\n    list: &SelectorList<E::Impl>,\n    element: &E,\n) -> bool {\n    list.slice()\n        .iter()\n        .all(|s| early_reject_by_local_name(s, 0, element))\n}\n\n/// Returns true if this compound would not match the given element by due\n/// to a local name selector (If one exists).\npub fn early_reject_by_local_name<E: Element>(\n    selector: &Selector<E::Impl>,\n    from_offset: usize,\n    element: &E,\n) -> bool {\n    let iter = selector.iter_from(from_offset);\n    for component in iter {\n        if match component {\n            Component::LocalName(name) => !matches_local_name(element, name),\n            Component::Is(list) | Component::Where(list) => {\n                complex_selector_early_reject_by_local_name(list, element)\n            },\n            _ => continue,\n        } {\n            return true;\n        }\n    }\n    false\n}\n\n/// Matches a compound selector belonging to `selector`, starting at offset\n/// `from_offset`, matching left to right.\n///\n/// Requires that `from_offset` points to a `Combinator`.\n///\n/// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the\n/// complex selector, but it happens to be the case we don't need it.\npub fn matches_compound_selector_from<E>(\n    selector: &Selector<E::Impl>,\n    mut from_offset: usize,\n    context: &mut MatchingContext<E::Impl>,\n    element: &E,\n) -> CompoundSelectorMatchingResult\nwhere\n    E: Element,\n{\n    debug_assert!(\n        !context\n            .matching_for_invalidation_comparison()\n            .unwrap_or(false),\n        \"CompoundSelectorMatchingResult doesn't support unknown\"\n    );\n    if cfg!(debug_assertions) && from_offset != 0 {\n        selector.combinator_at_parse_order(from_offset - 1); // This asserts.\n    }\n\n    let mut local_context = LocalMatchingContext {\n        shared: context,\n        // We have no info if this is an outer selector. This function is called in\n        // an invalidation context, which only calls this for non-subject (i.e.\n        // Non-rightmost) positions.\n        rightmost: SubjectOrPseudoElement::No,\n        quirks_data: None,\n    };\n\n    // Find the end of the selector or the next combinator, then match\n    // backwards, so that we match in the same order as\n    // matches_complex_selector, which is usually faster.\n    let start_offset = from_offset;\n    for component in selector.iter_raw_parse_order_from(from_offset) {\n        if matches!(*component, Component::Combinator(..)) {\n            debug_assert_ne!(from_offset, 0, \"Selector started with a combinator?\");\n            break;\n        }\n\n        from_offset += 1;\n    }\n\n    debug_assert!(from_offset >= 1);\n    debug_assert!(from_offset <= selector.len());\n\n    let iter = selector.iter_from(selector.len() - from_offset);\n    debug_assert!(\n        iter.clone().next().is_some() || from_offset != selector.len(),\n        \"Got the math wrong: {:?} | {:?} | {} {}\",\n        selector,\n        selector.iter_raw_match_order().as_slice(),\n        from_offset,\n        start_offset\n    );\n\n    debug_assert!(\n        !local_context.shared.featureless(),\n        \"Invalidating featureless element somehow?\"\n    );\n\n    for component in iter {\n        let result = matches_simple_selector(component, element, &mut local_context);\n        debug_assert!(\n            result != KleeneValue::Unknown,\n            \"Returned unknown in non invalidation context?\"\n        );\n        if !result.to_bool(true) {\n            return CompoundSelectorMatchingResult::NotMatched;\n        }\n    }\n\n    if from_offset != selector.len() {\n        return CompoundSelectorMatchingResult::Matched {\n            next_combinator_offset: from_offset,\n        };\n    }\n\n    CompoundSelectorMatchingResult::FullyMatched\n}\n\n/// Matches a complex selector.\n#[inline(always)]\nfn matches_complex_selector<E>(\n    mut iter: SelectorIter<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    // If this is the special pseudo-element mode, consume the ::pseudo-element\n    // before proceeding, since the caller has already handled that part.\n    if context.matching_mode() == MatchingMode::ForStatelessPseudoElement && !context.is_nested() {\n        // Consume the pseudo.\n        match *iter.next().unwrap() {\n            Component::PseudoElement(ref pseudo) => {\n                if let Some(ref f) = context.pseudo_element_matching_fn {\n                    if !f(pseudo) {\n                        return KleeneValue::False;\n                    }\n                }\n            },\n            ref other => {\n                debug_assert!(\n                    false,\n                    \"Used MatchingMode::ForStatelessPseudoElement \\\n                     in a non-pseudo selector {:?}\",\n                    other\n                );\n                return KleeneValue::False;\n            },\n        }\n\n        if !iter.matches_for_stateless_pseudo_element() {\n            return KleeneValue::False;\n        }\n\n        // Advance to the non-pseudo-element part of the selector.\n        let next_sequence = iter.next_sequence().unwrap();\n        debug_assert_eq!(next_sequence, Combinator::PseudoElement);\n    }\n\n    matches_complex_selector_internal(\n        iter,\n        element,\n        context,\n        rightmost,\n        SubjectOrPseudoElement::Yes,\n    )\n    .into()\n}\n\n/// Matches each selector of a list as a complex selector\nfn matches_complex_selector_list<E: Element>(\n    list: &[Selector<E::Impl>],\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue {\n    KleeneValue::any(list.iter(), |selector| {\n        matches_complex_selector(selector.iter(), element, context, rightmost)\n    })\n}\n\nfn matches_relative_selector<E: Element>(\n    relative_selector: &RelativeSelector<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> bool {\n    // Overall, we want to mark the path that we've traversed so that when an element\n    // is invalidated, we early-reject unnecessary relative selector invalidations.\n    if relative_selector.match_hint.is_descendant_direction() {\n        if context.needs_selector_flags() {\n            element.apply_selector_flags(\n                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n            );\n        }\n        let mut next_element = element.first_element_child();\n        while let Some(el) = next_element {\n            if context.needs_selector_flags() {\n                el.apply_selector_flags(\n                    ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n                );\n            }\n            let mut matched = matches_complex_selector(\n                relative_selector.selector.iter(),\n                &el,\n                context,\n                rightmost,\n            )\n            .to_bool(true);\n            if !matched && relative_selector.match_hint.is_subtree() {\n                matched = matches_relative_selector_subtree(\n                    &relative_selector.selector,\n                    &el,\n                    context,\n                    rightmost,\n                );\n            }\n            if matched {\n                return true;\n            }\n            next_element = el.next_sibling_element();\n        }\n    } else {\n        debug_assert!(\n            matches!(\n                relative_selector.match_hint,\n                RelativeSelectorMatchHint::InNextSibling\n                    | RelativeSelectorMatchHint::InNextSiblingSubtree\n                    | RelativeSelectorMatchHint::InSibling\n                    | RelativeSelectorMatchHint::InSiblingSubtree\n            ),\n            \"Not descendant direction, but also not sibling direction?\"\n        );\n        if context.needs_selector_flags() {\n            element.apply_selector_flags(\n                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,\n            );\n        }\n        let sibling_flag = if relative_selector.match_hint.is_subtree() {\n            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING\n        } else {\n            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING\n        };\n        let mut next_element = element.next_sibling_element();\n        while let Some(el) = next_element {\n            if context.needs_selector_flags() {\n                el.apply_selector_flags(sibling_flag);\n            }\n            let matched = if relative_selector.match_hint.is_subtree() {\n                matches_relative_selector_subtree(\n                    &relative_selector.selector,\n                    &el,\n                    context,\n                    rightmost,\n                )\n            } else {\n                matches_complex_selector(relative_selector.selector.iter(), &el, context, rightmost)\n                    .to_bool(true)\n            };\n            if matched {\n                return true;\n            }\n            if relative_selector.match_hint.is_next_sibling() {\n                break;\n            }\n            next_element = el.next_sibling_element();\n        }\n    }\n    return false;\n}\n\nfn relative_selector_match_early<E: Element>(\n    selector: &RelativeSelector<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n) -> Option<bool> {\n    // See if we can return a cached result.\n    if let Some(cached) = context\n        .selector_caches\n        .relative_selector\n        .lookup(element.opaque(), selector)\n    {\n        return Some(cached.matched());\n    }\n    // See if we can fast-reject.\n    if context\n        .selector_caches\n        .relative_selector_filter_map\n        .fast_reject(element, selector, context.quirks_mode())\n    {\n        // Alright, add as unmatched to cache.\n        context.selector_caches.relative_selector.add(\n            element.opaque(),\n            selector,\n            RelativeSelectorCachedMatch::NotMatched,\n        );\n        return Some(false);\n    }\n    None\n}\n\nfn match_relative_selectors<E: Element>(\n    selectors: &[RelativeSelector<E::Impl>],\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue {\n    if context.relative_selector_anchor().is_some() {\n        // FIXME(emilio): This currently can happen with nesting, and it's not fully\n        // correct, arguably. But the ideal solution isn't super-clear either. For now,\n        // cope with it and explicitly reject it at match time. See [1] for discussion.\n        //\n        // [1]: https://github.com/w3c/csswg-drafts/issues/9600\n        return KleeneValue::False;\n    }\n    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {\n        // In the context of invalidation, :has is expensive, especially because we\n        // can't use caching/filtering due to now/then matches. DOM structure also\n        // may have changed.\n        return if may_return_unknown {\n            KleeneValue::Unknown\n        } else {\n            KleeneValue::from(!context.in_negation())\n        };\n    }\n    context\n        .nest_for_relative_selector(element.opaque(), |context| {\n            do_match_relative_selectors(selectors, element, context, rightmost)\n        })\n        .into()\n}\n\n/// Matches a relative selector in a list of relative selectors.\nfn do_match_relative_selectors<E: Element>(\n    selectors: &[RelativeSelector<E::Impl>],\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> bool {\n    // Due to style sharing implications (See style sharing code), we mark the current styling context\n    // to mark elements considered for :has matching. Additionally, we want to mark the elements themselves,\n    // since we don't want to indiscriminately invalidate every element as a potential anchor.\n    if rightmost == SubjectOrPseudoElement::Yes {\n        if context.needs_selector_flags() {\n            element.apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR);\n        }\n    } else {\n        if context.needs_selector_flags() {\n            element\n                .apply_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT);\n        }\n    }\n\n    for relative_selector in selectors.iter() {\n        if let Some(result) = relative_selector_match_early(relative_selector, element, context) {\n            if result {\n                return true;\n            }\n            // Early return indicates no match, continue to next selector.\n            continue;\n        }\n\n        let matched = matches_relative_selector(relative_selector, element, context, rightmost);\n        context.selector_caches.relative_selector.add(\n            element.opaque(),\n            relative_selector,\n            if matched {\n                RelativeSelectorCachedMatch::Matched\n            } else {\n                RelativeSelectorCachedMatch::NotMatched\n            },\n        );\n        if matched {\n            return true;\n        }\n    }\n\n    false\n}\n\nfn matches_relative_selector_subtree<E: Element>(\n    selector: &Selector<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> bool {\n    let mut current = element.first_element_child();\n\n    while let Some(el) = current {\n        if context.needs_selector_flags() {\n            el.apply_selector_flags(\n                ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n            );\n        }\n        if matches_complex_selector(selector.iter(), &el, context, rightmost).to_bool(true) {\n            return true;\n        }\n\n        if matches_relative_selector_subtree(selector, &el, context, rightmost) {\n            return true;\n        }\n\n        current = el.next_sibling_element();\n    }\n\n    false\n}\n\n/// Whether the :hover and :active quirk applies.\n///\n/// https://quirks.spec.whatwg.org/#the-active-and-hover-quirk\nfn hover_and_active_quirk_applies<Impl: SelectorImpl>(\n    selector_iter: &SelectorIter<Impl>,\n    context: &MatchingContext<Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> bool {\n    debug_assert_eq!(context.quirks_mode(), QuirksMode::Quirks);\n\n    if context.is_nested() {\n        return false;\n    }\n\n    // This compound selector had a pseudo-element to the right that we\n    // intentionally skipped.\n    if rightmost == SubjectOrPseudoElement::Yes\n        && context.matching_mode() == MatchingMode::ForStatelessPseudoElement\n    {\n        return false;\n    }\n\n    selector_iter.clone().all(|simple| match *simple {\n        Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),\n        _ => false,\n    })\n}\n\n#[derive(Clone, Copy, PartialEq)]\nenum SubjectOrPseudoElement {\n    Yes,\n    No,\n}\n\nfn host_for_part<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>\nwhere\n    E: Element,\n{\n    let scope = context.current_host;\n    let mut curr = element.containing_shadow_host()?;\n    if scope == Some(curr.opaque()) {\n        return Some(curr);\n    }\n    loop {\n        let parent = curr.containing_shadow_host();\n        if parent.as_ref().map(|h| h.opaque()) == scope {\n            return Some(curr);\n        }\n        curr = parent?;\n    }\n}\n\nfn assigned_slot<E>(element: &E, context: &MatchingContext<E::Impl>) -> Option<E>\nwhere\n    E: Element,\n{\n    debug_assert!(element\n        .assigned_slot()\n        .map_or(true, |s| s.is_html_slot_element()));\n    let scope = context.current_host?;\n    let mut current_slot = element.assigned_slot()?;\n    while current_slot.containing_shadow_host().unwrap().opaque() != scope {\n        current_slot = current_slot.assigned_slot()?;\n    }\n    Some(current_slot)\n}\n\nstruct NextElement<E> {\n    next_element: Option<E>,\n    featureless: bool,\n}\n\nimpl<E> NextElement<E> {\n    #[inline(always)]\n    fn new(next_element: Option<E>, featureless: bool) -> Self {\n        Self {\n            next_element,\n            featureless,\n        }\n    }\n}\n\n#[inline(always)]\nfn next_element_for_combinator<E>(\n    element: &E,\n    combinator: Combinator,\n    context: &MatchingContext<E::Impl>,\n) -> NextElement<E>\nwhere\n    E: Element,\n{\n    match combinator {\n        Combinator::NextSibling | Combinator::LaterSibling => {\n            NextElement::new(element.prev_sibling_element(), false)\n        },\n        Combinator::Child | Combinator::Descendant => {\n            if let Some(parent) = element.parent_element() {\n                return NextElement::new(Some(parent), false);\n            }\n\n            let element = if element.parent_node_is_shadow_root() {\n                element.containing_shadow_host()\n            } else {\n                None\n            };\n            NextElement::new(element, true)\n        },\n        Combinator::Part => NextElement::new(host_for_part(element, context), false),\n        Combinator::SlotAssignment => NextElement::new(assigned_slot(element, context), false),\n        Combinator::PseudoElement => {\n            NextElement::new(element.pseudo_element_originating_element(), false)\n        },\n    }\n}\n\nfn matches_complex_selector_internal<E>(\n    mut selector_iter: SelectorIter<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    mut rightmost: SubjectOrPseudoElement,\n    mut first_subject_compound: SubjectOrPseudoElement,\n) -> SelectorMatchingResult\nwhere\n    E: Element,\n{\n    debug!(\n        \"Matching complex selector {:?} for {:?}\",\n        selector_iter, element\n    );\n\n    let matches_compound_selector =\n        matches_compound_selector(&mut selector_iter, element, context, rightmost);\n\n    let Some(combinator) = selector_iter.next_sequence() else {\n        return match matches_compound_selector {\n            KleeneValue::True => SelectorMatchingResult::Matched,\n            KleeneValue::Unknown => SelectorMatchingResult::Unknown,\n            KleeneValue::False => {\n                SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling\n            },\n        };\n    };\n\n    let is_pseudo_combinator = combinator.is_pseudo_element();\n    if context.featureless() && !is_pseudo_combinator {\n        // A featureless element shouldn't match any further combinator.\n        // TODO(emilio): Maybe we could avoid the compound matching more eagerly.\n        return SelectorMatchingResult::NotMatchedGlobally;\n    }\n\n    let is_sibling_combinator = combinator.is_sibling();\n    if is_sibling_combinator && context.needs_selector_flags() {\n        // We need the flags even if we don't match.\n        element.apply_selector_flags(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);\n    }\n\n    if matches_compound_selector == KleeneValue::False {\n        // We don't short circuit unknown here, since the rest of the selector\n        // to the left of this compound may still return false.\n        return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;\n    }\n\n    if !is_pseudo_combinator {\n        rightmost = SubjectOrPseudoElement::No;\n        first_subject_compound = SubjectOrPseudoElement::No;\n    }\n\n    // Stop matching :visited as soon as we find a link, or a combinator for\n    // something that isn't an ancestor.\n    let mut visited_handling = if is_sibling_combinator {\n        VisitedHandlingMode::AllLinksUnvisited\n    } else {\n        context.visited_handling()\n    };\n\n    let candidate_not_found = if is_sibling_combinator {\n        SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant\n    } else {\n        SelectorMatchingResult::NotMatchedGlobally\n    };\n\n    let mut element = element.clone();\n    loop {\n        if element.is_link() {\n            visited_handling = VisitedHandlingMode::AllLinksUnvisited;\n        }\n\n        let NextElement {\n            next_element,\n            featureless,\n        } = next_element_for_combinator(&element, combinator, &context);\n        element = match next_element {\n            None => return candidate_not_found,\n            Some(e) => e,\n        };\n\n        let result = context.with_visited_handling_mode(visited_handling, |context| {\n            context.with_featureless(featureless, |context| {\n                matches_complex_selector_internal(\n                    selector_iter.clone(),\n                    &element,\n                    context,\n                    rightmost,\n                    first_subject_compound,\n                )\n            })\n        });\n\n        // Return the status immediately if it is one of the global states.\n        match result {\n            SelectorMatchingResult::Matched => {\n                debug_assert!(\n                    matches_compound_selector.to_bool(true),\n                    \"Compound didn't match?\"\n                );\n                if !matches_compound_selector.to_bool(false) {\n                    return SelectorMatchingResult::Unknown;\n                }\n                return result;\n            },\n            SelectorMatchingResult::Unknown | SelectorMatchingResult::NotMatchedGlobally => {\n                return result\n            },\n            _ => {},\n        }\n\n        match combinator {\n            Combinator::Descendant => {\n                // The Descendant combinator and the status is\n                // NotMatchedAndRestartFromClosestLaterSibling or\n                // NotMatchedAndRestartFromClosestDescendant, or the LaterSibling combinator and\n                // the status is NotMatchedAndRestartFromClosestDescendant, we can continue to\n                // matching on the next candidate element.\n            },\n            Combinator::Child => {\n                // Upgrade the failure status to NotMatchedAndRestartFromClosestDescendant.\n                return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;\n            },\n            Combinator::LaterSibling => {\n                // If the failure status is NotMatchedAndRestartFromClosestDescendant and combinator is\n                // LaterSibling, give up this LaterSibling matching and restart from the closest\n                // descendant combinator.\n                if matches!(\n                    result,\n                    SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant\n                ) {\n                    return result;\n                }\n            },\n            Combinator::NextSibling\n            | Combinator::PseudoElement\n            | Combinator::Part\n            | Combinator::SlotAssignment => {\n                // NOTE(emilio): Conceptually, PseudoElement / Part / SlotAssignment should return\n                // `candidate_not_found`, but it doesn't matter in practice since they don't have\n                // sibling / descendant combinators to the right of them. This hopefully saves one\n                // branch.\n                return result;\n            },\n        }\n\n        if featureless {\n            // A featureless element didn't match the selector, we can stop matching now rather\n            // than looking at following elements for our combinator.\n            return candidate_not_found;\n        }\n    }\n}\n\n#[inline]\nfn matches_local_name<E>(element: &E, local_name: &LocalName<E::Impl>) -> bool\nwhere\n    E: Element,\n{\n    let name = select_name(element, &local_name.name, &local_name.lower_name).borrow();\n    element.has_local_name(name)\n}\n\nfn matches_part<E>(\n    element: &E,\n    parts: &[<E::Impl as SelectorImpl>::Identifier],\n    context: &mut MatchingContext<E::Impl>,\n) -> bool\nwhere\n    E: Element,\n{\n    let mut hosts = SmallVec::<[E; 4]>::new();\n\n    let mut host = match element.containing_shadow_host() {\n        Some(h) => h,\n        None => return false,\n    };\n\n    let current_host = context.current_host;\n    if current_host != Some(host.opaque()) {\n        loop {\n            let outer_host = host.containing_shadow_host();\n            if outer_host.as_ref().map(|h| h.opaque()) == current_host {\n                break;\n            }\n            let outer_host = match outer_host {\n                Some(h) => h,\n                None => return false,\n            };\n            // TODO(emilio): if worth it, we could early return if\n            // host doesn't have the exportparts attribute.\n            hosts.push(host);\n            host = outer_host;\n        }\n    }\n\n    // Translate the part into the right scope.\n    parts.iter().all(|part| {\n        let mut part = part.clone();\n        for host in hosts.iter().rev() {\n            part = match host.imported_part(&part) {\n                Some(p) => p,\n                None => return false,\n            };\n        }\n        element.is_part(&part)\n    })\n}\n\nfn matches_host<E>(\n    element: &E,\n    selector: Option<&Selector<E::Impl>>,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    let host = match context.shadow_host() {\n        Some(h) => h,\n        None => return KleeneValue::False,\n    };\n    if host != element.opaque() {\n        return KleeneValue::False;\n    }\n    let Some(selector) = selector else {\n        return KleeneValue::True;\n    };\n    context.nest(|context| {\n        context.with_featureless(false, |context| {\n            matches_complex_selector(selector.iter(), element, context, rightmost)\n        })\n    })\n}\n\nfn matches_slotted<E>(\n    element: &E,\n    selector: &Selector<E::Impl>,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    // <slots> are never flattened tree slottables.\n    if element.is_html_slot_element() {\n        return KleeneValue::False;\n    }\n    context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))\n}\n\nfn matches_rare_attribute_selector<E>(\n    element: &E,\n    attr_sel: &AttrSelectorWithOptionalNamespace<E::Impl>,\n) -> bool\nwhere\n    E: Element,\n{\n    let empty_string;\n    let namespace = match attr_sel.namespace() {\n        Some(ns) => ns,\n        None => {\n            empty_string = crate::parser::namespace_empty_string::<E::Impl>();\n            NamespaceConstraint::Specific(&empty_string)\n        },\n    };\n    element.attr_matches(\n        &namespace,\n        select_name(element, &attr_sel.local_name, &attr_sel.local_name_lower),\n        &match attr_sel.operation {\n            ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists,\n            ParsedAttrSelectorOperation::WithValue {\n                operator,\n                case_sensitivity,\n                ref value,\n            } => AttrSelectorOperation::WithValue {\n                operator,\n                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),\n                value,\n            },\n        },\n    )\n}\n\n/// There are relatively few selectors in a given compound that may match a featureless element.\n/// Instead of adding a check to every selector that may not match, we handle it here in an out of\n/// line path.\npub(crate) fn compound_matches_featureless_host<Impl: SelectorImpl>(\n    iter: &mut SelectorIter<Impl>,\n    scope_matches_featureless_host: bool,\n) -> MatchesFeaturelessHost {\n    let mut matches = MatchesFeaturelessHost::Only;\n    for component in iter {\n        match component {\n            Component::Scope | Component::ImplicitScope if scope_matches_featureless_host => {},\n            // :host only matches featureless elements.\n            Component::Host(..) => {},\n            // Pseudo-elements are allowed to match as well.\n            Component::PseudoElement(..) => {},\n            // We allow logical pseudo-classes, but we'll fail matching of the inner selectors if\n            // necessary.\n            Component::Is(ref l) | Component::Where(ref l) => {\n                let mut any_yes = false;\n                let mut any_no = false;\n                for selector in l.slice() {\n                    match selector.matches_featureless_host(scope_matches_featureless_host) {\n                        MatchesFeaturelessHost::Never => {\n                            any_no = true;\n                        },\n                        MatchesFeaturelessHost::Yes => {\n                            any_yes = true;\n                            any_no = true;\n                        },\n                        MatchesFeaturelessHost::Only => {\n                            any_yes = true;\n                        },\n                    }\n                }\n                if !any_yes {\n                    return MatchesFeaturelessHost::Never;\n                }\n                if any_no {\n                    // Potentially downgrade since we might match non-featureless elements too.\n                    matches = MatchesFeaturelessHost::Yes;\n                }\n            },\n            Component::Negation(ref l) => {\n                // For now preserving behavior, see\n                // https://github.com/w3c/csswg-drafts/issues/10179 for existing resolutions that\n                // tweak this behavior.\n                for selector in l.slice() {\n                    if selector.matches_featureless_host(scope_matches_featureless_host)\n                        != MatchesFeaturelessHost::Only\n                    {\n                        return MatchesFeaturelessHost::Never;\n                    }\n                }\n            },\n            // Other components don't match the host scope.\n            _ => return MatchesFeaturelessHost::Never,\n        }\n    }\n    matches\n}\n\n/// Determines whether the given element matches the given compound selector.\n#[inline]\nfn matches_compound_selector<E>(\n    selector_iter: &mut SelectorIter<E::Impl>,\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    if context.featureless()\n        && compound_matches_featureless_host(\n            &mut selector_iter.clone(),\n            /* scope_matches_featureless_host = */ true,\n        ) == MatchesFeaturelessHost::Never\n    {\n        return KleeneValue::False;\n    }\n    let quirks_data = if context.quirks_mode() == QuirksMode::Quirks {\n        Some(selector_iter.clone())\n    } else {\n        None\n    };\n    let mut local_context = LocalMatchingContext {\n        shared: context,\n        rightmost,\n        quirks_data,\n    };\n    KleeneValue::any_false(selector_iter, |simple| {\n        matches_simple_selector(simple, element, &mut local_context)\n    })\n}\n\n/// Determines whether the given element matches the given single selector.\nfn matches_simple_selector<E>(\n    selector: &Component<E::Impl>,\n    element: &E,\n    context: &mut LocalMatchingContext<E::Impl>,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    debug_assert!(context.shared.is_nested() || !context.shared.in_negation());\n    let rightmost = context.rightmost;\n    KleeneValue::from(match *selector {\n        Component::ID(ref id) => {\n            element.has_id(id, context.shared.classes_and_ids_case_sensitivity())\n        },\n        Component::Class(ref class) => {\n            element.has_class(class, context.shared.classes_and_ids_case_sensitivity())\n        },\n        Component::LocalName(ref local_name) => matches_local_name(element, local_name),\n        Component::AttributeInNoNamespaceExists {\n            ref local_name,\n            ref local_name_lower,\n        } => element.has_attr_in_no_namespace(select_name(element, local_name, local_name_lower)),\n        Component::AttributeInNoNamespace {\n            ref local_name,\n            ref value,\n            operator,\n            case_sensitivity,\n        } => element.attr_matches(\n            &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),\n            local_name,\n            &AttrSelectorOperation::WithValue {\n                operator,\n                case_sensitivity: to_unconditional_case_sensitivity(case_sensitivity, element),\n                value,\n            },\n        ),\n        Component::AttributeOther(ref attr_sel) => {\n            matches_rare_attribute_selector(element, attr_sel)\n        },\n        Component::Part(ref parts) => matches_part(element, parts, &mut context.shared),\n        Component::Slotted(ref selector) => {\n            return matches_slotted(element, selector, &mut context.shared, rightmost);\n        },\n        Component::PseudoElement(ref pseudo) => {\n            element.match_pseudo_element(pseudo, context.shared)\n        },\n        Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => true,\n        Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {\n            element.has_namespace(&url.borrow())\n        },\n        Component::ExplicitNoNamespace => {\n            let ns = crate::parser::namespace_empty_string::<E::Impl>();\n            element.has_namespace(&ns.borrow())\n        },\n        Component::NonTSPseudoClass(ref pc) => {\n            if let Some(ref iter) = context.quirks_data {\n                if pc.is_active_or_hover()\n                    && !element.is_link()\n                    && hover_and_active_quirk_applies(iter, context.shared, context.rightmost)\n                {\n                    return KleeneValue::False;\n                }\n            }\n            element.match_non_ts_pseudo_class(pc, &mut context.shared)\n        },\n        Component::Root => element.is_root(),\n        Component::Empty => {\n            if context.shared.needs_selector_flags() {\n                element.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);\n            }\n            element.is_empty()\n        },\n        Component::Host(ref selector) => {\n            return matches_host(element, selector.as_ref(), &mut context.shared, rightmost);\n        },\n        Component::ParentSelector => match context.shared.scope_element {\n            Some(ref scope_element) => element.opaque() == *scope_element,\n            None => element.is_root(),\n        },\n        Component::Scope | Component::ImplicitScope => {\n            let matching_for_invalidation = context.shared.matching_for_invalidation_comparison();\n            if context.shared.matching_for_revalidation() || matching_for_invalidation.is_some() {\n                let may_return_unknown = matching_for_invalidation.unwrap_or(false);\n                return if may_return_unknown {\n                    KleeneValue::Unknown\n                } else {\n                    KleeneValue::from(!context.shared.in_negation())\n                };\n            }\n            match context.shared.scope_element {\n                Some(ref scope_element) => element.opaque() == *scope_element,\n                None => element.is_root(),\n            }\n        },\n        Component::Nth(ref nth_data) => {\n            return matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost);\n        },\n        Component::NthOf(ref nth_of_data) => {\n            return context.shared.nest(|context| {\n                matches_generic_nth_child(\n                    element,\n                    context,\n                    nth_of_data.nth_data(),\n                    nth_of_data.selectors(),\n                    rightmost,\n                )\n            })\n        },\n        Component::Is(ref list) | Component::Where(ref list) => {\n            return context.shared.nest(|context| {\n                matches_complex_selector_list(list.slice(), element, context, rightmost)\n            })\n        },\n        Component::Negation(ref list) => {\n            return context.shared.nest_for_negation(|context| {\n                !matches_complex_selector_list(list.slice(), element, context, rightmost)\n            })\n        },\n        Component::Has(ref relative_selectors) => {\n            return match_relative_selectors(\n                relative_selectors,\n                element,\n                context.shared,\n                rightmost,\n            );\n        },\n        Component::Combinator(_) => unsafe {\n            debug_unreachable!(\"Shouldn't try to selector-match combinators\")\n        },\n        Component::RelativeSelectorAnchor => {\n            let anchor = context.shared.relative_selector_anchor();\n            // We may match inner relative selectors, in which case we want to always match.\n            anchor.map_or(true, |a| a == element.opaque())\n        },\n        Component::Invalid(..) => false,\n    })\n}\n\n#[inline(always)]\npub fn select_name<'a, E: Element, T: PartialEq>(\n    element: &E,\n    local_name: &'a T,\n    local_name_lower: &'a T,\n) -> &'a T {\n    if local_name == local_name_lower || element.is_html_element_in_html_document() {\n        local_name_lower\n    } else {\n        local_name\n    }\n}\n\n#[inline(always)]\npub fn to_unconditional_case_sensitivity<'a, E: Element>(\n    parsed: ParsedCaseSensitivity,\n    element: &E,\n) -> CaseSensitivity {\n    match parsed {\n        ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::ExplicitCaseSensitive => {\n            CaseSensitivity::CaseSensitive\n        },\n        ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,\n        ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {\n            if element.is_html_element_in_html_document() {\n                CaseSensitivity::AsciiCaseInsensitive\n            } else {\n                CaseSensitivity::CaseSensitive\n            }\n        },\n    }\n}\n\nfn matches_generic_nth_child<E>(\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    nth_data: &NthSelectorData,\n    selectors: &[Selector<E::Impl>],\n    rightmost: SubjectOrPseudoElement,\n) -> KleeneValue\nwhere\n    E: Element,\n{\n    if element.ignores_nth_child_selectors() {\n        return KleeneValue::False;\n    }\n    let has_selectors = !selectors.is_empty();\n    let selectors_match = !has_selectors\n        || matches_complex_selector_list(selectors, element, context, rightmost).to_bool(true);\n    if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {\n        // Skip expensive indexing math in invalidation.\n        return if selectors_match && may_return_unknown {\n            KleeneValue::Unknown\n        } else {\n            KleeneValue::from(selectors_match && !context.in_negation())\n        };\n    }\n\n    let NthSelectorData { ty, an_plus_b, .. } = *nth_data;\n    let is_of_type = ty.is_of_type();\n    if ty.is_only() {\n        debug_assert!(\n            !has_selectors,\n            \":only-child and :only-of-type cannot have a selector list!\"\n        );\n        return KleeneValue::from(\n            matches_generic_nth_child(\n                element,\n                context,\n                &NthSelectorData::first(is_of_type),\n                selectors,\n                rightmost,\n            )\n            .to_bool(true)\n                && matches_generic_nth_child(\n                    element,\n                    context,\n                    &NthSelectorData::last(is_of_type),\n                    selectors,\n                    rightmost,\n                )\n                .to_bool(true),\n        );\n    }\n\n    let is_from_end = ty.is_from_end();\n\n    // It's useful to know whether this can only select the first/last element\n    // child for optimization purposes, see the `HAS_EDGE_CHILD_SELECTOR` flag.\n    let is_edge_child_selector = nth_data.is_simple_edge() && !has_selectors;\n\n    if context.needs_selector_flags() {\n        let mut flags = if is_edge_child_selector {\n            ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR\n        } else if is_from_end {\n            ElementSelectorFlags::HAS_SLOW_SELECTOR\n        } else {\n            ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS\n        };\n        flags |= if has_selectors {\n            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF\n        } else {\n            ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH\n        };\n        element.apply_selector_flags(flags);\n    }\n\n    if !selectors_match {\n        return KleeneValue::False;\n    }\n\n    // :first/last-child are rather trivial to match, don't bother with the\n    // cache.\n    if is_edge_child_selector {\n        return if is_from_end {\n            element.next_sibling_element()\n        } else {\n            element.prev_sibling_element()\n        }\n        .is_none()\n        .into();\n    }\n\n    // Lookup or compute the index.\n    let index = if let Some(i) = context\n        .nth_index_cache(is_of_type, is_from_end, selectors)\n        .lookup(element.opaque())\n    {\n        i\n    } else {\n        let i = nth_child_index(\n            element,\n            context,\n            selectors,\n            is_of_type,\n            is_from_end,\n            /* check_cache = */ true,\n            rightmost,\n        );\n        context\n            .nth_index_cache(is_of_type, is_from_end, selectors)\n            .insert(element.opaque(), i);\n        i\n    };\n    debug_assert_eq!(\n        index,\n        nth_child_index(\n            element,\n            context,\n            selectors,\n            is_of_type,\n            is_from_end,\n            /* check_cache = */ false,\n            rightmost,\n        ),\n        \"invalid cache\"\n    );\n\n    an_plus_b.matches_index(index).into()\n}\n\n#[inline]\nfn nth_child_index<E>(\n    element: &E,\n    context: &mut MatchingContext<E::Impl>,\n    selectors: &[Selector<E::Impl>],\n    is_of_type: bool,\n    is_from_end: bool,\n    check_cache: bool,\n    rightmost: SubjectOrPseudoElement,\n) -> i32\nwhere\n    E: Element,\n{\n    // The traversal mostly processes siblings left to right. So when we walk\n    // siblings to the right when computing NthLast/NthLastOfType we're unlikely\n    // to get cache hits along the way. As such, we take the hit of walking the\n    // siblings to the left checking the cache in the is_from_end case (this\n    // matches what Gecko does). The indices-from-the-left is handled during the\n    // regular look further below.\n    if check_cache\n        && is_from_end\n        && !context\n            .nth_index_cache(is_of_type, is_from_end, selectors)\n            .is_empty()\n    {\n        let mut index: i32 = 1;\n        let mut curr = element.clone();\n        while let Some(e) = curr.prev_sibling_element() {\n            curr = e;\n            let matches = if is_of_type {\n                element.is_same_type(&curr)\n            } else if !selectors.is_empty() {\n                matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)\n            } else {\n                true\n            };\n            if !matches {\n                continue;\n            }\n            if let Some(i) = context\n                .nth_index_cache(is_of_type, is_from_end, selectors)\n                .lookup(curr.opaque())\n            {\n                return i - index;\n            }\n            index += 1;\n        }\n    }\n\n    let mut index: i32 = 1;\n    let mut curr = element.clone();\n    let next = |e: E| {\n        if is_from_end {\n            e.next_sibling_element()\n        } else {\n            e.prev_sibling_element()\n        }\n    };\n    while let Some(e) = next(curr) {\n        curr = e;\n        let matches = if is_of_type {\n            element.is_same_type(&curr)\n        } else if !selectors.is_empty() {\n            matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)\n        } else {\n            true\n        };\n        if !matches {\n            continue;\n        }\n        // If we're computing indices from the left, check each element in the\n        // cache. We handle the indices-from-the-right case at the top of this\n        // function.\n        if !is_from_end && check_cache {\n            if let Some(i) = context\n                .nth_index_cache(is_of_type, is_from_end, selectors)\n                .lookup(curr.opaque())\n            {\n                return i + index;\n            }\n        }\n        index += 1;\n    }\n\n    index\n}\n"
  },
  {
    "path": "selectors/nth_index_cache.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse std::hash::Hash;\n\nuse crate::{parser::Selector, tree::OpaqueElement, SelectorImpl};\nuse rustc_hash::FxHashMap;\n\n/// A cache to speed up matching of nth-index-like selectors.\n///\n/// See [1] for some discussion around the design tradeoffs.\n///\n/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1401855#c3\n#[derive(Default)]\npub struct NthIndexCache {\n    nth: NthIndexCacheInner,\n    nth_of_selectors: NthIndexOfSelectorsCaches,\n    nth_last: NthIndexCacheInner,\n    nth_last_of_selectors: NthIndexOfSelectorsCaches,\n    nth_of_type: NthIndexCacheInner,\n    nth_last_of_type: NthIndexCacheInner,\n}\n\nimpl NthIndexCache {\n    /// Gets the appropriate cache for the given parameters.\n    pub fn get<Impl: SelectorImpl>(\n        &mut self,\n        is_of_type: bool,\n        is_from_end: bool,\n        selectors: &[Selector<Impl>],\n    ) -> &mut NthIndexCacheInner {\n        if is_of_type {\n            return if is_from_end {\n                &mut self.nth_last_of_type\n            } else {\n                &mut self.nth_of_type\n            };\n        }\n        if !selectors.is_empty() {\n            return if is_from_end {\n                self.nth_last_of_selectors.lookup(selectors)\n            } else {\n                self.nth_of_selectors.lookup(selectors)\n            };\n        }\n        if is_from_end {\n            &mut self.nth_last\n        } else {\n            &mut self.nth\n        }\n    }\n}\n\n#[derive(Hash, Eq, PartialEq)]\nstruct SelectorListCacheKey(usize);\n\n/// Use the selector list's pointer as the cache key\nimpl SelectorListCacheKey {\n    // :nth-child of selectors are reference-counted with `ThinArc`, so we know their pointers are stable.\n    fn new<Impl: SelectorImpl>(selectors: &[Selector<Impl>]) -> Self {\n        Self(selectors.as_ptr() as usize)\n    }\n}\n\n/// Use a different map of cached indices per :nth-child's or :nth-last-child's selector list\n#[derive(Default)]\npub struct NthIndexOfSelectorsCaches(FxHashMap<SelectorListCacheKey, NthIndexCacheInner>);\n\n/// Get or insert a map of cached incides for the selector list of this\n/// particular :nth-child or :nth-last-child pseudoclass\nimpl NthIndexOfSelectorsCaches {\n    pub fn lookup<Impl: SelectorImpl>(\n        &mut self,\n        selectors: &[Selector<Impl>],\n    ) -> &mut NthIndexCacheInner {\n        self.0\n            .entry(SelectorListCacheKey::new(selectors))\n            .or_default()\n    }\n}\n\n/// The concrete per-pseudo-class cache.\n#[derive(Default)]\npub struct NthIndexCacheInner(FxHashMap<OpaqueElement, i32>);\n\nimpl NthIndexCacheInner {\n    /// Does a lookup for a given element in the cache.\n    pub fn lookup(&mut self, el: OpaqueElement) -> Option<i32> {\n        self.0.get(&el).copied()\n    }\n\n    /// Inserts an entry into the cache.\n    pub fn insert(&mut self, element: OpaqueElement, index: i32) {\n        self.0.insert(element, index);\n    }\n\n    /// Returns whether the cache is empty.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n"
  },
  {
    "path": "selectors/parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace};\nuse crate::attr::{NamespaceConstraint, ParsedAttrSelectorOperation, ParsedCaseSensitivity};\nuse crate::bloom::BLOOM_HASH_MASK;\nuse crate::builder::{\n    relative_selector_list_specificity_and_flags, selector_list_specificity_and_flags,\n    SelectorBuilder, SelectorFlags, Specificity, SpecificityAndFlags,\n};\nuse crate::context::QuirksMode;\nuse crate::sink::Push;\nuse crate::visitor::SelectorListKind;\npub use crate::visitor::SelectorVisitor;\nuse bitflags::bitflags;\nuse cssparser::match_ignore_ascii_case;\nuse cssparser::parse_nth;\nuse cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};\nuse cssparser::{CowRcStr, Delimiter, SourceLocation};\nuse cssparser::{Parser as CssParser, ToCss, Token};\nuse debug_unreachable::debug_unreachable;\nuse precomputed_hash::PrecomputedHash;\nuse servo_arc::{Arc, ArcUnionBorrow, ThinArc, ThinArcUnion, UniqueArc};\nuse smallvec::SmallVec;\nuse std::borrow::{Borrow, Cow};\nuse std::fmt::{self, Debug};\nuse std::iter::Rev;\nuse std::slice;\n\n#[cfg(feature = \"to_shmem\")]\nuse to_shmem_derive::ToShmem;\n\n/// A trait that represents a pseudo-element.\npub trait PseudoElement: Sized + ToCss {\n    /// The `SelectorImpl` this pseudo-element is used for.\n    type Impl: SelectorImpl;\n\n    /// Whether the pseudo-element supports a given state selector to the right\n    /// of it.\n    fn accepts_state_pseudo_classes(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is valid after a ::slotted(..) pseudo.\n    fn valid_after_slotted(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is valid when directly after a ::before/::after pseudo.\n    fn valid_after_before_or_after(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is element-backed.\n    /// https://drafts.csswg.org/css-pseudo-4/#element-like\n    fn parses_as_element_backed(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is ::before or ::after pseudo element,\n    /// which are treated specially when deciding what can come after them.\n    /// https://drafts.csswg.org/css-pseudo-4/#generated-content\n    fn is_before_or_after(&self) -> bool {\n        false\n    }\n\n    /// The count we contribute to the specificity from this pseudo-element.\n    fn specificity_count(&self) -> u32 {\n        1\n    }\n\n    /// Whether this pseudo-element is in a pseudo-element tree (excluding the pseudo-element\n    /// root).\n    /// https://drafts.csswg.org/css-view-transitions-1/#pseudo-root\n    fn is_in_pseudo_element_tree(&self) -> bool {\n        false\n    }\n}\n\n/// A trait that represents a pseudo-class.\npub trait NonTSPseudoClass: Sized + ToCss {\n    /// The `SelectorImpl` this pseudo-element is used for.\n    type Impl: SelectorImpl;\n\n    /// Whether this pseudo-class is :active or :hover.\n    fn is_active_or_hover(&self) -> bool;\n\n    /// Whether this pseudo-class belongs to:\n    ///\n    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos\n    fn is_user_action_state(&self) -> bool;\n\n    fn visit<V>(&self, _visitor: &mut V) -> bool\n    where\n        V: SelectorVisitor<Impl = Self::Impl>,\n    {\n        true\n    }\n}\n\n/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a\n/// Cow::Owned if `s` had to be converted into ASCII lowercase.\nfn to_ascii_lowercase(s: &str) -> Cow<'_, str> {\n    if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') {\n        let mut string = s.to_owned();\n        string[first_uppercase..].make_ascii_lowercase();\n        string.into()\n    } else {\n        s.into()\n    }\n}\n\nbitflags! {\n    /// Flags that indicate at which point of parsing a selector are we.\n    #[derive(Copy, Clone)]\n    struct SelectorParsingState: u16 {\n        /// Whether we should avoid adding default namespaces to selectors that\n        /// aren't type or universal selectors.\n        const SKIP_DEFAULT_NAMESPACE = 1 << 0;\n\n        /// Whether we've parsed a ::slotted() pseudo-element already.\n        ///\n        /// If so, then we can only parse a subset of pseudo-elements, and\n        /// whatever comes after them if so.\n        const AFTER_SLOTTED = 1 << 1;\n        /// Whether we've parsed a ::part() or element-backed pseudo-element already.\n        ///\n        /// If so, then we can only parse a subset of pseudo-elements, and\n        /// whatever comes after them if so.\n        const AFTER_PART_LIKE = 1 << 2;\n        /// Whether we've parsed a non-element-backed pseudo-element (as in, an\n        /// `Impl::PseudoElement` thus not accounting for `::slotted` or\n        /// `::part`) already.\n        ///\n        /// If so, then other pseudo-elements and most other selectors are\n        /// disallowed.\n        const AFTER_NON_ELEMENT_BACKED_PSEUDO = 1 << 3;\n        /// Whether we've parsed a non-stateful pseudo-element (again, as-in\n        /// `Impl::PseudoElement`) already. If so, then other pseudo-classes are\n        /// disallowed. If this flag is set, `AFTER_NON_ELEMENT_BACKED_PSEUDO` must be set\n        /// as well.\n        const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;\n        // Whether we've parsed a generated pseudo-element (as in ::before, ::after).\n        // If so then some other pseudo elements are disallowed (e.g. another generated pseudo)\n        // while others allowed (e.g. ::marker).\n        const AFTER_BEFORE_OR_AFTER_PSEUDO = 1 << 5;\n\n        /// Whether we are after any of the pseudo-like things.\n        const AFTER_PSEUDO = Self::AFTER_PART_LIKE.bits() | Self::AFTER_SLOTTED.bits() | Self::AFTER_NON_ELEMENT_BACKED_PSEUDO.bits() | Self::AFTER_BEFORE_OR_AFTER_PSEUDO.bits();\n\n        /// Whether we explicitly disallow combinators.\n        const DISALLOW_COMBINATORS = 1 << 6;\n\n        /// Whether we explicitly disallow pseudo-element-like things.\n        const DISALLOW_PSEUDOS = 1 << 7;\n\n        /// Whether we explicitly disallow relative selectors (i.e. `:has()`).\n        const DISALLOW_RELATIVE_SELECTOR = 1 << 8;\n\n        /// Whether we've parsed a pseudo-element which is in a pseudo-element tree (i.e. it is a\n        /// descendant pseudo of a pseudo-element root).\n        const IN_PSEUDO_ELEMENT_TREE = 1 << 9;\n    }\n}\n\nimpl SelectorParsingState {\n    #[inline]\n    fn allows_slotted(self) -> bool {\n        !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)\n    }\n\n    #[inline]\n    fn allows_part(self) -> bool {\n        !self.intersects(Self::AFTER_PSEUDO | Self::DISALLOW_PSEUDOS)\n    }\n\n    #[inline]\n    fn allows_non_functional_pseudo_classes(self) -> bool {\n        !self.intersects(Self::AFTER_SLOTTED | Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)\n    }\n\n    #[inline]\n    fn allows_tree_structural_pseudo_classes(self) -> bool {\n        !self.intersects(Self::AFTER_PSEUDO) || self.intersects(Self::IN_PSEUDO_ELEMENT_TREE)\n    }\n\n    #[inline]\n    fn allows_combinators(self) -> bool {\n        !self.intersects(Self::DISALLOW_COMBINATORS)\n    }\n\n    #[inline]\n    fn allows_only_child_pseudo_class_only(self) -> bool {\n        self.intersects(Self::IN_PSEUDO_ELEMENT_TREE)\n    }\n}\n\npub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;\n\n#[derive(Clone, Debug, PartialEq)]\npub enum SelectorParseErrorKind<'i> {\n    NoQualifiedNameInAttributeSelector(Token<'i>),\n    EmptySelector,\n    DanglingCombinator,\n    NonCompoundSelector,\n    NonPseudoElementAfterSlotted,\n    InvalidPseudoElementAfterSlotted,\n    InvalidPseudoElementInsideWhere,\n    InvalidState,\n    UnexpectedTokenInAttributeSelector(Token<'i>),\n    PseudoElementExpectedColon(Token<'i>),\n    PseudoElementExpectedIdent(Token<'i>),\n    NoIdentForPseudo(Token<'i>),\n    UnsupportedPseudoClassOrElement(CowRcStr<'i>),\n    UnexpectedIdent(CowRcStr<'i>),\n    ExpectedNamespace(CowRcStr<'i>),\n    ExpectedBarInAttr(Token<'i>),\n    BadValueInAttr(Token<'i>),\n    InvalidQualNameInAttr(Token<'i>),\n    ExplicitNamespaceUnexpectedToken(Token<'i>),\n    ClassNeedsIdent(Token<'i>),\n}\n\nmacro_rules! with_all_bounds {\n    (\n        [ $( $InSelector: tt )* ]\n        [ $( $CommonBounds: tt )* ]\n        [ $( $FromStr: tt )* ]\n    ) => {\n        /// This trait allows to define the parser implementation in regards\n        /// of pseudo-classes/elements\n        ///\n        /// NB: We need Clone so that we can derive(Clone) on struct with that\n        /// are parameterized on SelectorImpl. See\n        /// <https://github.com/rust-lang/rust/issues/26925>\n        pub trait SelectorImpl: Clone + Debug + Sized + 'static {\n            type ExtraMatchingData<'a>: Sized + Default;\n            type AttrValue: $($InSelector)*;\n            type Identifier: $($InSelector)* + PrecomputedHash;\n            type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName> + PrecomputedHash;\n            type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl> + PrecomputedHash;\n            type NamespacePrefix: $($InSelector)* + Default;\n            type BorrowedNamespaceUrl: ?Sized + Eq;\n            type BorrowedLocalName: ?Sized + Eq;\n\n            /// non tree-structural pseudo-classes\n            /// (see: https://drafts.csswg.org/selectors/#structural-pseudos)\n            type NonTSPseudoClass: $($CommonBounds)* + NonTSPseudoClass<Impl = Self>;\n\n            /// pseudo-elements\n            type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;\n\n            /// Whether attribute hashes should be collected for filtering\n            /// purposes.\n            fn should_collect_attr_hash(_name: &Self::LocalName) -> bool {\n                false\n            }\n        }\n    }\n}\n\nmacro_rules! with_bounds {\n    ( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {\n        with_all_bounds! {\n            [$($CommonBounds)* + $($FromStr)* + ToCss]\n            [$($CommonBounds)*]\n            [$($FromStr)*]\n        }\n    }\n}\n\nwith_bounds! {\n    [Clone + Eq]\n    [for<'a> From<&'a str>]\n}\n\npub trait Parser<'i> {\n    type Impl: SelectorImpl;\n    type Error: 'i + From<SelectorParseErrorKind<'i>>;\n\n    /// Whether to parse the `::slotted()` pseudo-element.\n    fn parse_slotted(&self) -> bool {\n        false\n    }\n\n    /// Whether to parse the `::part()` pseudo-element.\n    fn parse_part(&self) -> bool {\n        false\n    }\n\n    /// Whether to parse the selector list of nth-child() or nth-last-child().\n    fn parse_nth_child_of(&self) -> bool {\n        false\n    }\n\n    /// Whether to parse `:is` and `:where` pseudo-classes.\n    fn parse_is_and_where(&self) -> bool {\n        false\n    }\n\n    /// Whether to parse the :has pseudo-class.\n    fn parse_has(&self) -> bool {\n        false\n    }\n\n    /// Whether to parse the '&' delimiter as a parent selector.\n    fn parse_parent_selector(&self) -> bool {\n        false\n    }\n\n    /// Whether the given function name is an alias for the `:is()` function.\n    fn is_is_alias(&self, _name: &str) -> bool {\n        false\n    }\n\n    /// Whether to parse the `:host` pseudo-class.\n    fn parse_host(&self) -> bool {\n        false\n    }\n\n    /// Whether to allow forgiving selector-list parsing.\n    fn allow_forgiving_selectors(&self) -> bool {\n        true\n    }\n\n    /// This function can return an \"Err\" pseudo-element in order to support CSS2.1\n    /// pseudo-elements.\n    fn parse_non_ts_pseudo_class(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>> {\n        Err(\n            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn parse_non_ts_functional_pseudo_class<'t>(\n        &self,\n        name: CowRcStr<'i>,\n        parser: &mut CssParser<'i, 't>,\n        _after_part: bool,\n    ) -> Result<<Self::Impl as SelectorImpl>::NonTSPseudoClass, ParseError<'i, Self::Error>> {\n        Err(\n            parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn parse_pseudo_element(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ParseError<'i, Self::Error>> {\n        Err(\n            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn parse_functional_pseudo_element<'t>(\n        &self,\n        name: CowRcStr<'i>,\n        arguments: &mut CssParser<'i, 't>,\n    ) -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ParseError<'i, Self::Error>> {\n        Err(\n            arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn default_namespace(&self) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {\n        None\n    }\n\n    fn namespace_for_prefix(\n        &self,\n        _prefix: &<Self::Impl as SelectorImpl>::NamespacePrefix,\n    ) -> Option<<Self::Impl as SelectorImpl>::NamespaceUrl> {\n        None\n    }\n}\n\n/// A selector list is a tagged pointer with either a single selector, or a ThinArc<()> of multiple\n/// selectors.\n#[derive(Clone, Eq, Debug, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct SelectorList<Impl: SelectorImpl>(\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    ThinArcUnion<SpecificityAndFlags, Component<Impl>, (), Selector<Impl>>,\n);\n\nimpl<Impl: SelectorImpl> SelectorList<Impl> {\n    /// See Arc::mark_as_intentionally_leaked\n    pub fn mark_as_intentionally_leaked(&self) {\n        if let ArcUnionBorrow::Second(ref list) = self.0.borrow() {\n            list.with_arc(|list| list.mark_as_intentionally_leaked())\n        }\n        self.slice()\n            .iter()\n            .for_each(|s| s.mark_as_intentionally_leaked())\n    }\n\n    pub fn from_one(selector: Selector<Impl>) -> Self {\n        #[cfg(debug_assertions)]\n        let selector_repr = unsafe { *(&selector as *const _ as *const usize) };\n        let list = Self(ThinArcUnion::from_first(selector.into_data()));\n        #[cfg(debug_assertions)]\n        debug_assert_eq!(\n            selector_repr,\n            unsafe { *(&list as *const _ as *const usize) },\n            \"We rely on the same bit representation for the single selector variant\"\n        );\n        list\n    }\n\n    pub fn from_iter(mut iter: impl ExactSizeIterator<Item = Selector<Impl>>) -> Self {\n        if iter.len() == 1 {\n            Self::from_one(iter.next().unwrap())\n        } else {\n            Self(ThinArcUnion::from_second(ThinArc::from_header_and_iter(\n                (),\n                iter,\n            )))\n        }\n    }\n\n    #[inline]\n    pub fn slice(&self) -> &[Selector<Impl>] {\n        match self.0.borrow() {\n            ArcUnionBorrow::First(..) => {\n                // SAFETY: see from_one.\n                let selector: &Selector<Impl> = unsafe { std::mem::transmute(self) };\n                std::slice::from_ref(selector)\n            },\n            ArcUnionBorrow::Second(list) => list.get().slice(),\n        }\n    }\n\n    #[inline]\n    pub fn len(&self) -> usize {\n        match self.0.borrow() {\n            ArcUnionBorrow::First(..) => 1,\n            ArcUnionBorrow::Second(list) => list.len(),\n        }\n    }\n\n    /// Returns the address on the heap of the ThinArc for memory reporting.\n    pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void {\n        match self.0.borrow() {\n            ArcUnionBorrow::First(s) => s.with_arc(|a| a.heap_ptr()),\n            ArcUnionBorrow::Second(s) => s.with_arc(|a| a.heap_ptr()),\n        }\n    }\n}\n\n/// Uniquely identify a selector based on its components, which is behind ThinArc and\n/// is therefore stable.\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\npub struct SelectorKey(usize);\n\nimpl SelectorKey {\n    /// Create a new key based on the given selector.\n    pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>) -> Self {\n        Self(selector.0.slice().as_ptr() as usize)\n    }\n}\n\n/// Whether or not we're using forgiving parsing mode\n#[derive(PartialEq)]\nenum ForgivingParsing {\n    /// Discard the entire selector list upon encountering any invalid selector.\n    /// This is the default behavior for almost all of CSS.\n    No,\n    /// Ignore invalid selectors, potentially creating an empty selector list.\n    ///\n    /// This is the error recovery mode of :is() and :where()\n    Yes,\n}\n\n/// Flag indicating if we're parsing relative selectors.\n#[derive(Copy, Clone, PartialEq)]\npub enum ParseRelative {\n    /// Expect selectors to start with a combinator, assuming descendant combinator if not present.\n    ForHas,\n    /// Allow selectors to start with a combinator, prepending a parent selector if so. Do nothing\n    /// otherwise\n    ForNesting,\n    /// Allow selectors to start with a combinator, prepending a scope selector if so. Do nothing\n    /// otherwise\n    ForScope,\n    /// Treat as parse error if any selector begins with a combinator.\n    No,\n}\n\nimpl<Impl: SelectorImpl> SelectorList<Impl> {\n    /// Returns a selector list with a single `:scope` selector (with specificity)\n    pub fn scope() -> Self {\n        Self::from_one(Selector::scope())\n    }\n    /// Returns a selector list with a single implicit `:scope` selector (no specificity)\n    pub fn implicit_scope() -> Self {\n        Self::from_one(Selector::implicit_scope())\n    }\n\n    /// Parse a comma-separated list of Selectors.\n    /// <https://drafts.csswg.org/selectors/#grouping>\n    ///\n    /// Return the Selectors or Err if there is an invalid selector.\n    pub fn parse<'i, 't, P>(\n        parser: &P,\n        input: &mut CssParser<'i, 't>,\n        parse_relative: ParseRelative,\n    ) -> Result<Self, ParseError<'i, P::Error>>\n    where\n        P: Parser<'i, Impl = Impl>,\n    {\n        Self::parse_with_state(\n            parser,\n            input,\n            SelectorParsingState::empty(),\n            ForgivingParsing::No,\n            parse_relative,\n        )\n    }\n\n    /// Same as `parse`, but disallow parsing of pseudo-elements.\n    pub fn parse_disallow_pseudo<'i, 't, P>(\n        parser: &P,\n        input: &mut CssParser<'i, 't>,\n        parse_relative: ParseRelative,\n    ) -> Result<Self, ParseError<'i, P::Error>>\n    where\n        P: Parser<'i, Impl = Impl>,\n    {\n        Self::parse_with_state(\n            parser,\n            input,\n            SelectorParsingState::DISALLOW_PSEUDOS,\n            ForgivingParsing::No,\n            parse_relative,\n        )\n    }\n\n    pub fn parse_forgiving<'i, 't, P>(\n        parser: &P,\n        input: &mut CssParser<'i, 't>,\n        parse_relative: ParseRelative,\n    ) -> Result<Self, ParseError<'i, P::Error>>\n    where\n        P: Parser<'i, Impl = Impl>,\n    {\n        Self::parse_with_state(\n            parser,\n            input,\n            SelectorParsingState::empty(),\n            ForgivingParsing::Yes,\n            parse_relative,\n        )\n    }\n\n    #[inline]\n    fn parse_with_state<'i, 't, P>(\n        parser: &P,\n        input: &mut CssParser<'i, 't>,\n        state: SelectorParsingState,\n        recovery: ForgivingParsing,\n        parse_relative: ParseRelative,\n    ) -> Result<Self, ParseError<'i, P::Error>>\n    where\n        P: Parser<'i, Impl = Impl>,\n    {\n        let mut values = SmallVec::<[_; 4]>::new();\n        let forgiving = recovery == ForgivingParsing::Yes && parser.allow_forgiving_selectors();\n        loop {\n            let selector = input.parse_until_before(Delimiter::Comma, |input| {\n                let start = input.position();\n                let mut selector = parse_selector(parser, input, state, parse_relative);\n                if forgiving && (selector.is_err() || input.expect_exhausted().is_err()) {\n                    input.expect_no_error_token()?;\n                    selector = Ok(Selector::new_invalid(input.slice_from(start)));\n                }\n                selector\n            })?;\n\n            values.push(selector);\n\n            match input.next() {\n                Ok(&Token::Comma) => {},\n                Ok(_) => unreachable!(),\n                Err(_) => break,\n            }\n        }\n        Ok(Self::from_iter(values.into_iter()))\n    }\n\n    /// Replaces the parent selector in all the items of the selector list.\n    pub fn replace_parent_selector(&self, parent: &SelectorList<Impl>) -> Self {\n        Self::from_iter(\n            self.slice()\n                .iter()\n                .map(|selector| selector.replace_parent_selector(parent)),\n        )\n    }\n\n    /// Creates a SelectorList from a Vec of selectors. Used in tests.\n    #[allow(dead_code)]\n    pub(crate) fn from_vec(v: Vec<Selector<Impl>>) -> Self {\n        SelectorList::from_iter(v.into_iter())\n    }\n}\n\n/// Parses one compound selector suitable for nested stuff like :-moz-any, etc.\nfn parse_inner_compound_selector<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n) -> Result<Selector<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    parse_selector(\n        parser,\n        input,\n        state | SelectorParsingState::DISALLOW_PSEUDOS | SelectorParsingState::DISALLOW_COMBINATORS,\n        ParseRelative::No,\n    )\n}\n\n/// Ancestor hashes for the bloom filter. We precompute these and store them\n/// inline with selectors to optimize cache performance during matching.\n/// This matters a lot.\n///\n/// We use 4 hashes, which is copied from Gecko, who copied it from WebKit.\n/// Note that increasing the number of hashes here will adversely affect the\n/// cache hit when fast-rejecting long lists of Rules with inline hashes.\n///\n/// Because the bloom filter only uses the bottom 24 bits of the hash, we pack\n/// the fourth hash into the upper bits of the first three hashes in order to\n/// shrink Rule (whose size matters a lot). This scheme minimizes the runtime\n/// overhead of the packing for the first three hashes (we just need to mask\n/// off the upper bits) at the expense of making the fourth somewhat more\n/// complicated to assemble, because we often bail out before checking all the\n/// hashes.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct AncestorHashes {\n    pub packed_hashes: [u32; 3],\n}\n\npub(crate) fn collect_selector_hashes<'a, Impl: SelectorImpl, Iter>(\n    iter: Iter,\n    quirks_mode: QuirksMode,\n    hashes: &mut [u32; 4],\n    len: &mut usize,\n    create_inner_iterator: fn(&'a Selector<Impl>) -> Iter,\n) -> bool\nwhere\n    Iter: Iterator<Item = &'a Component<Impl>>,\n{\n    for component in iter {\n        let hash = match *component {\n            Component::LocalName(LocalName {\n                ref name,\n                ref lower_name,\n            }) => {\n                // Only insert the local-name into the filter if it's all\n                // lowercase.  Otherwise we would need to test both hashes, and\n                // our data structures aren't really set up for that.\n                if name != lower_name {\n                    continue;\n                }\n                name.precomputed_hash()\n            },\n            Component::DefaultNamespace(ref url) | Component::Namespace(_, ref url) => {\n                url.precomputed_hash()\n            },\n            // In quirks mode, class and id selectors should match\n            // case-insensitively, so just avoid inserting them into the filter.\n            Component::ID(ref id) if quirks_mode != QuirksMode::Quirks => id.precomputed_hash(),\n            Component::Class(ref class) if quirks_mode != QuirksMode::Quirks => {\n                class.precomputed_hash()\n            },\n            Component::AttributeInNoNamespace { ref local_name, .. }\n                if Impl::should_collect_attr_hash(local_name) =>\n            {\n                // AttributeInNoNamespace is only used when local_name ==\n                // local_name_lower.\n                local_name.precomputed_hash()\n            },\n            Component::AttributeInNoNamespaceExists {\n                ref local_name,\n                ref local_name_lower,\n                ..\n            } => {\n                // Only insert the local-name into the filter if it's all\n                // lowercase.  Otherwise we would need to test both hashes, and\n                // our data structures aren't really set up for that.\n                if local_name != local_name_lower || !Impl::should_collect_attr_hash(local_name) {\n                    continue;\n                }\n                local_name.precomputed_hash()\n            },\n            Component::AttributeOther(ref selector) => {\n                if selector.local_name != selector.local_name_lower\n                    || !Impl::should_collect_attr_hash(&selector.local_name)\n                {\n                    continue;\n                }\n                selector.local_name.precomputed_hash()\n            },\n            Component::Is(ref list) | Component::Where(ref list) => {\n                // :where and :is OR their selectors, so we can't put any hash\n                // in the filter if there's more than one selector, as that'd\n                // exclude elements that may match one of the other selectors.\n                let slice = list.slice();\n                if slice.len() == 1\n                    && !collect_selector_hashes(\n                        create_inner_iterator(&slice[0]),\n                        quirks_mode,\n                        hashes,\n                        len,\n                        create_inner_iterator,\n                    )\n                {\n                    return false;\n                }\n                continue;\n            },\n            _ => continue,\n        };\n\n        hashes[*len] = hash & BLOOM_HASH_MASK;\n        *len += 1;\n        if *len == hashes.len() {\n            return false;\n        }\n    }\n    true\n}\n\nfn collect_ancestor_hashes<Impl: SelectorImpl>(\n    iter: SelectorIter<Impl>,\n    quirks_mode: QuirksMode,\n    hashes: &mut [u32; 4],\n    len: &mut usize,\n) {\n    collect_selector_hashes(AncestorIter::new(iter), quirks_mode, hashes, len, |s| {\n        AncestorIter(s.iter())\n    });\n}\n\nimpl AncestorHashes {\n    pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self {\n        // Compute ancestor hashes for the bloom filter.\n        let mut hashes = [0u32; 4];\n        let mut len = 0;\n        collect_ancestor_hashes(selector.iter(), quirks_mode, &mut hashes, &mut len);\n        debug_assert!(len <= 4);\n\n        // Now, pack the fourth hash (if it exists) into the upper byte of each of\n        // the other three hashes.\n        if len == 4 {\n            let fourth = hashes[3];\n            hashes[0] |= (fourth & 0x000000ff) << 24;\n            hashes[1] |= (fourth & 0x0000ff00) << 16;\n            hashes[2] |= (fourth & 0x00ff0000) << 8;\n        }\n\n        AncestorHashes {\n            packed_hashes: [hashes[0], hashes[1], hashes[2]],\n        }\n    }\n\n    /// Returns the fourth hash, reassembled from parts.\n    pub fn fourth_hash(&self) -> u32 {\n        ((self.packed_hashes[0] & 0xff000000) >> 24)\n            | ((self.packed_hashes[1] & 0xff000000) >> 16)\n            | ((self.packed_hashes[2] & 0xff000000) >> 8)\n    }\n}\n\n#[inline]\npub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {\n    // Rust type’s default, not default namespace\n    Impl::NamespaceUrl::default()\n}\n\npub(super) type SelectorData<Impl> = ThinArc<SpecificityAndFlags, Component<Impl>>;\n\n/// Whether a selector may match a featureless host element, and whether it may match other\n/// elements.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum MatchesFeaturelessHost {\n    /// The selector may match a featureless host, but also a non-featureless element.\n    Yes,\n    /// The selector is guaranteed to never match a non-featureless host element.\n    Only,\n    /// The selector never matches a featureless host.\n    Never,\n}\n\nimpl MatchesFeaturelessHost {\n    /// Whether we may match.\n    #[inline]\n    pub fn may_match(self) -> bool {\n        return !matches!(self, Self::Never);\n    }\n}\n\n/// A Selector stores a sequence of simple selectors and combinators. The\n/// iterator classes allow callers to iterate at either the raw sequence level or\n/// at the level of sequences of simple selectors separated by combinators. Most\n/// callers want the higher-level iterator.\n///\n/// We store compound selectors internally right-to-left (in matching order).\n/// Additionally, we invert the order of top-level compound selectors so that\n/// each one matches left-to-right. This is because matching namespace, local name,\n/// id, and class are all relatively cheap, whereas matching pseudo-classes might\n/// be expensive (depending on the pseudo-class). Since authors tend to put the\n/// pseudo-classes on the right, it's faster to start matching on the left.\n///\n/// This reordering doesn't change the semantics of selector matching, and we\n/// handle it in to_css to make it invisible to serialization.\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\n#[repr(transparent)]\npub struct Selector<Impl: SelectorImpl>(\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] SelectorData<Impl>,\n);\n\nimpl<Impl: SelectorImpl> Selector<Impl> {\n    /// See Arc::mark_as_intentionally_leaked\n    pub fn mark_as_intentionally_leaked(&self) {\n        self.0.mark_as_intentionally_leaked()\n    }\n\n    fn scope() -> Self {\n        Self(ThinArc::from_header_and_iter(\n            SpecificityAndFlags {\n                specificity: Specificity::single_class_like().into(),\n                flags: SelectorFlags::HAS_SCOPE,\n            },\n            std::iter::once(Component::Scope),\n        ))\n    }\n\n    /// An implicit scope selector, much like :where(:scope).\n    fn implicit_scope() -> Self {\n        Self(ThinArc::from_header_and_iter(\n            SpecificityAndFlags {\n                specificity: 0,\n                flags: SelectorFlags::HAS_SCOPE,\n            },\n            std::iter::once(Component::ImplicitScope),\n        ))\n    }\n\n    #[inline]\n    pub fn specificity(&self) -> u32 {\n        self.0.header.specificity\n    }\n\n    #[inline]\n    pub(crate) fn flags(&self) -> SelectorFlags {\n        self.0.header.flags\n    }\n\n    #[inline]\n    pub fn has_pseudo_element(&self) -> bool {\n        self.flags().intersects(SelectorFlags::HAS_PSEUDO)\n    }\n\n    #[inline]\n    pub fn has_parent_selector(&self) -> bool {\n        self.flags().intersects(SelectorFlags::HAS_PARENT)\n    }\n\n    #[inline]\n    pub fn has_scope_selector(&self) -> bool {\n        self.flags().intersects(SelectorFlags::HAS_SCOPE)\n    }\n\n    #[inline]\n    pub fn is_slotted(&self) -> bool {\n        self.flags().intersects(SelectorFlags::HAS_SLOTTED)\n    }\n\n    #[inline]\n    pub fn is_part(&self) -> bool {\n        self.flags().intersects(SelectorFlags::HAS_PART)\n    }\n\n    #[inline]\n    pub fn parts(&self) -> Option<&[Impl::Identifier]> {\n        if !self.is_part() {\n            return None;\n        }\n\n        let mut iter = self.iter();\n        if self.has_pseudo_element() {\n            // Skip the pseudo-element.\n            for _ in &mut iter {}\n\n            let combinator = iter.next_sequence()?;\n            debug_assert_eq!(combinator, Combinator::PseudoElement);\n        }\n\n        for component in iter {\n            if let Component::Part(ref part) = *component {\n                return Some(part);\n            }\n        }\n\n        debug_assert!(false, \"is_part() lied somehow?\");\n        None\n    }\n\n    #[inline]\n    pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> {\n        if !self.has_pseudo_element() {\n            return None;\n        }\n\n        for component in self.iter() {\n            if let Component::PseudoElement(ref pseudo) = *component {\n                return Some(pseudo);\n            }\n        }\n\n        debug_assert!(false, \"has_pseudo_element lied!\");\n        None\n    }\n\n    #[inline]\n    pub fn pseudo_elements(&self) -> SmallVec<[&Impl::PseudoElement; 3]> {\n        let mut pseudos = SmallVec::new();\n\n        if !self.has_pseudo_element() {\n            return pseudos;\n        }\n\n        let mut iter = self.iter();\n        loop {\n            for component in &mut iter {\n                if let Component::PseudoElement(ref pseudo) = *component {\n                    pseudos.push(pseudo);\n                }\n            }\n            match iter.next_sequence() {\n                Some(Combinator::PseudoElement) => {},\n                _ => break,\n            }\n        }\n\n        debug_assert!(!pseudos.is_empty(), \"has_pseudo_element lied!\");\n\n        pseudos\n    }\n\n    /// Whether this selector (pseudo-element part excluded) matches every element.\n    ///\n    /// Used for \"pre-computed\" pseudo-elements in components/style/stylist.rs\n    #[inline]\n    pub fn is_universal(&self) -> bool {\n        self.iter_raw_match_order().all(|c| {\n            matches!(\n                *c,\n                Component::ExplicitUniversalType\n                    | Component::ExplicitAnyNamespace\n                    | Component::Combinator(Combinator::PseudoElement)\n                    | Component::PseudoElement(..)\n            )\n        })\n    }\n\n    /// Whether this selector may match a featureless shadow host, with no combinators to the\n    /// left, and optionally has a pseudo-element to the right.\n    #[inline]\n    pub fn matches_featureless_host(\n        &self,\n        scope_matches_featureless_host: bool,\n    ) -> MatchesFeaturelessHost {\n        let flags = self.flags();\n        if !flags.intersects(SelectorFlags::HAS_HOST | SelectorFlags::HAS_SCOPE) {\n            return MatchesFeaturelessHost::Never;\n        }\n\n        let mut iter = self.iter();\n        if flags.intersects(SelectorFlags::HAS_PSEUDO) {\n            for _ in &mut iter {\n                // Skip over pseudo-elements\n            }\n            match iter.next_sequence() {\n                Some(c) if c.is_pseudo_element() => {},\n                _ => {\n                    debug_assert!(false, \"Pseudo selector without pseudo combinator?\");\n                    return MatchesFeaturelessHost::Never;\n                },\n            }\n        }\n\n        let compound_matches = crate::matching::compound_matches_featureless_host(\n            &mut iter,\n            scope_matches_featureless_host,\n        );\n        if iter.next_sequence().is_some() {\n            return MatchesFeaturelessHost::Never;\n        }\n        return compound_matches;\n    }\n\n    /// Returns an iterator over this selector in matching order (right-to-left).\n    /// When a combinator is reached, the iterator will return None, and\n    /// next_sequence() may be called to continue to the next sequence.\n    #[inline]\n    pub fn iter(&self) -> SelectorIter<'_, Impl> {\n        SelectorIter {\n            iter: self.iter_raw_match_order(),\n            next_combinator: None,\n        }\n    }\n\n    /// Same as `iter()`, but skips `RelativeSelectorAnchor` and its associated combinator.\n    #[inline]\n    pub fn iter_skip_relative_selector_anchor(&self) -> SelectorIter<'_, Impl> {\n        if cfg!(debug_assertions) {\n            let mut selector_iter = self.iter_raw_parse_order_from(0);\n            assert!(\n                matches!(\n                    selector_iter.next().unwrap(),\n                    Component::RelativeSelectorAnchor\n                ),\n                \"Relative selector does not start with RelativeSelectorAnchor\"\n            );\n            assert!(\n                selector_iter.next().unwrap().is_combinator(),\n                \"Relative combinator does not exist\"\n            );\n        }\n\n        SelectorIter {\n            iter: self.0.slice()[..self.len() - 2].iter(),\n            next_combinator: None,\n        }\n    }\n\n    /// Returns an iterator over this selector in matching order (right-to-left),\n    /// skipping the rightmost |offset| Components.\n    #[inline]\n    pub fn iter_from(&self, offset: usize) -> SelectorIter<'_, Impl> {\n        let iter = self.0.slice()[offset..].iter();\n        SelectorIter {\n            iter,\n            next_combinator: None,\n        }\n    }\n\n    /// Returns the combinator at index `index` (zero-indexed from the right),\n    /// or panics if the component is not a combinator.\n    #[inline]\n    pub fn combinator_at_match_order(&self, index: usize) -> Combinator {\n        match self.0.slice()[index] {\n            Component::Combinator(c) => c,\n            ref other => panic!(\n                \"Not a combinator: {:?}, {:?}, index: {}\",\n                other, self, index\n            ),\n        }\n    }\n\n    /// Returns an iterator over the entire sequence of simple selectors and\n    /// combinators, in matching order (from right to left).\n    #[inline]\n    pub fn iter_raw_match_order(&self) -> slice::Iter<'_, Component<Impl>> {\n        self.0.slice().iter()\n    }\n\n    /// Returns the combinator at index `index` (zero-indexed from the left),\n    /// or panics if the component is not a combinator.\n    #[inline]\n    pub fn combinator_at_parse_order(&self, index: usize) -> Combinator {\n        match self.0.slice()[self.len() - index - 1] {\n            Component::Combinator(c) => c,\n            ref other => panic!(\n                \"Not a combinator: {:?}, {:?}, index: {}\",\n                other, self, index\n            ),\n        }\n    }\n\n    /// Returns an iterator over the sequence of simple selectors and\n    /// combinators, in parse order (from left to right), starting from\n    /// `offset`.\n    #[inline]\n    pub fn iter_raw_parse_order_from(\n        &self,\n        offset: usize,\n    ) -> Rev<slice::Iter<'_, Component<Impl>>> {\n        self.0.slice()[..self.len() - offset].iter().rev()\n    }\n\n    /// Creates a Selector from a vec of Components, specified in parse order. Used in tests.\n    #[allow(dead_code)]\n    pub(crate) fn from_vec(\n        vec: Vec<Component<Impl>>,\n        specificity: u32,\n        flags: SelectorFlags,\n    ) -> Self {\n        let mut builder = SelectorBuilder::default();\n        for component in vec.into_iter() {\n            if let Some(combinator) = component.as_combinator() {\n                builder.push_combinator(combinator);\n            } else {\n                builder.push_simple_selector(component);\n            }\n        }\n        let spec = SpecificityAndFlags { specificity, flags };\n        Selector(builder.build_with_specificity_and_flags(spec, ParseRelative::No))\n    }\n\n    #[inline]\n    fn into_data(self) -> SelectorData<Impl> {\n        self.0\n    }\n\n    pub fn replace_parent_selector(&self, parent: &SelectorList<Impl>) -> Self {\n        let parent_specificity_and_flags = selector_list_specificity_and_flags(\n            parent.slice().iter(),\n            /* for_nesting_parent = */ true,\n        );\n\n        let mut specificity = Specificity::from(self.specificity());\n        let mut flags = self.flags() - SelectorFlags::HAS_PARENT;\n        let forbidden_flags = SelectorFlags::forbidden_for_nesting();\n\n        fn replace_parent_on_selector_list<Impl: SelectorImpl>(\n            orig: &[Selector<Impl>],\n            parent: &SelectorList<Impl>,\n            specificity: &mut Specificity,\n            flags: &mut SelectorFlags,\n            propagate_specificity: bool,\n            forbidden_flags: SelectorFlags,\n        ) -> Option<SelectorList<Impl>> {\n            if !orig.iter().any(|s| s.has_parent_selector()) {\n                return None;\n            }\n\n            let result =\n                SelectorList::from_iter(orig.iter().map(|s| s.replace_parent_selector(parent)));\n\n            let result_specificity_and_flags = selector_list_specificity_and_flags(\n                result.slice().iter(),\n                /* for_nesting_parent = */ false,\n            );\n            if propagate_specificity {\n                *specificity += Specificity::from(\n                    result_specificity_and_flags.specificity\n                        - selector_list_specificity_and_flags(\n                            orig.iter(),\n                            /* for_nesting_parent = */ false,\n                        )\n                        .specificity,\n                );\n            }\n            flags.insert(result_specificity_and_flags.flags - forbidden_flags);\n            Some(result)\n        }\n\n        fn replace_parent_on_relative_selector_list<Impl: SelectorImpl>(\n            orig: &[RelativeSelector<Impl>],\n            parent: &SelectorList<Impl>,\n            specificity: &mut Specificity,\n            flags: &mut SelectorFlags,\n            forbidden_flags: SelectorFlags,\n        ) -> Vec<RelativeSelector<Impl>> {\n            let mut any = false;\n\n            let result = orig\n                .iter()\n                .map(|s| {\n                    if !s.selector.has_parent_selector() {\n                        return s.clone();\n                    }\n                    any = true;\n                    RelativeSelector {\n                        match_hint: s.match_hint,\n                        selector: s.selector.replace_parent_selector(parent),\n                    }\n                })\n                .collect();\n\n            if !any {\n                return result;\n            }\n\n            let result_specificity_and_flags = relative_selector_list_specificity_and_flags(\n                &result, /* for_nesting_parent = */ false,\n            );\n            flags.insert(result_specificity_and_flags.flags - forbidden_flags);\n            *specificity += Specificity::from(\n                result_specificity_and_flags.specificity\n                    - relative_selector_list_specificity_and_flags(\n                        orig, /* for_nesting_parent = */ false,\n                    )\n                    .specificity,\n            );\n            result\n        }\n\n        fn replace_parent_on_selector<Impl: SelectorImpl>(\n            orig: &Selector<Impl>,\n            parent: &SelectorList<Impl>,\n            specificity: &mut Specificity,\n            flags: &mut SelectorFlags,\n            forbidden_flags: SelectorFlags,\n        ) -> Selector<Impl> {\n            let new_selector = orig.replace_parent_selector(parent);\n            *specificity += Specificity::from(new_selector.specificity() - orig.specificity());\n            flags.insert(new_selector.flags() - forbidden_flags);\n            new_selector\n        }\n\n        if !self.has_parent_selector() {\n            return self.clone();\n        }\n\n        let iter = self.iter_raw_match_order().map(|component| {\n            use self::Component::*;\n            match *component {\n                LocalName(..)\n                | ID(..)\n                | Class(..)\n                | AttributeInNoNamespaceExists { .. }\n                | AttributeInNoNamespace { .. }\n                | AttributeOther(..)\n                | ExplicitUniversalType\n                | ExplicitAnyNamespace\n                | ExplicitNoNamespace\n                | DefaultNamespace(..)\n                | Namespace(..)\n                | Root\n                | Empty\n                | Scope\n                | ImplicitScope\n                | Nth(..)\n                | NonTSPseudoClass(..)\n                | PseudoElement(..)\n                | Combinator(..)\n                | Host(None)\n                | Part(..)\n                | Invalid(..)\n                | RelativeSelectorAnchor => component.clone(),\n                ParentSelector => {\n                    specificity += Specificity::from(parent_specificity_and_flags.specificity);\n                    flags.insert(parent_specificity_and_flags.flags - forbidden_flags);\n                    Is(parent.clone())\n                },\n                Negation(ref selectors) => {\n                    Negation(\n                        replace_parent_on_selector_list(\n                            selectors.slice(),\n                            parent,\n                            &mut specificity,\n                            &mut flags,\n                            /* propagate_specificity = */ true,\n                            forbidden_flags,\n                        )\n                        .unwrap_or_else(|| selectors.clone()),\n                    )\n                },\n                Is(ref selectors) => {\n                    Is(replace_parent_on_selector_list(\n                        selectors.slice(),\n                        parent,\n                        &mut specificity,\n                        &mut flags,\n                        /* propagate_specificity = */ true,\n                        forbidden_flags,\n                    )\n                    .unwrap_or_else(|| selectors.clone()))\n                },\n                Where(ref selectors) => {\n                    Where(\n                        replace_parent_on_selector_list(\n                            selectors.slice(),\n                            parent,\n                            &mut specificity,\n                            &mut flags,\n                            /* propagate_specificity = */ false,\n                            forbidden_flags,\n                        )\n                        .unwrap_or_else(|| selectors.clone()),\n                    )\n                },\n                Has(ref selectors) => Has(replace_parent_on_relative_selector_list(\n                    selectors,\n                    parent,\n                    &mut specificity,\n                    &mut flags,\n                    forbidden_flags,\n                )\n                .into_boxed_slice()),\n\n                Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(\n                    selector,\n                    parent,\n                    &mut specificity,\n                    &mut flags,\n                    forbidden_flags,\n                ))),\n                NthOf(ref data) => {\n                    let selectors = replace_parent_on_selector_list(\n                        data.selectors(),\n                        parent,\n                        &mut specificity,\n                        &mut flags,\n                        /* propagate_specificity = */ true,\n                        forbidden_flags,\n                    );\n                    NthOf(match selectors {\n                        Some(s) => {\n                            NthOfSelectorData::new(data.nth_data(), s.slice().iter().cloned())\n                        },\n                        None => data.clone(),\n                    })\n                },\n                Slotted(ref selector) => Slotted(replace_parent_on_selector(\n                    selector,\n                    parent,\n                    &mut specificity,\n                    &mut flags,\n                    forbidden_flags,\n                )),\n            }\n        });\n        let mut items = UniqueArc::from_header_and_iter(Default::default(), iter);\n        *items.header_mut() = SpecificityAndFlags {\n            specificity: specificity.into(),\n            flags,\n        };\n        Selector(items.shareable())\n    }\n\n    /// Returns count of simple selectors and combinators in the Selector.\n    #[inline]\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    /// Returns the address on the heap of the ThinArc for memory reporting.\n    pub fn thin_arc_heap_ptr(&self) -> *const ::std::os::raw::c_void {\n        self.0.heap_ptr()\n    }\n\n    /// Traverse selector components inside `self`.\n    ///\n    /// Implementations of this method should call `SelectorVisitor` methods\n    /// or other impls of `Visit` as appropriate based on the fields of `Self`.\n    ///\n    /// A return value of `false` indicates terminating the traversal.\n    /// It should be propagated with an early return.\n    /// On the contrary, `true` indicates that all fields of `self` have been traversed:\n    ///\n    /// ```rust,ignore\n    /// if !visitor.visit_simple_selector(&self.some_simple_selector) {\n    ///     return false;\n    /// }\n    /// if !self.some_component.visit(visitor) {\n    ///     return false;\n    /// }\n    /// true\n    /// ```\n    pub fn visit<V>(&self, visitor: &mut V) -> bool\n    where\n        V: SelectorVisitor<Impl = Impl>,\n    {\n        let mut current = self.iter();\n        let mut combinator = None;\n        loop {\n            if !visitor.visit_complex_selector(combinator) {\n                return false;\n            }\n\n            for selector in &mut current {\n                if !selector.visit(visitor) {\n                    return false;\n                }\n            }\n\n            combinator = current.next_sequence();\n            if combinator.is_none() {\n                break;\n            }\n        }\n\n        true\n    }\n\n    /// Parse a selector, without any pseudo-element.\n    #[inline]\n    pub fn parse<'i, 't, P>(\n        parser: &P,\n        input: &mut CssParser<'i, 't>,\n    ) -> Result<Self, ParseError<'i, P::Error>>\n    where\n        P: Parser<'i, Impl = Impl>,\n    {\n        parse_selector(\n            parser,\n            input,\n            SelectorParsingState::empty(),\n            ParseRelative::No,\n        )\n    }\n\n    pub fn new_invalid(s: &str) -> Self {\n        fn check_for_parent(input: &mut CssParser, has_parent: &mut bool) {\n            while let Ok(t) = input.next() {\n                match *t {\n                    Token::Function(_)\n                    | Token::ParenthesisBlock\n                    | Token::CurlyBracketBlock\n                    | Token::SquareBracketBlock => {\n                        let _ = input.parse_nested_block(\n                            |i| -> Result<(), ParseError<'_, BasicParseError>> {\n                                check_for_parent(i, has_parent);\n                                Ok(())\n                            },\n                        );\n                    },\n                    Token::Delim('&') => {\n                        *has_parent = true;\n                    },\n                    _ => {},\n                }\n                if *has_parent {\n                    break;\n                }\n            }\n        }\n        let mut has_parent = false;\n        {\n            let mut parser = cssparser::ParserInput::new(s);\n            let mut parser = CssParser::new(&mut parser);\n            check_for_parent(&mut parser, &mut has_parent);\n        }\n        Self(ThinArc::from_header_and_iter(\n            SpecificityAndFlags {\n                specificity: 0,\n                flags: if has_parent {\n                    SelectorFlags::HAS_PARENT\n                } else {\n                    SelectorFlags::empty()\n                },\n            },\n            std::iter::once(Component::Invalid(Arc::new(String::from(s.trim())))),\n        ))\n    }\n\n    /// Is the compound starting at the offset the subject compound, or referring to its pseudo-element?\n    pub fn is_rightmost(&self, offset: usize) -> bool {\n        // There can really be only one pseudo-element, and it's not really valid for anything else to\n        // follow it.\n        offset == 0\n            || matches!(\n                self.combinator_at_match_order(offset - 1),\n                Combinator::PseudoElement\n            )\n    }\n}\n\n#[derive(Clone)]\npub struct SelectorIter<'a, Impl: 'a + SelectorImpl> {\n    iter: slice::Iter<'a, Component<Impl>>,\n    next_combinator: Option<Combinator>,\n}\n\nimpl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {\n    /// Prepares this iterator to point to the next sequence to the left,\n    /// returning the combinator if the sequence was found.\n    #[inline]\n    pub fn next_sequence(&mut self) -> Option<Combinator> {\n        self.next_combinator.take()\n    }\n\n    #[inline]\n    pub(crate) fn matches_for_stateless_pseudo_element(&mut self) -> bool {\n        let first = match self.next() {\n            Some(c) => c,\n            // Note that this is the common path that we keep inline: the\n            // pseudo-element not having anything to its right.\n            None => return true,\n        };\n        self.matches_for_stateless_pseudo_element_internal(first)\n    }\n\n    #[inline(never)]\n    fn matches_for_stateless_pseudo_element_internal(&mut self, first: &Component<Impl>) -> bool {\n        if !first.matches_for_stateless_pseudo_element() {\n            return false;\n        }\n        for component in self {\n            // The only other parser-allowed Components in this sequence are\n            // state pseudo-classes, or one of the other things that can contain\n            // them.\n            if !component.matches_for_stateless_pseudo_element() {\n                return false;\n            }\n        }\n        true\n    }\n\n    /// Returns remaining count of the simple selectors and combinators in the Selector.\n    #[inline]\n    pub fn selector_length(&self) -> usize {\n        self.iter.len()\n    }\n}\n\nimpl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> {\n    type Item = &'a Component<Impl>;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        debug_assert!(\n            self.next_combinator.is_none(),\n            \"You should call next_sequence!\"\n        );\n        match *self.iter.next()? {\n            Component::Combinator(c) => {\n                self.next_combinator = Some(c);\n                None\n            },\n            ref x => Some(x),\n        }\n    }\n}\n\nimpl<'a, Impl: SelectorImpl> fmt::Debug for SelectorIter<'a, Impl> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let iter = self.iter.clone().rev();\n        for component in iter {\n            component.to_css(f)?\n        }\n        Ok(())\n    }\n}\n\n/// An iterator over all combinators in a selector. Does not traverse selectors within psuedoclasses.\nstruct CombinatorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>);\nimpl<'a, Impl: 'a + SelectorImpl> CombinatorIter<'a, Impl> {\n    fn new(inner: SelectorIter<'a, Impl>) -> Self {\n        let mut result = CombinatorIter(inner);\n        result.consume_non_combinators();\n        result\n    }\n\n    fn consume_non_combinators(&mut self) {\n        while self.0.next().is_some() {}\n    }\n}\n\nimpl<'a, Impl: SelectorImpl> Iterator for CombinatorIter<'a, Impl> {\n    type Item = Combinator;\n    fn next(&mut self) -> Option<Self::Item> {\n        let result = self.0.next_sequence();\n        self.consume_non_combinators();\n        result\n    }\n}\n\n/// An iterator over all simple selectors belonging to ancestors.\nstruct AncestorIter<'a, Impl: 'a + SelectorImpl>(SelectorIter<'a, Impl>);\nimpl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {\n    /// Creates an AncestorIter. The passed-in iterator is assumed to point to\n    /// the beginning of the child sequence, which will be skipped.\n    fn new(inner: SelectorIter<'a, Impl>) -> Self {\n        let mut result = AncestorIter(inner);\n        result.skip_until_ancestor();\n        result\n    }\n\n    /// Skips a sequence of simple selectors and all subsequent sequences until\n    /// a non-pseudo-element ancestor combinator is reached.\n    fn skip_until_ancestor(&mut self) {\n        loop {\n            while self.0.next().is_some() {}\n            // If this is ever changed to stop at the \"pseudo-element\"\n            // combinator, we will need to fix the way we compute hashes for\n            // revalidation selectors.\n            if self.0.next_sequence().map_or(true, |x| {\n                matches!(x, Combinator::Child | Combinator::Descendant)\n            }) {\n                break;\n            }\n        }\n    }\n}\n\nimpl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> {\n    type Item = &'a Component<Impl>;\n    fn next(&mut self) -> Option<Self::Item> {\n        // Grab the next simple selector in the sequence if available.\n        let next = self.0.next();\n        if next.is_some() {\n            return next;\n        }\n\n        // See if there are more sequences. If so, skip any non-ancestor sequences.\n        if let Some(combinator) = self.0.next_sequence() {\n            if !matches!(combinator, Combinator::Child | Combinator::Descendant) {\n                self.skip_until_ancestor();\n            }\n        }\n\n        self.0.next()\n    }\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum Combinator {\n    Child,        //  >\n    Descendant,   // space\n    NextSibling,  // +\n    LaterSibling, // ~\n    /// A dummy combinator we use to the left of pseudo-elements.\n    ///\n    /// It serializes as the empty string, and acts effectively as a child\n    /// combinator in most cases.  If we ever actually start using a child\n    /// combinator for this, we will need to fix up the way hashes are computed\n    /// for revalidation selectors.\n    PseudoElement,\n    /// Another combinator used for ::slotted(), which represent the jump from\n    /// a node to its assigned slot.\n    SlotAssignment,\n    /// Another combinator used for `::part()`, which represents the jump from\n    /// the part to the containing shadow host.\n    Part,\n}\n\nimpl Combinator {\n    /// Returns true if this combinator is a child or descendant combinator.\n    #[inline]\n    pub fn is_ancestor(&self) -> bool {\n        matches!(\n            *self,\n            Combinator::Child\n                | Combinator::Descendant\n                | Combinator::PseudoElement\n                | Combinator::SlotAssignment\n        )\n    }\n\n    /// Returns true if this combinator is a pseudo-element combinator.\n    #[inline]\n    pub fn is_pseudo_element(&self) -> bool {\n        matches!(*self, Combinator::PseudoElement)\n    }\n\n    /// Returns true if this combinator is a next- or later-sibling combinator.\n    #[inline]\n    pub fn is_sibling(&self) -> bool {\n        matches!(*self, Combinator::NextSibling | Combinator::LaterSibling)\n    }\n}\n\n/// An enum for the different types of :nth- pseudoclasses\n#[derive(Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub enum NthType {\n    Child,\n    LastChild,\n    OnlyChild,\n    OfType,\n    LastOfType,\n    OnlyOfType,\n}\n\nimpl NthType {\n    pub fn is_only(self) -> bool {\n        self == Self::OnlyChild || self == Self::OnlyOfType\n    }\n\n    pub fn is_of_type(self) -> bool {\n        self == Self::OfType || self == Self::LastOfType || self == Self::OnlyOfType\n    }\n\n    pub fn is_from_end(self) -> bool {\n        self == Self::LastChild || self == Self::LastOfType\n    }\n}\n\n/// The properties that comprise an An+B syntax\n#[derive(Copy, Clone, Eq, PartialEq, Debug)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct AnPlusB(pub i32, pub i32);\n\nimpl AnPlusB {\n    #[inline]\n    pub fn matches_index(&self, i: i32) -> bool {\n        // Is there a non-negative integer n such that An+B=i?\n        match i.checked_sub(self.1) {\n            None => false,\n            Some(an) => match an.checked_div(self.0) {\n                Some(n) => n >= 0 && self.0 * n == an,\n                None /* a == 0 */ => an == 0,\n            },\n        }\n    }\n}\n\nimpl ToCss for AnPlusB {\n    /// Serialize <an+b> (part of the CSS Syntax spec).\n    /// <https://drafts.csswg.org/css-syntax-3/#serialize-an-anb-value>\n    #[inline]\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match (self.0, self.1) {\n            (0, 0) => dest.write_char('0'),\n\n            (1, 0) => dest.write_char('n'),\n            (-1, 0) => dest.write_str(\"-n\"),\n            (_, 0) => write!(dest, \"{}n\", self.0),\n\n            (0, _) => write!(dest, \"{}\", self.1),\n            (1, _) => write!(dest, \"n{:+}\", self.1),\n            (-1, _) => write!(dest, \"-n{:+}\", self.1),\n            (_, _) => write!(dest, \"{}n{:+}\", self.0, self.1),\n        }\n    }\n}\n\n/// The properties that comprise an :nth- pseudoclass as of Selectors 3 (e.g.,\n/// nth-child(An+B)).\n/// https://www.w3.org/TR/selectors-3/#nth-child-pseudo\n#[derive(Copy, Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct NthSelectorData {\n    pub ty: NthType,\n    pub is_function: bool,\n    pub an_plus_b: AnPlusB,\n}\n\nimpl NthSelectorData {\n    /// Returns selector data for :only-{child,of-type}\n    #[inline]\n    pub const fn only(of_type: bool) -> Self {\n        Self {\n            ty: if of_type {\n                NthType::OnlyOfType\n            } else {\n                NthType::OnlyChild\n            },\n            is_function: false,\n            an_plus_b: AnPlusB(0, 1),\n        }\n    }\n\n    /// Returns selector data for :first-{child,of-type}\n    #[inline]\n    pub const fn first(of_type: bool) -> Self {\n        Self {\n            ty: if of_type {\n                NthType::OfType\n            } else {\n                NthType::Child\n            },\n            is_function: false,\n            an_plus_b: AnPlusB(0, 1),\n        }\n    }\n\n    /// Returns selector data for :last-{child,of-type}\n    #[inline]\n    pub const fn last(of_type: bool) -> Self {\n        Self {\n            ty: if of_type {\n                NthType::LastOfType\n            } else {\n                NthType::LastChild\n            },\n            is_function: false,\n            an_plus_b: AnPlusB(0, 1),\n        }\n    }\n\n    /// Returns true if this is an edge selector that is not `:*-of-type``\n    #[inline]\n    pub fn is_simple_edge(&self) -> bool {\n        self.an_plus_b.0 == 0\n            && self.an_plus_b.1 == 1\n            && !self.ty.is_of_type()\n            && !self.ty.is_only()\n    }\n\n    /// Writes the beginning of the selector.\n    #[inline]\n    fn write_start<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {\n        dest.write_str(match self.ty {\n            NthType::Child if self.is_function => \":nth-child(\",\n            NthType::Child => \":first-child\",\n            NthType::LastChild if self.is_function => \":nth-last-child(\",\n            NthType::LastChild => \":last-child\",\n            NthType::OfType if self.is_function => \":nth-of-type(\",\n            NthType::OfType => \":first-of-type\",\n            NthType::LastOfType if self.is_function => \":nth-last-of-type(\",\n            NthType::LastOfType => \":last-of-type\",\n            NthType::OnlyChild => \":only-child\",\n            NthType::OnlyOfType => \":only-of-type\",\n        })\n    }\n\n    #[inline]\n    fn write_affine<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {\n        self.an_plus_b.to_css(dest)\n    }\n}\n\n/// The properties that comprise an :nth- pseudoclass as of Selectors 4 (e.g.,\n/// nth-child(An+B [of S]?)).\n/// https://www.w3.org/TR/selectors-4/#nth-child-pseudo\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct NthOfSelectorData<Impl: SelectorImpl>(\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] ThinArc<NthSelectorData, Selector<Impl>>,\n);\n\nimpl<Impl: SelectorImpl> NthOfSelectorData<Impl> {\n    /// Returns selector data for :nth-{,last-}{child,of-type}(An+B [of S])\n    #[inline]\n    pub fn new<I>(nth_data: &NthSelectorData, selectors: I) -> Self\n    where\n        I: Iterator<Item = Selector<Impl>> + ExactSizeIterator,\n    {\n        Self(ThinArc::from_header_and_iter(*nth_data, selectors))\n    }\n\n    /// Returns the An+B part of the selector\n    #[inline]\n    pub fn nth_data(&self) -> &NthSelectorData {\n        &self.0.header\n    }\n\n    /// Returns the selector list part of the selector\n    #[inline]\n    pub fn selectors(&self) -> &[Selector<Impl>] {\n        self.0.slice()\n    }\n}\n\n/// Flag indicating where a given relative selector's match would be contained.\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\npub enum RelativeSelectorMatchHint {\n    /// Within this element's subtree.\n    InSubtree,\n    /// Within this element's direct children.\n    InChild,\n    /// This element's next sibling.\n    InNextSibling,\n    /// Within this element's next sibling's subtree.\n    InNextSiblingSubtree,\n    /// Within this element's subsequent siblings.\n    InSibling,\n    /// Across this element's subsequent siblings and their subtrees.\n    InSiblingSubtree,\n}\n\nimpl RelativeSelectorMatchHint {\n    /// Create a new relative selector match hint based on its composition.\n    pub fn new(\n        relative_combinator: Combinator,\n        has_child_or_descendants: bool,\n        has_adjacent_or_next_siblings: bool,\n    ) -> Self {\n        match relative_combinator {\n            Combinator::Descendant => RelativeSelectorMatchHint::InSubtree,\n            Combinator::Child => {\n                if !has_child_or_descendants {\n                    RelativeSelectorMatchHint::InChild\n                } else {\n                    // Technically, for any composition that consists of child combinators only,\n                    // the search space is depth-constrained, but it's probably not worth optimizing for.\n                    RelativeSelectorMatchHint::InSubtree\n                }\n            },\n            Combinator::NextSibling => {\n                if !has_child_or_descendants && !has_adjacent_or_next_siblings {\n                    RelativeSelectorMatchHint::InNextSibling\n                } else if !has_child_or_descendants && has_adjacent_or_next_siblings {\n                    RelativeSelectorMatchHint::InSibling\n                } else if has_child_or_descendants && !has_adjacent_or_next_siblings {\n                    // Match won't cross multiple siblings.\n                    RelativeSelectorMatchHint::InNextSiblingSubtree\n                } else {\n                    RelativeSelectorMatchHint::InSiblingSubtree\n                }\n            },\n            Combinator::LaterSibling => {\n                if !has_child_or_descendants {\n                    RelativeSelectorMatchHint::InSibling\n                } else {\n                    // Even if the match may not cross multiple siblings, we have to look until\n                    // we find a match anyway.\n                    RelativeSelectorMatchHint::InSiblingSubtree\n                }\n            },\n            Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {\n                debug_assert!(false, \"Unexpected relative combinator\");\n                RelativeSelectorMatchHint::InSubtree\n            },\n        }\n    }\n\n    /// Is the match traversal direction towards the descendant of this element (As opposed to siblings)?\n    pub fn is_descendant_direction(&self) -> bool {\n        matches!(*self, Self::InChild | Self::InSubtree)\n    }\n\n    /// Is the match traversal terminated at the next sibling?\n    pub fn is_next_sibling(&self) -> bool {\n        matches!(*self, Self::InNextSibling | Self::InNextSiblingSubtree)\n    }\n\n    /// Does the match involve matching the subtree?\n    pub fn is_subtree(&self) -> bool {\n        matches!(\n            *self,\n            Self::InSubtree | Self::InSiblingSubtree | Self::InNextSiblingSubtree\n        )\n    }\n}\n\n/// Count of combinators in a given relative selector, not traversing selectors of pseudoclasses.\n#[derive(Clone, Copy)]\npub struct RelativeSelectorCombinatorCount {\n    relative_combinator: Combinator,\n    pub child_or_descendants: usize,\n    pub adjacent_or_next_siblings: usize,\n}\n\nimpl RelativeSelectorCombinatorCount {\n    /// Create a new relative selector combinator count from a given relative selector.\n    pub fn new<Impl: SelectorImpl>(relative_selector: &RelativeSelector<Impl>) -> Self {\n        let mut result = RelativeSelectorCombinatorCount {\n            relative_combinator: relative_selector.selector.combinator_at_parse_order(1),\n            child_or_descendants: 0,\n            adjacent_or_next_siblings: 0,\n        };\n\n        for combinator in CombinatorIter::new(\n            relative_selector\n                .selector\n                .iter_skip_relative_selector_anchor(),\n        ) {\n            match combinator {\n                Combinator::Descendant | Combinator::Child => {\n                    result.child_or_descendants += 1;\n                },\n                Combinator::NextSibling | Combinator::LaterSibling => {\n                    result.adjacent_or_next_siblings += 1;\n                },\n                Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {\n                    continue;\n                },\n            };\n        }\n        result\n    }\n\n    /// Get the match hint based on the current combinator count.\n    pub fn get_match_hint(&self) -> RelativeSelectorMatchHint {\n        RelativeSelectorMatchHint::new(\n            self.relative_combinator,\n            self.child_or_descendants != 0,\n            self.adjacent_or_next_siblings != 0,\n        )\n    }\n}\n\n/// Storage for a relative selector.\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct RelativeSelector<Impl: SelectorImpl> {\n    /// Match space constraining hint.\n    pub match_hint: RelativeSelectorMatchHint,\n    /// The selector. Guaranteed to contain `RelativeSelectorAnchor` and the relative combinator in parse order.\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    pub selector: Selector<Impl>,\n}\n\nbitflags! {\n    /// Composition of combinators in a given selector, not traversing selectors of pseudoclasses.\n    #[derive(Clone, Debug, Eq, PartialEq)]\n    struct CombinatorComposition: u8 {\n        const DESCENDANTS = 1 << 0;\n        const SIBLINGS = 1 << 1;\n    }\n}\n\nimpl CombinatorComposition {\n    fn for_relative_selector<Impl: SelectorImpl>(inner_selector: &Selector<Impl>) -> Self {\n        let mut result = CombinatorComposition::empty();\n        for combinator in CombinatorIter::new(inner_selector.iter_skip_relative_selector_anchor()) {\n            match combinator {\n                Combinator::Descendant | Combinator::Child => {\n                    result.insert(Self::DESCENDANTS);\n                },\n                Combinator::NextSibling | Combinator::LaterSibling => {\n                    result.insert(Self::SIBLINGS);\n                },\n                Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => {\n                    continue;\n                },\n            };\n            if result.is_all() {\n                break;\n            }\n        }\n        return result;\n    }\n}\n\nimpl<Impl: SelectorImpl> RelativeSelector<Impl> {\n    fn from_selector_list(selector_list: SelectorList<Impl>) -> Box<[Self]> {\n        selector_list\n            .slice()\n            .iter()\n            .map(|selector| {\n                // It's more efficient to keep track of all this during the parse time, but that seems like a lot of special\n                // case handling for what it's worth.\n                if cfg!(debug_assertions) {\n                    let relative_selector_anchor = selector.iter_raw_parse_order_from(0).next();\n                    debug_assert!(\n                        relative_selector_anchor.is_some(),\n                        \"Relative selector is empty\"\n                    );\n                    debug_assert!(\n                        matches!(\n                            relative_selector_anchor.unwrap(),\n                            Component::RelativeSelectorAnchor\n                        ),\n                        \"Relative selector anchor is missing\"\n                    );\n                }\n                // Leave a hint for narrowing down the search space when we're matching.\n                let composition = CombinatorComposition::for_relative_selector(&selector);\n                let match_hint = RelativeSelectorMatchHint::new(\n                    selector.combinator_at_parse_order(1),\n                    composition.intersects(CombinatorComposition::DESCENDANTS),\n                    composition.intersects(CombinatorComposition::SIBLINGS),\n                );\n                RelativeSelector {\n                    match_hint,\n                    selector: selector.clone(),\n                }\n            })\n            .collect()\n    }\n}\n\n/// A CSS simple selector or combinator. We store both in the same enum for\n/// optimal packing and cache performance, see [1].\n///\n/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1357973\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub enum Component<Impl: SelectorImpl> {\n    LocalName(LocalName<Impl>),\n\n    ID(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::Identifier),\n    Class(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::Identifier),\n\n    AttributeInNoNamespaceExists {\n        #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n        local_name: Impl::LocalName,\n        local_name_lower: Impl::LocalName,\n    },\n    // Used only when local_name is already lowercase.\n    AttributeInNoNamespace {\n        local_name: Impl::LocalName,\n        operator: AttrSelectorOperator,\n        #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n        value: Impl::AttrValue,\n        case_sensitivity: ParsedCaseSensitivity,\n    },\n    // Use a Box in the less common cases with more data to keep size_of::<Component>() small.\n    AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),\n\n    ExplicitUniversalType,\n    ExplicitAnyNamespace,\n\n    ExplicitNoNamespace,\n    DefaultNamespace(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::NamespaceUrl),\n    Namespace(\n        #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::NamespacePrefix,\n        #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::NamespaceUrl,\n    ),\n\n    /// Pseudo-classes\n    Negation(SelectorList<Impl>),\n    Root,\n    Empty,\n    Scope,\n    /// :scope added implicitly into scoped rules (i.e. In `@scope`) not\n    /// explicitly using `:scope` or `&` selectors.\n    ///\n    /// https://drafts.csswg.org/css-cascade-6/#scoped-rules\n    ///\n    /// Unlike the normal `:scope` selector, this does not add any specificity.\n    /// See https://github.com/w3c/csswg-drafts/issues/10196\n    ImplicitScope,\n    ParentSelector,\n    Nth(NthSelectorData),\n    NthOf(NthOfSelectorData<Impl>),\n    NonTSPseudoClass(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::NonTSPseudoClass),\n    /// The ::slotted() pseudo-element:\n    ///\n    /// https://drafts.csswg.org/css-scoping/#slotted-pseudo\n    ///\n    /// The selector here is a compound selector, that is, no combinators.\n    ///\n    /// NOTE(emilio): This should support a list of selectors, but as of this\n    /// writing no other browser does, and that allows them to put ::slotted()\n    /// in the rule hash, so we do that too.\n    ///\n    /// See https://github.com/w3c/csswg-drafts/issues/2158\n    Slotted(Selector<Impl>),\n    /// The `::part` pseudo-element.\n    ///   https://drafts.csswg.org/css-shadow-parts/#part\n    Part(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Box<[Impl::Identifier]>),\n    /// The `:host` pseudo-class:\n    ///\n    /// https://drafts.csswg.org/css-scoping/#host-selector\n    ///\n    /// NOTE(emilio): This should support a list of selectors, but as of this\n    /// writing no other browser does, and that allows them to put :host()\n    /// in the rule hash, so we do that too.\n    ///\n    /// See https://github.com/w3c/csswg-drafts/issues/2158\n    Host(Option<Selector<Impl>>),\n    /// The `:where` pseudo-class.\n    ///\n    /// https://drafts.csswg.org/selectors/#zero-matches\n    ///\n    /// The inner argument is conceptually a SelectorList, but we move the\n    /// selectors to the heap to keep Component small.\n    Where(SelectorList<Impl>),\n    /// The `:is` pseudo-class.\n    ///\n    /// https://drafts.csswg.org/selectors/#matches-pseudo\n    ///\n    /// Same comment as above re. the argument.\n    Is(SelectorList<Impl>),\n    /// The `:has` pseudo-class.\n    ///\n    /// https://drafts.csswg.org/selectors/#has-pseudo\n    ///\n    /// Same comment as above re. the argument.\n    Has(Box<[RelativeSelector<Impl>]>),\n    /// An invalid selector inside :is() / :where().\n    Invalid(Arc<String>),\n    /// An implementation-dependent pseudo-element selector.\n    PseudoElement(#[cfg_attr(feature = \"to_shmem\", shmem(field_bound))] Impl::PseudoElement),\n\n    Combinator(Combinator),\n\n    /// Used only for relative selectors, which starts with a combinator\n    /// (With an implied descendant combinator if not specified).\n    ///\n    /// https://drafts.csswg.org/selectors-4/#typedef-relative-selector\n    RelativeSelectorAnchor,\n}\n\nimpl<Impl: SelectorImpl> Component<Impl> {\n    /// Returns true if this is a combinator.\n    #[inline]\n    pub fn is_combinator(&self) -> bool {\n        matches!(*self, Component::Combinator(_))\n    }\n\n    /// Returns true if this is a :host() selector.\n    #[inline]\n    pub fn is_host(&self) -> bool {\n        matches!(*self, Component::Host(..))\n    }\n\n    /// Returns the value as a combinator if applicable, None otherwise.\n    pub fn as_combinator(&self) -> Option<Combinator> {\n        match *self {\n            Component::Combinator(c) => Some(c),\n            _ => None,\n        }\n    }\n\n    /// Whether a given selector (to the right of a pseudo-element) should match for stateless\n    /// pseudo-elements. Note that generally nothing matches for those, but since we have :not(),\n    /// we still need to traverse nested selector lists.\n    fn matches_for_stateless_pseudo_element(&self) -> bool {\n        match *self {\n            Component::Negation(ref selectors) => !selectors.slice().iter().all(|selector| {\n                selector\n                    .iter_raw_match_order()\n                    .all(|c| c.matches_for_stateless_pseudo_element())\n            }),\n            Component::Is(ref selectors) | Component::Where(ref selectors) => {\n                selectors.slice().iter().any(|selector| {\n                    selector\n                        .iter_raw_match_order()\n                        .all(|c| c.matches_for_stateless_pseudo_element())\n                })\n            },\n            _ => false,\n        }\n    }\n\n    pub fn visit<V>(&self, visitor: &mut V) -> bool\n    where\n        V: SelectorVisitor<Impl = Impl>,\n    {\n        use self::Component::*;\n        if !visitor.visit_simple_selector(self) {\n            return false;\n        }\n\n        match *self {\n            Slotted(ref selector) => {\n                if !selector.visit(visitor) {\n                    return false;\n                }\n            },\n            Host(Some(ref selector)) => {\n                if !selector.visit(visitor) {\n                    return false;\n                }\n            },\n            AttributeInNoNamespaceExists {\n                ref local_name,\n                ref local_name_lower,\n            } => {\n                if !visitor.visit_attribute_selector(\n                    &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),\n                    local_name,\n                    local_name_lower,\n                ) {\n                    return false;\n                }\n            },\n            AttributeInNoNamespace { ref local_name, .. } => {\n                if !visitor.visit_attribute_selector(\n                    &NamespaceConstraint::Specific(&namespace_empty_string::<Impl>()),\n                    local_name,\n                    local_name,\n                ) {\n                    return false;\n                }\n            },\n            AttributeOther(ref attr_selector) => {\n                let empty_string;\n                let namespace = match attr_selector.namespace() {\n                    Some(ns) => ns,\n                    None => {\n                        empty_string = crate::parser::namespace_empty_string::<Impl>();\n                        NamespaceConstraint::Specific(&empty_string)\n                    },\n                };\n                if !visitor.visit_attribute_selector(\n                    &namespace,\n                    &attr_selector.local_name,\n                    &attr_selector.local_name_lower,\n                ) {\n                    return false;\n                }\n            },\n\n            NonTSPseudoClass(ref pseudo_class) => {\n                if !pseudo_class.visit(visitor) {\n                    return false;\n                }\n            },\n            Negation(ref list) | Is(ref list) | Where(ref list) => {\n                let list_kind = SelectorListKind::from_component(self);\n                debug_assert!(!list_kind.is_empty());\n                if !visitor.visit_selector_list(list_kind, list.slice()) {\n                    return false;\n                }\n            },\n            NthOf(ref nth_of_data) => {\n                if !visitor.visit_selector_list(SelectorListKind::NTH_OF, nth_of_data.selectors()) {\n                    return false;\n                }\n            },\n            Has(ref list) => {\n                if !visitor.visit_relative_selector_list(list) {\n                    return false;\n                }\n            },\n            _ => {},\n        }\n\n        true\n    }\n\n    // Returns true if this has any selector that requires an index calculation. e.g.\n    // :nth-child, :first-child, etc. For nested selectors, return true only if the\n    // indexed selector is in its subject compound.\n    pub fn has_indexed_selector_in_subject(&self) -> bool {\n        match *self {\n            Component::NthOf(..) | Component::Nth(..) => return true,\n            Component::Is(ref selectors)\n            | Component::Where(ref selectors)\n            | Component::Negation(ref selectors) => {\n                // Check the subject compound.\n                for selector in selectors.slice() {\n                    let mut iter = selector.iter();\n                    while let Some(c) = iter.next() {\n                        if c.has_indexed_selector_in_subject() {\n                            return true;\n                        }\n                    }\n                }\n            },\n            _ => (),\n        };\n        false\n    }\n}\n\n#[derive(Clone, Eq, PartialEq)]\n#[cfg_attr(feature = \"to_shmem\", derive(ToShmem))]\n#[cfg_attr(feature = \"to_shmem\", shmem(no_bounds))]\npub struct LocalName<Impl: SelectorImpl> {\n    #[cfg_attr(feature = \"to_shmem\", shmem(field_bound))]\n    pub name: Impl::LocalName,\n    pub lower_name: Impl::LocalName,\n}\n\nimpl<Impl: SelectorImpl> Debug for Selector<Impl> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"Selector(\")?;\n        self.to_css(f)?;\n        write!(\n            f,\n            \", specificity = {:#x}, flags = {:?})\",\n            self.specificity(),\n            self.flags()\n        )\n    }\n}\n\nimpl<Impl: SelectorImpl> Debug for Component<Impl> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.to_css(f)\n    }\n}\nimpl<Impl: SelectorImpl> Debug for AttrSelectorWithOptionalNamespace<Impl> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.to_css(f)\n    }\n}\nimpl<Impl: SelectorImpl> Debug for LocalName<Impl> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.to_css(f)\n    }\n}\n\nfn serialize_selector_list<'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result\nwhere\n    Impl: SelectorImpl,\n    I: Iterator<Item = &'a Selector<Impl>>,\n    W: fmt::Write,\n{\n    let mut first = true;\n    for selector in iter {\n        if !first {\n            dest.write_str(\", \")?;\n        }\n        first = false;\n        selector.to_css(dest)?;\n    }\n    Ok(())\n}\n\nimpl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        serialize_selector_list(self.slice().iter(), dest)\n    }\n}\n\nimpl<Impl: SelectorImpl> ToCss for Selector<Impl> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        // Compound selectors invert the order of their contents, so we need to\n        // undo that during serialization.\n        //\n        // This two-iterator strategy involves walking over the selector twice.\n        // We could do something more clever, but selector serialization probably\n        // isn't hot enough to justify it, and the stringification likely\n        // dominates anyway.\n        //\n        // NB: A parse-order iterator is a Rev<>, which doesn't expose as_slice(),\n        // which we need for |split|. So we split by combinators on a match-order\n        // sequence and then reverse.\n\n        let mut combinators = self\n            .iter_raw_match_order()\n            .rev()\n            .filter_map(|x| x.as_combinator());\n        let compound_selectors = self\n            .iter_raw_match_order()\n            .as_slice()\n            .split(|x| x.is_combinator())\n            .rev();\n\n        let mut combinators_exhausted = false;\n        for compound in compound_selectors {\n            debug_assert!(!combinators_exhausted);\n\n            // https://drafts.csswg.org/cssom/#serializing-selectors\n            let first_compound = match compound.first() {\n                None => continue,\n                Some(c) => c,\n            };\n            if matches!(\n                first_compound,\n                Component::RelativeSelectorAnchor | Component::ImplicitScope\n            ) {\n                debug_assert!(\n                    compound.len() == 1,\n                    \"RelativeSelectorAnchor/ImplicitScope should only be a simple selector\"\n                );\n                if let Some(c) = combinators.next() {\n                    c.to_css_relative(dest)?;\n                } else {\n                    // Direct property declarations in `@scope` does not have\n                    // combinators, since its selector is `:implicit-scope`.\n                    debug_assert!(\n                        matches!(first_compound, Component::ImplicitScope),\n                        \"Only implicit :scope may not have any combinator\"\n                    );\n                }\n                continue;\n            }\n\n            // 1. If there is only one simple selector in the compound selectors\n            //    which is a universal selector, append the result of\n            //    serializing the universal selector to s.\n            //\n            // Check if `!compound.empty()` first--this can happen if we have\n            // something like `... > ::before`, because we store `>` and `::`\n            // both as combinators internally.\n            //\n            // If we are in this case, after we have serialized the universal\n            // selector, we skip Step 2 and continue with the algorithm.\n            let (can_elide_namespace, first_non_namespace) = match compound[0] {\n                Component::ExplicitAnyNamespace\n                | Component::ExplicitNoNamespace\n                | Component::Namespace(..) => (false, 1),\n                Component::DefaultNamespace(..) => (true, 1),\n                _ => (true, 0),\n            };\n            let mut perform_step_2 = true;\n            let next_combinator = combinators.next();\n            if first_non_namespace == compound.len() - 1 {\n                match (next_combinator, &compound[first_non_namespace]) {\n                    // We have to be careful here, because if there is a\n                    // pseudo element \"combinator\" there isn't really just\n                    // the one simple selector. Technically this compound\n                    // selector contains the pseudo element selector as well\n                    // -- Combinator::PseudoElement, just like\n                    // Combinator::SlotAssignment, don't exist in the\n                    // spec.\n                    (Some(Combinator::PseudoElement), _)\n                    | (Some(Combinator::SlotAssignment), _) => (),\n                    (_, &Component::ExplicitUniversalType) => {\n                        // Iterate over everything so we serialize the namespace\n                        // too.\n                        for simple in compound.iter() {\n                            simple.to_css(dest)?;\n                        }\n                        // Skip step 2, which is an \"otherwise\".\n                        perform_step_2 = false;\n                    },\n                    _ => (),\n                }\n            }\n\n            // 2. Otherwise, for each simple selector in the compound selectors\n            //    that is not a universal selector of which the namespace prefix\n            //    maps to a namespace that is not the default namespace\n            //    serialize the simple selector and append the result to s.\n            //\n            // See https://github.com/w3c/csswg-drafts/issues/1606, which is\n            // proposing to change this to match up with the behavior asserted\n            // in cssom/serialize-namespaced-type-selectors.html, which the\n            // following code tries to match.\n            if perform_step_2 {\n                for simple in compound.iter() {\n                    if let Component::ExplicitUniversalType = *simple {\n                        // Can't have a namespace followed by a pseudo-element\n                        // selector followed by a universal selector in the same\n                        // compound selector, so we don't have to worry about the\n                        // real namespace being in a different `compound`.\n                        if can_elide_namespace {\n                            continue;\n                        }\n                    }\n                    simple.to_css(dest)?;\n                }\n            }\n\n            // 3. If this is not the last part of the chain of the selector\n            //    append a single SPACE (U+0020), followed by the combinator\n            //    \">\", \"+\", \"~\", \">>\", \"||\", as appropriate, followed by another\n            //    single SPACE (U+0020) if the combinator was not whitespace, to\n            //    s.\n            match next_combinator {\n                Some(c) => c.to_css(dest)?,\n                None => combinators_exhausted = true,\n            };\n\n            // 4. If this is the last part of the chain of the selector and\n            //    there is a pseudo-element, append \"::\" followed by the name of\n            //    the pseudo-element, to s.\n            //\n            // (we handle this above)\n        }\n\n        Ok(())\n    }\n}\n\nimpl Combinator {\n    fn to_css_internal<W>(&self, dest: &mut W, prefix_space: bool) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if matches!(\n            *self,\n            Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment\n        ) {\n            return Ok(());\n        }\n        if prefix_space {\n            dest.write_char(' ')?;\n        }\n        match *self {\n            Combinator::Child => dest.write_str(\"> \"),\n            Combinator::Descendant => Ok(()),\n            Combinator::NextSibling => dest.write_str(\"+ \"),\n            Combinator::LaterSibling => dest.write_str(\"~ \"),\n            Combinator::PseudoElement | Combinator::Part | Combinator::SlotAssignment => unsafe {\n                debug_unreachable!(\"Already handled\")\n            },\n        }\n    }\n\n    fn to_css_relative<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.to_css_internal(dest, false)\n    }\n}\n\nimpl ToCss for Combinator {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.to_css_internal(dest, true)\n    }\n}\n\nimpl<Impl: SelectorImpl> ToCss for Component<Impl> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use self::Component::*;\n\n        match *self {\n            Combinator(ref c) => c.to_css(dest),\n            Slotted(ref selector) => {\n                dest.write_str(\"::slotted(\")?;\n                selector.to_css(dest)?;\n                dest.write_char(')')\n            },\n            Part(ref part_names) => {\n                dest.write_str(\"::part(\")?;\n                for (i, name) in part_names.iter().enumerate() {\n                    if i != 0 {\n                        dest.write_char(' ')?;\n                    }\n                    name.to_css(dest)?;\n                }\n                dest.write_char(')')\n            },\n            PseudoElement(ref p) => p.to_css(dest),\n            ID(ref s) => {\n                dest.write_char('#')?;\n                s.to_css(dest)\n            },\n            Class(ref s) => {\n                dest.write_char('.')?;\n                s.to_css(dest)\n            },\n            LocalName(ref s) => s.to_css(dest),\n            ExplicitUniversalType => dest.write_char('*'),\n\n            DefaultNamespace(_) => Ok(()),\n            ExplicitNoNamespace => dest.write_char('|'),\n            ExplicitAnyNamespace => dest.write_str(\"*|\"),\n            Namespace(ref prefix, _) => {\n                prefix.to_css(dest)?;\n                dest.write_char('|')\n            },\n\n            AttributeInNoNamespaceExists { ref local_name, .. } => {\n                dest.write_char('[')?;\n                local_name.to_css(dest)?;\n                dest.write_char(']')\n            },\n            AttributeInNoNamespace {\n                ref local_name,\n                operator,\n                ref value,\n                case_sensitivity,\n                ..\n            } => {\n                dest.write_char('[')?;\n                local_name.to_css(dest)?;\n                operator.to_css(dest)?;\n                value.to_css(dest)?;\n                match case_sensitivity {\n                    ParsedCaseSensitivity::CaseSensitive\n                    | ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {\n                    },\n                    ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(\" i\")?,\n                    ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(\" s\")?,\n                }\n                dest.write_char(']')\n            },\n            AttributeOther(ref attr_selector) => attr_selector.to_css(dest),\n\n            // Pseudo-classes\n            Root => dest.write_str(\":root\"),\n            Empty => dest.write_str(\":empty\"),\n            Scope => dest.write_str(\":scope\"),\n            ParentSelector => dest.write_char('&'),\n            Host(ref selector) => {\n                dest.write_str(\":host\")?;\n                if let Some(ref selector) = *selector {\n                    dest.write_char('(')?;\n                    selector.to_css(dest)?;\n                    dest.write_char(')')?;\n                }\n                Ok(())\n            },\n            Nth(ref nth_data) => {\n                nth_data.write_start(dest)?;\n                if nth_data.is_function {\n                    nth_data.write_affine(dest)?;\n                    dest.write_char(')')?;\n                }\n                Ok(())\n            },\n            NthOf(ref nth_of_data) => {\n                let nth_data = nth_of_data.nth_data();\n                nth_data.write_start(dest)?;\n                debug_assert!(\n                    nth_data.is_function,\n                    \"A selector must be a function to hold An+B notation\"\n                );\n                nth_data.write_affine(dest)?;\n                debug_assert!(\n                    matches!(nth_data.ty, NthType::Child | NthType::LastChild),\n                    \"Only :nth-child or :nth-last-child can be of a selector list\"\n                );\n                debug_assert!(\n                    !nth_of_data.selectors().is_empty(),\n                    \"The selector list should not be empty\"\n                );\n                dest.write_str(\" of \")?;\n                serialize_selector_list(nth_of_data.selectors().iter(), dest)?;\n                dest.write_char(')')\n            },\n            Is(ref list) | Where(ref list) | Negation(ref list) => {\n                match *self {\n                    Where(..) => dest.write_str(\":where(\")?,\n                    Is(..) => dest.write_str(\":is(\")?,\n                    Negation(..) => dest.write_str(\":not(\")?,\n                    _ => unreachable!(),\n                }\n                serialize_selector_list(list.slice().iter(), dest)?;\n                dest.write_str(\")\")\n            },\n            Has(ref list) => {\n                dest.write_str(\":has(\")?;\n                serialize_selector_list(list.iter().map(|rel| &rel.selector), dest)?;\n                dest.write_str(\")\")\n            },\n            NonTSPseudoClass(ref pseudo) => pseudo.to_css(dest),\n            Invalid(ref css) => dest.write_str(css),\n            RelativeSelectorAnchor | ImplicitScope => Ok(()),\n        }\n    }\n}\n\nimpl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        dest.write_char('[')?;\n        match self.namespace {\n            Some(NamespaceConstraint::Specific((ref prefix, _))) => {\n                prefix.to_css(dest)?;\n                dest.write_char('|')?\n            },\n            Some(NamespaceConstraint::Any) => dest.write_str(\"*|\")?,\n            None => {},\n        }\n        self.local_name.to_css(dest)?;\n        match self.operation {\n            ParsedAttrSelectorOperation::Exists => {},\n            ParsedAttrSelectorOperation::WithValue {\n                operator,\n                case_sensitivity,\n                ref value,\n            } => {\n                operator.to_css(dest)?;\n                value.to_css(dest)?;\n                match case_sensitivity {\n                    ParsedCaseSensitivity::CaseSensitive\n                    | ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {\n                    },\n                    ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(\" i\")?,\n                    ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(\" s\")?,\n                }\n            },\n        }\n        dest.write_char(']')\n    }\n}\n\nimpl<Impl: SelectorImpl> ToCss for LocalName<Impl> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.name.to_css(dest)\n    }\n}\n\n/// Build up a Selector.\n/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;\n///\n/// `Err` means invalid selector.\nfn parse_selector<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    mut state: SelectorParsingState,\n    parse_relative: ParseRelative,\n) -> Result<Selector<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    let mut builder = SelectorBuilder::default();\n\n    // Helps rewind less, but also simplifies dealing with relative combinators below.\n    input.skip_whitespace();\n\n    if parse_relative != ParseRelative::No {\n        let combinator = try_parse_combinator(input);\n        match parse_relative {\n            ParseRelative::ForHas => {\n                builder.push_simple_selector(Component::RelativeSelectorAnchor);\n                // Do we see a combinator? If so, push that. Otherwise, push a descendant\n                // combinator.\n                builder.push_combinator(combinator.unwrap_or(Combinator::Descendant));\n            },\n            ParseRelative::ForNesting | ParseRelative::ForScope => {\n                if let Ok(combinator) = combinator {\n                    let selector = match parse_relative {\n                        ParseRelative::ForHas | ParseRelative::No => unreachable!(),\n                        ParseRelative::ForNesting => Component::ParentSelector,\n                        // See https://github.com/w3c/csswg-drafts/issues/10196\n                        // Implicitly added `:scope` does not add specificity\n                        // for non-relative selectors, so do the same.\n                        ParseRelative::ForScope => Component::ImplicitScope,\n                    };\n                    builder.push_simple_selector(selector);\n                    builder.push_combinator(combinator);\n                }\n            },\n            ParseRelative::No => unreachable!(),\n        }\n    }\n    loop {\n        // Parse a sequence of simple selectors.\n        let empty = parse_compound_selector(parser, &mut state, input, &mut builder)?;\n        if empty {\n            return Err(input.new_custom_error(if builder.has_combinators() {\n                SelectorParseErrorKind::DanglingCombinator\n            } else {\n                SelectorParseErrorKind::EmptySelector\n            }));\n        }\n\n        if state.intersects(SelectorParsingState::AFTER_PSEUDO) {\n            debug_assert!(state.intersects(\n                SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO\n                    | SelectorParsingState::AFTER_BEFORE_OR_AFTER_PSEUDO\n                    | SelectorParsingState::AFTER_SLOTTED\n                    | SelectorParsingState::AFTER_PART_LIKE\n            ));\n            break;\n        }\n\n        let combinator = if let Ok(c) = try_parse_combinator(input) {\n            c\n        } else {\n            break;\n        };\n\n        if !state.allows_combinators() {\n            return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n        }\n\n        builder.push_combinator(combinator);\n    }\n    return Ok(Selector(builder.build(parse_relative)));\n}\n\nfn try_parse_combinator<'i, 't>(input: &mut CssParser<'i, 't>) -> Result<Combinator, ()> {\n    let mut any_whitespace = false;\n    loop {\n        let before_this_token = input.state();\n        match input.next_including_whitespace() {\n            Err(_e) => return Err(()),\n            Ok(&Token::WhiteSpace(_)) => any_whitespace = true,\n            Ok(&Token::Delim('>')) => {\n                return Ok(Combinator::Child);\n            },\n            Ok(&Token::Delim('+')) => {\n                return Ok(Combinator::NextSibling);\n            },\n            Ok(&Token::Delim('~')) => {\n                return Ok(Combinator::LaterSibling);\n            },\n            Ok(_) => {\n                input.reset(&before_this_token);\n                if any_whitespace {\n                    return Ok(Combinator::Descendant);\n                } else {\n                    return Err(());\n                }\n            },\n        }\n    }\n}\n\n/// * `Err(())`: Invalid selector, abort\n/// * `Ok(false)`: Not a type selector, could be something else. `input` was not consumed.\n/// * `Ok(true)`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`)\nfn parse_type_selector<'i, 't, P, Impl, S>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n    sink: &mut S,\n) -> Result<bool, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n    S: Push<Component<Impl>>,\n{\n    match parse_qualified_name(parser, input, /* in_attr_selector = */ false) {\n        Err(ParseError {\n            kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),\n            ..\n        })\n        | Ok(OptionalQName::None(_)) => Ok(false),\n        Ok(OptionalQName::Some(namespace, local_name)) => {\n            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {\n                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n            }\n            match namespace {\n                QNamePrefix::ImplicitAnyNamespace => {},\n                QNamePrefix::ImplicitDefaultNamespace(url) => {\n                    sink.push(Component::DefaultNamespace(url))\n                },\n                QNamePrefix::ExplicitNamespace(prefix, url) => {\n                    sink.push(match parser.default_namespace() {\n                        Some(ref default_url) if url == *default_url => {\n                            Component::DefaultNamespace(url)\n                        },\n                        _ => Component::Namespace(prefix, url),\n                    })\n                },\n                QNamePrefix::ExplicitNoNamespace => sink.push(Component::ExplicitNoNamespace),\n                QNamePrefix::ExplicitAnyNamespace => {\n                    match parser.default_namespace() {\n                        // Element type selectors that have no namespace\n                        // component (no namespace separator) represent elements\n                        // without regard to the element's namespace (equivalent\n                        // to \"*|\") unless a default namespace has been declared\n                        // for namespaced selectors (e.g. in CSS, in the style\n                        // sheet). If a default namespace has been declared,\n                        // such selectors will represent only elements in the\n                        // default namespace.\n                        // -- Selectors § 6.1.1\n                        // So we'll have this act the same as the\n                        // QNamePrefix::ImplicitAnyNamespace case.\n                        None => {},\n                        Some(_) => sink.push(Component::ExplicitAnyNamespace),\n                    }\n                },\n                QNamePrefix::ImplicitNoNamespace => {\n                    unreachable!() // Not returned with in_attr_selector = false\n                },\n            }\n            match local_name {\n                Some(name) => sink.push(Component::LocalName(LocalName {\n                    lower_name: to_ascii_lowercase(&name).as_ref().into(),\n                    name: name.as_ref().into(),\n                })),\n                None => sink.push(Component::ExplicitUniversalType),\n            }\n            Ok(true)\n        },\n        Err(e) => Err(e),\n    }\n}\n\n#[derive(Debug)]\nenum SimpleSelectorParseResult<Impl: SelectorImpl> {\n    SimpleSelector(Component<Impl>),\n    PseudoElement(Impl::PseudoElement),\n    SlottedPseudo(Selector<Impl>),\n    PartPseudo(Box<[Impl::Identifier]>),\n}\n\n#[derive(Debug)]\nenum QNamePrefix<Impl: SelectorImpl> {\n    ImplicitNoNamespace,                          // `foo` in attr selectors\n    ImplicitAnyNamespace,                         // `foo` in type selectors, without a default ns\n    ImplicitDefaultNamespace(Impl::NamespaceUrl), // `foo` in type selectors, with a default ns\n    ExplicitNoNamespace,                          // `|foo`\n    ExplicitAnyNamespace,                         // `*|foo`\n    ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), // `prefix|foo`\n}\n\nenum OptionalQName<'i, Impl: SelectorImpl> {\n    Some(QNamePrefix<Impl>, Option<CowRcStr<'i>>),\n    None(Token<'i>),\n}\n\n/// * `Err(())`: Invalid selector, abort\n/// * `Ok(None(token))`: Not a simple selector, could be something else. `input` was not consumed,\n///                      but the token is still returned.\n/// * `Ok(Some(namespace, local_name))`: `None` for the local name means a `*` universal selector\nfn parse_qualified_name<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    in_attr_selector: bool,\n) -> Result<OptionalQName<'i, Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    let default_namespace = |local_name| {\n        let namespace = match parser.default_namespace() {\n            Some(url) => QNamePrefix::ImplicitDefaultNamespace(url),\n            None => QNamePrefix::ImplicitAnyNamespace,\n        };\n        Ok(OptionalQName::Some(namespace, local_name))\n    };\n\n    let explicit_namespace = |input: &mut CssParser<'i, 't>, namespace| {\n        let location = input.current_source_location();\n        match input.next_including_whitespace() {\n            Ok(&Token::Delim('*')) if !in_attr_selector => Ok(OptionalQName::Some(namespace, None)),\n            Ok(&Token::Ident(ref local_name)) => {\n                Ok(OptionalQName::Some(namespace, Some(local_name.clone())))\n            },\n            Ok(t) if in_attr_selector => {\n                let e = SelectorParseErrorKind::InvalidQualNameInAttr(t.clone());\n                Err(location.new_custom_error(e))\n            },\n            Ok(t) => Err(location.new_custom_error(\n                SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t.clone()),\n            )),\n            Err(e) => Err(e.into()),\n        }\n    };\n\n    let start = input.state();\n    match input.next_including_whitespace() {\n        Ok(Token::Ident(value)) => {\n            let value = value.clone();\n            let after_ident = input.state();\n            match input.next_including_whitespace() {\n                Ok(&Token::Delim('|')) => {\n                    let prefix = value.as_ref().into();\n                    let result = parser.namespace_for_prefix(&prefix);\n                    let url = result.ok_or(\n                        after_ident\n                            .source_location()\n                            .new_custom_error(SelectorParseErrorKind::ExpectedNamespace(value)),\n                    )?;\n                    explicit_namespace(input, QNamePrefix::ExplicitNamespace(prefix, url))\n                },\n                _ => {\n                    input.reset(&after_ident);\n                    if in_attr_selector {\n                        Ok(OptionalQName::Some(\n                            QNamePrefix::ImplicitNoNamespace,\n                            Some(value),\n                        ))\n                    } else {\n                        default_namespace(Some(value))\n                    }\n                },\n            }\n        },\n        Ok(Token::Delim('*')) => {\n            let after_star = input.state();\n            match input.next_including_whitespace() {\n                Ok(&Token::Delim('|')) => {\n                    explicit_namespace(input, QNamePrefix::ExplicitAnyNamespace)\n                },\n                _ if !in_attr_selector => {\n                    input.reset(&after_star);\n                    default_namespace(None)\n                },\n                result => {\n                    let t = result?;\n                    Err(after_star\n                        .source_location()\n                        .new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t.clone())))\n                },\n            }\n        },\n        Ok(Token::Delim('|')) => explicit_namespace(input, QNamePrefix::ExplicitNoNamespace),\n        Ok(t) => {\n            let t = t.clone();\n            input.reset(&start);\n            Ok(OptionalQName::None(t))\n        },\n        Err(e) => {\n            input.reset(&start);\n            Err(e.into())\n        },\n    }\n}\n\nfn parse_attribute_selector<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    let namespace;\n    let local_name;\n\n    input.skip_whitespace();\n\n    match parse_qualified_name(parser, input, /* in_attr_selector = */ true)? {\n        OptionalQName::None(t) => {\n            return Err(input.new_custom_error(\n                SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t),\n            ));\n        },\n        OptionalQName::Some(_, None) => unreachable!(),\n        OptionalQName::Some(ns, Some(ln)) => {\n            local_name = ln;\n            namespace = match ns {\n                QNamePrefix::ImplicitNoNamespace | QNamePrefix::ExplicitNoNamespace => None,\n                QNamePrefix::ExplicitNamespace(prefix, url) => {\n                    Some(NamespaceConstraint::Specific((prefix, url)))\n                },\n                QNamePrefix::ExplicitAnyNamespace => Some(NamespaceConstraint::Any),\n                QNamePrefix::ImplicitAnyNamespace | QNamePrefix::ImplicitDefaultNamespace(_) => {\n                    unreachable!() // Not returned with in_attr_selector = true\n                },\n            }\n        },\n    }\n\n    let location = input.current_source_location();\n    let operator = match input.next() {\n        // [foo]\n        Err(_) => {\n            let local_name_lower = to_ascii_lowercase(&local_name).as_ref().into();\n            let local_name = local_name.as_ref().into();\n            if let Some(namespace) = namespace {\n                return Ok(Component::AttributeOther(Box::new(\n                    AttrSelectorWithOptionalNamespace {\n                        namespace: Some(namespace),\n                        local_name,\n                        local_name_lower,\n                        operation: ParsedAttrSelectorOperation::Exists,\n                    },\n                )));\n            } else {\n                return Ok(Component::AttributeInNoNamespaceExists {\n                    local_name,\n                    local_name_lower,\n                });\n            }\n        },\n\n        // [foo=bar]\n        Ok(&Token::Delim('=')) => AttrSelectorOperator::Equal,\n        // [foo~=bar]\n        Ok(&Token::IncludeMatch) => AttrSelectorOperator::Includes,\n        // [foo|=bar]\n        Ok(&Token::DashMatch) => AttrSelectorOperator::DashMatch,\n        // [foo^=bar]\n        Ok(&Token::PrefixMatch) => AttrSelectorOperator::Prefix,\n        // [foo*=bar]\n        Ok(&Token::SubstringMatch) => AttrSelectorOperator::Substring,\n        // [foo$=bar]\n        Ok(&Token::SuffixMatch) => AttrSelectorOperator::Suffix,\n        Ok(t) => {\n            return Err(location.new_custom_error(\n                SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(t.clone()),\n            ));\n        },\n    };\n\n    let value = match input.expect_ident_or_string() {\n        Ok(t) => t.clone(),\n        Err(BasicParseError {\n            kind: BasicParseErrorKind::UnexpectedToken(t),\n            location,\n        }) => return Err(location.new_custom_error(SelectorParseErrorKind::BadValueInAttr(t))),\n        Err(e) => return Err(e.into()),\n    };\n\n    let attribute_flags = parse_attribute_flags(input)?;\n    let value = value.as_ref().into();\n    let local_name_lower;\n    let local_name_is_ascii_lowercase;\n    let case_sensitivity;\n    {\n        let local_name_lower_cow = to_ascii_lowercase(&local_name);\n        case_sensitivity =\n            attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());\n        local_name_lower = local_name_lower_cow.as_ref().into();\n        local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..));\n    }\n    let local_name = local_name.as_ref().into();\n    if namespace.is_some() || !local_name_is_ascii_lowercase {\n        Ok(Component::AttributeOther(Box::new(\n            AttrSelectorWithOptionalNamespace {\n                namespace,\n                local_name,\n                local_name_lower,\n                operation: ParsedAttrSelectorOperation::WithValue {\n                    operator,\n                    case_sensitivity,\n                    value,\n                },\n            },\n        )))\n    } else {\n        Ok(Component::AttributeInNoNamespace {\n            local_name,\n            operator,\n            value,\n            case_sensitivity,\n        })\n    }\n}\n\n/// An attribute selector can have 's' or 'i' as flags, or no flags at all.\nenum AttributeFlags {\n    // Matching should be case-sensitive ('s' flag).\n    CaseSensitive,\n    // Matching should be case-insensitive ('i' flag).\n    AsciiCaseInsensitive,\n    // No flags.  Matching behavior depends on the name of the attribute.\n    CaseSensitivityDependsOnName,\n}\n\nimpl AttributeFlags {\n    fn to_case_sensitivity(\n        self,\n        local_name_lower: &str,\n        have_namespace: bool,\n    ) -> ParsedCaseSensitivity {\n        match self {\n            AttributeFlags::CaseSensitive => ParsedCaseSensitivity::ExplicitCaseSensitive,\n            AttributeFlags::AsciiCaseInsensitive => ParsedCaseSensitivity::AsciiCaseInsensitive,\n            AttributeFlags::CaseSensitivityDependsOnName => {\n                if !have_namespace\n                    && include!(concat!(\n                        env!(\"OUT_DIR\"),\n                        \"/ascii_case_insensitive_html_attributes.rs\"\n                    ))\n                    .contains(local_name_lower)\n                {\n                    ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument\n                } else {\n                    ParsedCaseSensitivity::CaseSensitive\n                }\n            },\n        }\n    }\n}\n\nfn parse_attribute_flags<'i, 't>(\n    input: &mut CssParser<'i, 't>,\n) -> Result<AttributeFlags, BasicParseError<'i>> {\n    let location = input.current_source_location();\n    let token = match input.next() {\n        Ok(t) => t,\n        Err(..) => {\n            // Selectors spec says language-defined; HTML says it depends on the\n            // exact attribute name.\n            return Ok(AttributeFlags::CaseSensitivityDependsOnName);\n        },\n    };\n\n    let ident = match *token {\n        Token::Ident(ref i) => i,\n        ref other => return Err(location.new_basic_unexpected_token_error(other.clone())),\n    };\n\n    Ok(match_ignore_ascii_case! {\n        ident,\n        \"i\" => AttributeFlags::AsciiCaseInsensitive,\n        \"s\" => AttributeFlags::CaseSensitive,\n        _ => return Err(location.new_basic_unexpected_token_error(token.clone())),\n    })\n}\n\n/// Level 3: Parse **one** simple_selector.  (Though we might insert a second\n/// implied \"<defaultns>|*\" type selector.)\nfn parse_negation<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    let list = SelectorList::parse_with_state(\n        parser,\n        input,\n        state\n            | SelectorParsingState::SKIP_DEFAULT_NAMESPACE\n            | SelectorParsingState::DISALLOW_PSEUDOS,\n        ForgivingParsing::No,\n        ParseRelative::No,\n    )?;\n\n    Ok(Component::Negation(list))\n}\n\n/// simple_selector_sequence\n/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*\n/// | [ HASH | class | attrib | pseudo | negation ]+\n///\n/// `Err(())` means invalid selector.\n/// `Ok(true)` is an empty selector\nfn parse_compound_selector<'i, 't, P, Impl>(\n    parser: &P,\n    state: &mut SelectorParsingState,\n    input: &mut CssParser<'i, 't>,\n    builder: &mut SelectorBuilder<Impl>,\n) -> Result<bool, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    input.skip_whitespace();\n\n    let mut empty = true;\n    if parse_type_selector(parser, input, *state, builder)? {\n        empty = false;\n    }\n\n    loop {\n        let result = match parse_one_simple_selector(parser, input, *state)? {\n            None => break,\n            Some(result) => result,\n        };\n\n        if empty {\n            if let Some(url) = parser.default_namespace() {\n                // If there was no explicit type selector, but there is a\n                // default namespace, there is an implicit \"<defaultns>|*\" type\n                // selector. Except for :host() or :not() / :is() / :where(),\n                // where we ignore it.\n                //\n                // https://drafts.csswg.org/css-scoping/#host-element-in-tree:\n                //\n                //     When considered within its own shadow trees, the shadow\n                //     host is featureless. Only the :host, :host(), and\n                //     :host-context() pseudo-classes are allowed to match it.\n                //\n                // https://drafts.csswg.org/selectors-4/#featureless:\n                //\n                //     A featureless element does not match any selector at all,\n                //     except those it is explicitly defined to match. If a\n                //     given selector is allowed to match a featureless element,\n                //     it must do so while ignoring the default namespace.\n                //\n                // https://drafts.csswg.org/selectors-4/#matches\n                //\n                //     Default namespace declarations do not affect the compound\n                //     selector representing the subject of any selector within\n                //     a :is() pseudo-class, unless that compound selector\n                //     contains an explicit universal selector or type selector.\n                //\n                //     (Similar quotes for :where() / :not())\n                //\n                let ignore_default_ns = state\n                    .intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE)\n                    || matches!(\n                        result,\n                        SimpleSelectorParseResult::SimpleSelector(Component::Host(..))\n                    );\n                if !ignore_default_ns {\n                    builder.push_simple_selector(Component::DefaultNamespace(url));\n                }\n            }\n        }\n\n        empty = false;\n\n        match result {\n            SimpleSelectorParseResult::SimpleSelector(s) => {\n                builder.push_simple_selector(s);\n            },\n            SimpleSelectorParseResult::PartPseudo(part_names) => {\n                state.insert(SelectorParsingState::AFTER_PART_LIKE);\n                builder.push_combinator(Combinator::Part);\n                builder.push_simple_selector(Component::Part(part_names));\n            },\n            SimpleSelectorParseResult::SlottedPseudo(selector) => {\n                state.insert(SelectorParsingState::AFTER_SLOTTED);\n                builder.push_combinator(Combinator::SlotAssignment);\n                builder.push_simple_selector(Component::Slotted(selector));\n            },\n            SimpleSelectorParseResult::PseudoElement(p) => {\n                if p.parses_as_element_backed() {\n                    state.insert(SelectorParsingState::AFTER_PART_LIKE);\n                } else {\n                    state.insert(SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO);\n                    if p.is_before_or_after() {\n                        state.insert(SelectorParsingState::AFTER_BEFORE_OR_AFTER_PSEUDO);\n                    }\n                }\n                if !p.accepts_state_pseudo_classes() {\n                    state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);\n                }\n                if p.is_in_pseudo_element_tree() {\n                    state.insert(SelectorParsingState::IN_PSEUDO_ELEMENT_TREE);\n                }\n                builder.push_combinator(Combinator::PseudoElement);\n                builder.push_simple_selector(Component::PseudoElement(p));\n            },\n        }\n    }\n    Ok(empty)\n}\n\nfn parse_is_where<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n    component: impl FnOnce(SelectorList<Impl>) -> Component<Impl>,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    debug_assert!(parser.parse_is_and_where());\n    // https://drafts.csswg.org/selectors/#matches-pseudo:\n    //\n    //     Pseudo-elements cannot be represented by the matches-any\n    //     pseudo-class; they are not valid within :is().\n    //\n    let inner = SelectorList::parse_with_state(\n        parser,\n        input,\n        state\n            | SelectorParsingState::SKIP_DEFAULT_NAMESPACE\n            | SelectorParsingState::DISALLOW_PSEUDOS,\n        ForgivingParsing::Yes,\n        ParseRelative::No,\n    )?;\n    Ok(component(inner))\n}\n\nfn parse_has<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    debug_assert!(parser.parse_has());\n    if state.intersects(\n        SelectorParsingState::DISALLOW_RELATIVE_SELECTOR | SelectorParsingState::AFTER_PSEUDO,\n    ) {\n        return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n    }\n    // Nested `:has()` is disallowed, mark it as such.\n    // Note: The spec defines \":has-allowed pseudo-element,\" but there's no\n    // pseudo-element defined as such at the moment.\n    // https://w3c.github.io/csswg-drafts/selectors-4/#has-allowed-pseudo-element\n    let inner = SelectorList::parse_with_state(\n        parser,\n        input,\n        state\n            | SelectorParsingState::SKIP_DEFAULT_NAMESPACE\n            | SelectorParsingState::DISALLOW_PSEUDOS\n            | SelectorParsingState::DISALLOW_RELATIVE_SELECTOR,\n        ForgivingParsing::No,\n        ParseRelative::ForHas,\n    )?;\n    Ok(Component::Has(RelativeSelector::from_selector_list(inner)))\n}\n\nfn parse_functional_pseudo_class<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    name: CowRcStr<'i>,\n    state: SelectorParsingState,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    match_ignore_ascii_case! { &name,\n        \"nth-child\" => return parse_nth_pseudo_class(parser, input, state, NthType::Child),\n        \"nth-of-type\" => return parse_nth_pseudo_class(parser, input, state, NthType::OfType),\n        \"nth-last-child\" => return parse_nth_pseudo_class(parser, input, state, NthType::LastChild),\n        \"nth-last-of-type\" => return parse_nth_pseudo_class(parser, input, state, NthType::LastOfType),\n        \"is\" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Is),\n        \"where\" if parser.parse_is_and_where() => return parse_is_where(parser, input, state, Component::Where),\n        \"has\" if parser.parse_has() => return parse_has(parser, input, state),\n        \"host\" => {\n            if !state.allows_tree_structural_pseudo_classes() {\n                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n            }\n            return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));\n        },\n        \"not\" => {\n            return parse_negation(parser, input, state)\n        },\n        _ => {}\n    }\n\n    if parser.parse_is_and_where() && parser.is_is_alias(&name) {\n        return parse_is_where(parser, input, state, Component::Is);\n    }\n\n    if state.intersects(\n        SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO | SelectorParsingState::AFTER_SLOTTED,\n    ) {\n        return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n    }\n\n    let after_part = state.intersects(SelectorParsingState::AFTER_PART_LIKE);\n    P::parse_non_ts_functional_pseudo_class(parser, name, input, after_part)\n        .map(Component::NonTSPseudoClass)\n}\n\nfn parse_nth_pseudo_class<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n    ty: NthType,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    if !state.allows_tree_structural_pseudo_classes() {\n        return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n    }\n    let (a, b) = parse_nth(input)?;\n    let nth_data = NthSelectorData {\n        ty,\n        is_function: true,\n        an_plus_b: AnPlusB(a, b),\n    };\n    if !parser.parse_nth_child_of() || ty.is_of_type() {\n        return Ok(Component::Nth(nth_data));\n    }\n\n    // Try to parse \"of <selector-list>\".\n    if input.try_parse(|i| i.expect_ident_matching(\"of\")).is_err() {\n        return Ok(Component::Nth(nth_data));\n    }\n    // Whitespace between \"of\" and the selector list is optional\n    // https://github.com/w3c/csswg-drafts/issues/8285\n    let selectors = SelectorList::parse_with_state(\n        parser,\n        input,\n        state\n            | SelectorParsingState::SKIP_DEFAULT_NAMESPACE\n            | SelectorParsingState::DISALLOW_PSEUDOS,\n        ForgivingParsing::No,\n        ParseRelative::No,\n    )?;\n    Ok(Component::NthOf(NthOfSelectorData::new(\n        &nth_data,\n        selectors.slice().iter().cloned(),\n    )))\n}\n\n/// Returns whether the name corresponds to a CSS2 pseudo-element that\n/// can be specified with the single colon syntax (in addition to the\n/// double-colon syntax, which can be used for all pseudo-elements).\npub fn is_css2_pseudo_element(name: &str) -> bool {\n    // ** Do not add to this list! **\n    match_ignore_ascii_case! { name,\n        \"before\" | \"after\" | \"first-line\" | \"first-letter\" => true,\n        _ => false,\n    }\n}\n\n/// Parse a simple selector other than a type selector.\n///\n/// * `Err(())`: Invalid selector, abort\n/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed.\n/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element\nfn parse_one_simple_selector<'i, 't, P, Impl>(\n    parser: &P,\n    input: &mut CssParser<'i, 't>,\n    state: SelectorParsingState,\n) -> Result<Option<SimpleSelectorParseResult<Impl>>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    let start = input.state();\n    let token = match input.next_including_whitespace().map(|t| t.clone()) {\n        Ok(t) => t,\n        Err(..) => {\n            input.reset(&start);\n            return Ok(None);\n        },\n    };\n\n    Ok(Some(match token {\n        Token::IDHash(id) => {\n            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {\n                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n            }\n            let id = Component::ID(id.as_ref().into());\n            SimpleSelectorParseResult::SimpleSelector(id)\n        },\n        Token::Delim(delim) if delim == '.' || (delim == '&' && parser.parse_parent_selector()) => {\n            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {\n                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n            }\n            let location = input.current_source_location();\n            SimpleSelectorParseResult::SimpleSelector(if delim == '&' {\n                Component::ParentSelector\n            } else {\n                let class = match *input.next_including_whitespace()? {\n                    Token::Ident(ref class) => class,\n                    ref t => {\n                        let e = SelectorParseErrorKind::ClassNeedsIdent(t.clone());\n                        return Err(location.new_custom_error(e));\n                    },\n                };\n                Component::Class(class.as_ref().into())\n            })\n        },\n        Token::SquareBracketBlock => {\n            if state.intersects(SelectorParsingState::AFTER_PSEUDO) {\n                return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n            }\n            let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;\n            SimpleSelectorParseResult::SimpleSelector(attr)\n        },\n        Token::Colon => {\n            let location = input.current_source_location();\n            let (is_single_colon, next_token) = match input.next_including_whitespace()?.clone() {\n                Token::Colon => (false, input.next_including_whitespace()?.clone()),\n                t => (true, t),\n            };\n            let (name, is_functional) = match next_token {\n                Token::Ident(name) => (name, false),\n                Token::Function(name) => (name, true),\n                t => {\n                    let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);\n                    return Err(input.new_custom_error(e));\n                },\n            };\n            let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(&name);\n            if is_pseudo_element {\n                // Pseudos after pseudo elements are not allowed in some cases:\n                // - Some states will disallow pseudos, such as the interiors of\n                // :has/:is/:where/:not (DISALLOW_PSEUDOS).\n                // - Non-element backed pseudos do not allow other pseudos to follow (AFTER_NON_ELEMENT_BACKED_PSEUDO)...\n                // - ... except ::before and ::after, which allow _some_ pseudos.\n                if state.intersects(SelectorParsingState::DISALLOW_PSEUDOS)\n                    || (state.intersects(SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO)\n                        && !state.intersects(SelectorParsingState::AFTER_BEFORE_OR_AFTER_PSEUDO))\n                {\n                    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n                }\n                let pseudo_element = if is_functional {\n                    if P::parse_part(parser) && name.eq_ignore_ascii_case(\"part\") {\n                        if !state.allows_part() {\n                            return Err(\n                                input.new_custom_error(SelectorParseErrorKind::InvalidState)\n                            );\n                        }\n                        let names = input.parse_nested_block(|input| {\n                            let mut result = Vec::with_capacity(1);\n                            result.push(input.expect_ident()?.as_ref().into());\n                            while !input.is_exhausted() {\n                                result.push(input.expect_ident()?.as_ref().into());\n                            }\n                            Ok(result.into_boxed_slice())\n                        })?;\n                        return Ok(Some(SimpleSelectorParseResult::PartPseudo(names)));\n                    }\n                    if P::parse_slotted(parser) && name.eq_ignore_ascii_case(\"slotted\") {\n                        if !state.allows_slotted() {\n                            return Err(\n                                input.new_custom_error(SelectorParseErrorKind::InvalidState)\n                            );\n                        }\n                        let selector = input.parse_nested_block(|input| {\n                            parse_inner_compound_selector(parser, input, state)\n                        })?;\n                        return Ok(Some(SimpleSelectorParseResult::SlottedPseudo(selector)));\n                    }\n                    input.parse_nested_block(|input| {\n                        P::parse_functional_pseudo_element(parser, name, input)\n                    })?\n                } else {\n                    P::parse_pseudo_element(parser, location, name)?\n                };\n\n                if state.intersects(SelectorParsingState::AFTER_BEFORE_OR_AFTER_PSEUDO)\n                    && !pseudo_element.valid_after_before_or_after()\n                {\n                    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n                }\n\n                if state.intersects(SelectorParsingState::AFTER_SLOTTED)\n                    && !pseudo_element.valid_after_slotted()\n                {\n                    return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));\n                }\n                SimpleSelectorParseResult::PseudoElement(pseudo_element)\n            } else {\n                let pseudo_class = if is_functional {\n                    input.parse_nested_block(|input| {\n                        parse_functional_pseudo_class(parser, input, name, state)\n                    })?\n                } else {\n                    parse_simple_pseudo_class(parser, location, name, state)?\n                };\n                SimpleSelectorParseResult::SimpleSelector(pseudo_class)\n            }\n        },\n        _ => {\n            input.reset(&start);\n            return Ok(None);\n        },\n    }))\n}\n\nfn parse_simple_pseudo_class<'i, P, Impl>(\n    parser: &P,\n    location: SourceLocation,\n    name: CowRcStr<'i>,\n    state: SelectorParsingState,\n) -> Result<Component<Impl>, ParseError<'i, P::Error>>\nwhere\n    P: Parser<'i, Impl = Impl>,\n    Impl: SelectorImpl,\n{\n    if !state.allows_non_functional_pseudo_classes() {\n        return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));\n    }\n\n    if state.allows_tree_structural_pseudo_classes() {\n        // If a descendant pseudo of a pseudo-element root has no other siblings, then :only-child\n        // matches that pseudo. Note that we don't accept other tree structural pseudo classes in\n        // this case (to match other browsers). And the spec mentions only `:only-child` as well.\n        // https://drafts.csswg.org/css-view-transitions-1/#pseudo-root\n        if state.allows_only_child_pseudo_class_only() {\n            if name.eq_ignore_ascii_case(\"only-child\") {\n                return Ok(Component::Nth(NthSelectorData::only(\n                    /* of_type = */ false,\n                )));\n            }\n            // Other non-functional pseudo classes are not allowed.\n            // FIXME: Perhaps we can refactor this, e.g. distinguish tree-structural pseudo classes\n            // from other non-ts pseudo classes. Otherwise, this special case looks weird.\n            return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));\n        }\n\n        match_ignore_ascii_case! { &name,\n            \"first-child\" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ false))),\n            \"last-child\" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ false))),\n            \"only-child\" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false))),\n            \"root\" => return Ok(Component::Root),\n            \"empty\" => return Ok(Component::Empty),\n            \"scope\" => return Ok(Component::Scope),\n            \"host\" if P::parse_host(parser) => return Ok(Component::Host(None)),\n            \"first-of-type\" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ true))),\n            \"last-of-type\" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ true))),\n            \"only-of-type\" => return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ true))),\n            _ => {},\n        }\n    }\n\n    let pseudo_class = P::parse_non_ts_pseudo_class(parser, location, name)?;\n    if state.intersects(SelectorParsingState::AFTER_NON_ELEMENT_BACKED_PSEUDO)\n        && !pseudo_class.is_user_action_state()\n    {\n        return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));\n    }\n    Ok(Component::NonTSPseudoClass(pseudo_class))\n}\n\n// NB: pub module in order to access the DummyParser\n#[cfg(test)]\npub mod tests {\n    use super::*;\n    use crate::builder::SelectorFlags;\n    use crate::parser;\n    use cssparser::{serialize_identifier, Parser as CssParser, ParserInput, ToCss};\n    use std::collections::HashMap;\n    use std::fmt;\n\n    #[derive(Clone, Debug, Eq, PartialEq)]\n    pub enum PseudoClass {\n        Hover,\n        Active,\n        Lang(String),\n    }\n\n    #[derive(Clone, Debug, Eq, PartialEq)]\n    pub enum PseudoElement {\n        Before,\n        After,\n        Marker,\n        DetailsContent,\n        Highlight(String),\n    }\n\n    impl parser::PseudoElement for PseudoElement {\n        type Impl = DummySelectorImpl;\n\n        fn accepts_state_pseudo_classes(&self) -> bool {\n            true\n        }\n\n        fn valid_after_slotted(&self) -> bool {\n            true\n        }\n\n        fn valid_after_before_or_after(&self) -> bool {\n            matches!(self, Self::Marker)\n        }\n\n        fn is_before_or_after(&self) -> bool {\n            matches!(self, Self::Before | Self::After)\n        }\n\n        fn parses_as_element_backed(&self) -> bool {\n            matches!(self, Self::DetailsContent)\n        }\n    }\n\n    impl parser::NonTSPseudoClass for PseudoClass {\n        type Impl = DummySelectorImpl;\n\n        #[inline]\n        fn is_active_or_hover(&self) -> bool {\n            matches!(*self, PseudoClass::Active | PseudoClass::Hover)\n        }\n\n        #[inline]\n        fn is_user_action_state(&self) -> bool {\n            self.is_active_or_hover()\n        }\n    }\n\n    impl ToCss for PseudoClass {\n        fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            match *self {\n                PseudoClass::Hover => dest.write_str(\":hover\"),\n                PseudoClass::Active => dest.write_str(\":active\"),\n                PseudoClass::Lang(ref lang) => {\n                    dest.write_str(\":lang(\")?;\n                    serialize_identifier(lang, dest)?;\n                    dest.write_char(')')\n                },\n            }\n        }\n    }\n\n    impl ToCss for PseudoElement {\n        fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            match *self {\n                PseudoElement::Before => dest.write_str(\"::before\"),\n                PseudoElement::After => dest.write_str(\"::after\"),\n                PseudoElement::Marker => dest.write_str(\"::marker\"),\n                PseudoElement::DetailsContent => dest.write_str(\"::details-content\"),\n                PseudoElement::Highlight(ref name) => {\n                    dest.write_str(\"::highlight(\")?;\n                    serialize_identifier(&name, dest)?;\n                    dest.write_char(')')\n                },\n            }\n        }\n    }\n\n    #[derive(Clone, Debug, PartialEq)]\n    pub struct DummySelectorImpl;\n\n    #[derive(Default)]\n    pub struct DummyParser {\n        default_ns: Option<DummyAtom>,\n        ns_prefixes: HashMap<DummyAtom, DummyAtom>,\n    }\n\n    impl DummyParser {\n        fn default_with_namespace(default_ns: DummyAtom) -> DummyParser {\n            DummyParser {\n                default_ns: Some(default_ns),\n                ns_prefixes: Default::default(),\n            }\n        }\n    }\n\n    impl SelectorImpl for DummySelectorImpl {\n        type ExtraMatchingData<'a> = std::marker::PhantomData<&'a ()>;\n        type AttrValue = DummyAttrValue;\n        type Identifier = DummyAtom;\n        type LocalName = DummyAtom;\n        type NamespaceUrl = DummyAtom;\n        type NamespacePrefix = DummyAtom;\n        type BorrowedLocalName = DummyAtom;\n        type BorrowedNamespaceUrl = DummyAtom;\n        type NonTSPseudoClass = PseudoClass;\n        type PseudoElement = PseudoElement;\n    }\n\n    #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]\n    pub struct DummyAttrValue(String);\n\n    impl ToCss for DummyAttrValue {\n        fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use std::fmt::Write;\n\n            dest.write_char('\"')?;\n            write!(cssparser::CssStringWriter::new(dest), \"{}\", &self.0)?;\n            dest.write_char('\"')\n        }\n    }\n\n    impl<'a> From<&'a str> for DummyAttrValue {\n        fn from(string: &'a str) -> Self {\n            Self(string.into())\n        }\n    }\n\n    #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]\n    pub struct DummyAtom(String);\n\n    impl ToCss for DummyAtom {\n        fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            serialize_identifier(&self.0, dest)\n        }\n    }\n\n    impl From<String> for DummyAtom {\n        fn from(string: String) -> Self {\n            DummyAtom(string)\n        }\n    }\n\n    impl<'a> From<&'a str> for DummyAtom {\n        fn from(string: &'a str) -> Self {\n            DummyAtom(string.into())\n        }\n    }\n\n    impl PrecomputedHash for DummyAtom {\n        fn precomputed_hash(&self) -> u32 {\n            self.0.as_ptr() as u32\n        }\n    }\n\n    impl<'i> Parser<'i> for DummyParser {\n        type Impl = DummySelectorImpl;\n        type Error = SelectorParseErrorKind<'i>;\n\n        fn parse_slotted(&self) -> bool {\n            true\n        }\n\n        fn parse_nth_child_of(&self) -> bool {\n            true\n        }\n\n        fn parse_is_and_where(&self) -> bool {\n            true\n        }\n\n        fn parse_has(&self) -> bool {\n            true\n        }\n\n        fn parse_parent_selector(&self) -> bool {\n            true\n        }\n\n        fn parse_part(&self) -> bool {\n            true\n        }\n\n        fn parse_host(&self) -> bool {\n            true\n        }\n\n        fn parse_non_ts_pseudo_class(\n            &self,\n            location: SourceLocation,\n            name: CowRcStr<'i>,\n        ) -> Result<PseudoClass, SelectorParseError<'i>> {\n            match_ignore_ascii_case! { &name,\n                \"hover\" => return Ok(PseudoClass::Hover),\n                \"active\" => return Ok(PseudoClass::Active),\n                _ => {}\n            }\n            Err(\n                location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                    name,\n                )),\n            )\n        }\n\n        fn parse_non_ts_functional_pseudo_class<'t>(\n            &self,\n            name: CowRcStr<'i>,\n            parser: &mut CssParser<'i, 't>,\n            after_part: bool,\n        ) -> Result<PseudoClass, SelectorParseError<'i>> {\n            match_ignore_ascii_case! { &name,\n                \"lang\" if !after_part => {\n                    let lang = parser.expect_ident_or_string()?.as_ref().to_owned();\n                    return Ok(PseudoClass::Lang(lang));\n                },\n                _ => {}\n            }\n            Err(\n                parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                    name,\n                )),\n            )\n        }\n\n        fn parse_pseudo_element(\n            &self,\n            location: SourceLocation,\n            name: CowRcStr<'i>,\n        ) -> Result<PseudoElement, SelectorParseError<'i>> {\n            match_ignore_ascii_case! { &name,\n                \"before\" => return Ok(PseudoElement::Before),\n                \"after\" => return Ok(PseudoElement::After),\n                \"marker\" => return Ok(PseudoElement::Marker),\n                \"details-content\" => return Ok(PseudoElement::DetailsContent),\n                _ => {}\n            }\n            Err(\n                location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                    name,\n                )),\n            )\n        }\n\n        fn parse_functional_pseudo_element<'t>(\n            &self,\n            name: CowRcStr<'i>,\n            parser: &mut CssParser<'i, 't>,\n        ) -> Result<PseudoElement, SelectorParseError<'i>> {\n            match_ignore_ascii_case! { &name,\n                \"highlight\" => return Ok(PseudoElement::Highlight(parser.expect_ident()?.as_ref().to_owned())),\n                _ => {}\n            }\n            Err(\n                parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                    name,\n                )),\n            )\n        }\n\n        fn default_namespace(&self) -> Option<DummyAtom> {\n            self.default_ns.clone()\n        }\n\n        fn namespace_for_prefix(&self, prefix: &DummyAtom) -> Option<DummyAtom> {\n            self.ns_prefixes.get(prefix).cloned()\n        }\n    }\n\n    fn parse<'i>(\n        input: &'i str,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_relative(input, ParseRelative::No)\n    }\n\n    fn parse_relative<'i>(\n        input: &'i str,\n        parse_relative: ParseRelative,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_relative(input, &DummyParser::default(), parse_relative)\n    }\n\n    fn parse_expected<'i, 'a>(\n        input: &'i str,\n        expected: Option<&'a str>,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_expected(input, &DummyParser::default(), expected)\n    }\n\n    fn parse_relative_expected<'i, 'a>(\n        input: &'i str,\n        parse_relative: ParseRelative,\n        expected: Option<&'a str>,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_relative_expected(input, &DummyParser::default(), parse_relative, expected)\n    }\n\n    fn parse_ns<'i>(\n        input: &'i str,\n        parser: &DummyParser,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_relative(input, parser, ParseRelative::No)\n    }\n\n    fn parse_ns_relative<'i>(\n        input: &'i str,\n        parser: &DummyParser,\n        parse_relative: ParseRelative,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_relative_expected(input, parser, parse_relative, None)\n    }\n\n    fn parse_ns_expected<'i, 'a>(\n        input: &'i str,\n        parser: &DummyParser,\n        expected: Option<&'a str>,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        parse_ns_relative_expected(input, parser, ParseRelative::No, expected)\n    }\n\n    fn parse_ns_relative_expected<'i, 'a>(\n        input: &'i str,\n        parser: &DummyParser,\n        parse_relative: ParseRelative,\n        expected: Option<&'a str>,\n    ) -> Result<SelectorList<DummySelectorImpl>, SelectorParseError<'i>> {\n        let mut parser_input = ParserInput::new(input);\n        let result = SelectorList::parse(\n            parser,\n            &mut CssParser::new(&mut parser_input),\n            parse_relative,\n        );\n        if let Ok(ref selectors) = result {\n            // We can't assume that the serialized parsed selector will equal\n            // the input; for example, if there is no default namespace, '*|foo'\n            // should serialize to 'foo'.\n            assert_eq!(\n                selectors.to_css_string(),\n                match expected {\n                    Some(x) => x,\n                    None => input,\n                }\n            );\n        }\n        result\n    }\n\n    fn specificity(a: u32, b: u32, c: u32) -> u32 {\n        a << 20 | b << 10 | c\n    }\n\n    #[test]\n    fn test_empty() {\n        let mut input = ParserInput::new(\":empty\");\n        let list = SelectorList::parse(\n            &DummyParser::default(),\n            &mut CssParser::new(&mut input),\n            ParseRelative::No,\n        );\n        assert!(list.is_ok());\n    }\n\n    const MATHML: &str = \"http://www.w3.org/1998/Math/MathML\";\n    const SVG: &str = \"http://www.w3.org/2000/svg\";\n\n    #[test]\n    fn test_parsing() {\n        assert!(parse(\"\").is_err());\n        assert!(parse(\":lang(4)\").is_err());\n        assert!(parse(\":lang(en US)\").is_err());\n        assert_eq!(\n            parse(\"EeÉ\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::LocalName(LocalName {\n                    name: DummyAtom::from(\"EeÉ\"),\n                    lower_name: DummyAtom::from(\"eeÉ\"),\n                })],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"|e\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ExplicitNoNamespace,\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"e\"),\n                        lower_name: DummyAtom::from(\"e\"),\n                    }),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // When the default namespace is not set, *| should be elided.\n        // https://github.com/servo/servo/pull/17537\n        assert_eq!(\n            parse_expected(\"*|e\", Some(\"e\")),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::LocalName(LocalName {\n                    name: DummyAtom::from(\"e\"),\n                    lower_name: DummyAtom::from(\"e\"),\n                })],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // When the default namespace is set, *| should _not_ be elided (as foo\n        // is no longer equivalent to *|foo--the former is only for foo in the\n        // default namespace).\n        // https://github.com/servo/servo/issues/16020\n        assert_eq!(\n            parse_ns(\n                \"*|e\",\n                &DummyParser::default_with_namespace(DummyAtom::from(\"https://mozilla.org\"))\n            ),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ExplicitAnyNamespace,\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"e\"),\n                        lower_name: DummyAtom::from(\"e\"),\n                    }),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"*\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::ExplicitUniversalType],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"|*\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ExplicitNoNamespace,\n                    Component::ExplicitUniversalType,\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_expected(\"*|*\", Some(\"*\")),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::ExplicitUniversalType],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\n                \"*|*\",\n                &DummyParser::default_with_namespace(DummyAtom::from(\"https://mozilla.org\"))\n            ),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ExplicitAnyNamespace,\n                    Component::ExplicitUniversalType,\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\".foo:lang(en-US)\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Class(DummyAtom::from(\"foo\")),\n                    Component::NonTSPseudoClass(PseudoClass::Lang(\"en-US\".to_owned())),\n                ],\n                specificity(0, 2, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"#bar\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::ID(DummyAtom::from(\"bar\"))],\n                specificity(1, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"e.foo#bar\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"e\"),\n                        lower_name: DummyAtom::from(\"e\"),\n                    }),\n                    Component::Class(DummyAtom::from(\"foo\")),\n                    Component::ID(DummyAtom::from(\"bar\")),\n                ],\n                specificity(1, 1, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"e.foo #bar\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"e\"),\n                        lower_name: DummyAtom::from(\"e\"),\n                    }),\n                    Component::Class(DummyAtom::from(\"foo\")),\n                    Component::Combinator(Combinator::Descendant),\n                    Component::ID(DummyAtom::from(\"bar\")),\n                ],\n                specificity(1, 1, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // Default namespace does not apply to attribute selectors\n        // https://github.com/mozilla/servo/pull/1652\n        let mut parser = DummyParser::default();\n        assert_eq!(\n            parse_ns(\"[Foo]\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::AttributeInNoNamespaceExists {\n                    local_name: DummyAtom::from(\"Foo\"),\n                    local_name_lower: DummyAtom::from(\"foo\"),\n                }],\n                specificity(0, 1, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert!(parse_ns(\"svg|circle\", &parser).is_err());\n        parser\n            .ns_prefixes\n            .insert(DummyAtom(\"svg\".into()), DummyAtom(SVG.into()));\n        assert_eq!(\n            parse_ns(\"svg|circle\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Namespace(DummyAtom(\"svg\".into()), SVG.into()),\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"circle\"),\n                        lower_name: DummyAtom::from(\"circle\"),\n                    }),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\"svg|*\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Namespace(DummyAtom(\"svg\".into()), SVG.into()),\n                    Component::ExplicitUniversalType,\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // Default namespace does not apply to attribute selectors\n        // https://github.com/mozilla/servo/pull/1652\n        // but it does apply to implicit type selectors\n        // https://github.com/servo/rust-selectors/pull/82\n        parser.default_ns = Some(MATHML.into());\n        assert_eq!(\n            parse_ns(\"[Foo]\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::AttributeInNoNamespaceExists {\n                        local_name: DummyAtom::from(\"Foo\"),\n                        local_name_lower: DummyAtom::from(\"foo\"),\n                    },\n                ],\n                specificity(0, 1, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // Default namespace does apply to type selectors\n        assert_eq!(\n            parse_ns(\"e\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"e\"),\n                        lower_name: DummyAtom::from(\"e\"),\n                    }),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\"*\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::ExplicitUniversalType,\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\"*|*\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ExplicitAnyNamespace,\n                    Component::ExplicitUniversalType,\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // Default namespace applies to universal and type selectors inside :not and :matches,\n        // but not otherwise.\n        assert_eq!(\n            parse_ns(\":not(.cl)\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::Negation(SelectorList::from_vec(vec![Selector::from_vec(\n                        vec![Component::Class(DummyAtom::from(\"cl\"))],\n                        specificity(0, 1, 0),\n                        SelectorFlags::empty(),\n                    )])),\n                ],\n                specificity(0, 1, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\":not(*)\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::Negation(SelectorList::from_vec(vec![Selector::from_vec(\n                        vec![\n                            Component::DefaultNamespace(MATHML.into()),\n                            Component::ExplicitUniversalType,\n                        ],\n                        specificity(0, 0, 0),\n                        SelectorFlags::empty(),\n                    )]),),\n                ],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\":not(e)\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::DefaultNamespace(MATHML.into()),\n                    Component::Negation(SelectorList::from_vec(vec![Selector::from_vec(\n                        vec![\n                            Component::DefaultNamespace(MATHML.into()),\n                            Component::LocalName(LocalName {\n                                name: DummyAtom::from(\"e\"),\n                                lower_name: DummyAtom::from(\"e\"),\n                            }),\n                        ],\n                        specificity(0, 0, 1),\n                        SelectorFlags::empty(),\n                    )])),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse(\"[attr|=\\\"foo\\\"]\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::AttributeInNoNamespace {\n                    local_name: DummyAtom::from(\"attr\"),\n                    operator: AttrSelectorOperator::DashMatch,\n                    value: DummyAttrValue::from(\"foo\"),\n                    case_sensitivity: ParsedCaseSensitivity::CaseSensitive,\n                }],\n                specificity(0, 1, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // https://github.com/mozilla/servo/issues/1723\n        assert_eq!(\n            parse(\"::before\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Combinator(Combinator::PseudoElement),\n                    Component::PseudoElement(PseudoElement::Before),\n                ],\n                specificity(0, 0, 1),\n                SelectorFlags::HAS_PSEUDO,\n            )]))\n        );\n        assert_eq!(\n            parse(\"::before:hover\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Combinator(Combinator::PseudoElement),\n                    Component::PseudoElement(PseudoElement::Before),\n                    Component::NonTSPseudoClass(PseudoClass::Hover),\n                ],\n                specificity(0, 1, 1),\n                SelectorFlags::HAS_PSEUDO,\n            )]))\n        );\n        assert_eq!(\n            parse(\"::before:hover:hover\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Combinator(Combinator::PseudoElement),\n                    Component::PseudoElement(PseudoElement::Before),\n                    Component::NonTSPseudoClass(PseudoClass::Hover),\n                    Component::NonTSPseudoClass(PseudoClass::Hover),\n                ],\n                specificity(0, 2, 1),\n                SelectorFlags::HAS_PSEUDO,\n            )]))\n        );\n        assert!(parse(\"::before:hover:lang(foo)\").is_err());\n        assert!(parse(\"::before:hover .foo\").is_err());\n        assert!(parse(\"::before .foo\").is_err());\n        assert!(parse(\"::before ~ bar\").is_err());\n        assert!(parse(\"::before:active\").is_ok());\n\n        // https://github.com/servo/servo/issues/15335\n        assert!(parse(\":: before\").is_err());\n        assert_eq!(\n            parse(\"div ::after\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::LocalName(LocalName {\n                        name: DummyAtom::from(\"div\"),\n                        lower_name: DummyAtom::from(\"div\"),\n                    }),\n                    Component::Combinator(Combinator::Descendant),\n                    Component::Combinator(Combinator::PseudoElement),\n                    Component::PseudoElement(PseudoElement::After),\n                ],\n                specificity(0, 0, 2),\n                SelectorFlags::HAS_PSEUDO,\n            )]))\n        );\n        assert_eq!(\n            parse(\"#d1 > .ok\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ID(DummyAtom::from(\"d1\")),\n                    Component::Combinator(Combinator::Child),\n                    Component::Class(DummyAtom::from(\"ok\")),\n                ],\n                specificity(1, 1, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        parser.default_ns = None;\n        assert!(parse(\":not(#provel.old)\").is_ok());\n        assert!(parse(\":not(#provel > old)\").is_ok());\n        assert!(parse(\"table[rules]:not([rules=\\\"none\\\"]):not([rules=\\\"\\\"])\").is_ok());\n        // https://github.com/servo/servo/issues/16017\n        assert_eq!(\n            parse_ns(\":not(*)\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::Negation(SelectorList::from_vec(vec![\n                    Selector::from_vec(\n                        vec![Component::ExplicitUniversalType],\n                        specificity(0, 0, 0),\n                        SelectorFlags::empty(),\n                    )\n                ]))],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        assert_eq!(\n            parse_ns(\":not(|*)\", &parser),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::Negation(SelectorList::from_vec(vec![\n                    Selector::from_vec(\n                        vec![\n                            Component::ExplicitNoNamespace,\n                            Component::ExplicitUniversalType,\n                        ],\n                        specificity(0, 0, 0),\n                        SelectorFlags::empty(),\n                    )\n                ]))],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n        // *| should be elided if there is no default namespace.\n        // https://github.com/servo/servo/pull/17537\n        assert_eq!(\n            parse_ns_expected(\":not(*|*)\", &parser, Some(\":not(*)\")),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![Component::Negation(SelectorList::from_vec(vec![\n                    Selector::from_vec(\n                        vec![Component::ExplicitUniversalType],\n                        specificity(0, 0, 0),\n                        SelectorFlags::empty(),\n                    )\n                ]))],\n                specificity(0, 0, 0),\n                SelectorFlags::empty(),\n            )]))\n        );\n\n        assert!(parse(\"::highlight(foo)\").is_ok());\n\n        assert!(parse(\"::slotted()\").is_err());\n        assert!(parse(\"::slotted(div)\").is_ok());\n        assert!(parse(\"::slotted(div).foo\").is_err());\n        assert!(parse(\"::slotted(div + bar)\").is_err());\n        assert!(parse(\"::slotted(div) + foo\").is_err());\n\n        assert!(parse(\"::part()\").is_err());\n        assert!(parse(\"::part(42)\").is_err());\n        assert!(parse(\"::part(foo bar)\").is_ok());\n        assert!(parse(\"::part(foo):hover\").is_ok());\n        assert!(parse(\"::part(foo) + bar\").is_err());\n\n        assert!(parse(\"div ::slotted(div)\").is_ok());\n        assert!(parse(\"div + slot::slotted(div)\").is_ok());\n        assert!(parse(\"div + slot::slotted(div.foo)\").is_ok());\n        assert!(parse(\"slot::slotted(div,foo)::first-line\").is_err());\n        assert!(parse(\"::slotted(div)::before\").is_ok());\n        assert!(parse(\"slot::slotted(div,foo)\").is_err());\n\n        assert!(parse(\"foo:where()\").is_ok());\n        assert!(parse(\"foo:where(div, foo, .bar baz)\").is_ok());\n        assert!(parse(\"foo:where(::before)\").is_ok());\n    }\n\n    #[test]\n    fn parent_selector() {\n        assert!(parse(\"foo &\").is_ok());\n        assert_eq!(\n            parse(\"#foo &.bar\"),\n            Ok(SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ID(DummyAtom::from(\"foo\")),\n                    Component::Combinator(Combinator::Descendant),\n                    Component::ParentSelector,\n                    Component::Class(DummyAtom::from(\"bar\")),\n                ],\n                specificity(1, 1, 0),\n                SelectorFlags::HAS_PARENT\n            )]))\n        );\n\n        let parent = parse(\".bar, div .baz\").unwrap();\n        let child = parse(\"#foo &.bar\").unwrap();\n        assert_eq!(\n            child.replace_parent_selector(&parent),\n            parse(\"#foo :is(.bar, div .baz).bar\").unwrap()\n        );\n\n        let has_child = parse(\"#foo:has(&.bar)\").unwrap();\n        assert_eq!(\n            has_child.replace_parent_selector(&parent),\n            parse(\"#foo:has(:is(.bar, div .baz).bar)\").unwrap()\n        );\n\n        let child =\n            parse_relative_expected(\"#foo\", ParseRelative::ForNesting, Some(\"& #foo\")).unwrap();\n        assert_eq!(\n            child.replace_parent_selector(&parent),\n            parse(\":is(.bar, div .baz) #foo\").unwrap()\n        );\n\n        let child =\n            parse_relative_expected(\"+ #foo\", ParseRelative::ForNesting, Some(\"& + #foo\")).unwrap();\n        assert_eq!(child, parse(\"& + #foo\").unwrap());\n    }\n\n    #[test]\n    fn test_pseudo_iter() {\n        let list = parse(\"q::before\").unwrap();\n        let selector = &list.slice()[0];\n        assert!(!selector.is_universal());\n        let mut iter = selector.iter();\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::Before))\n        );\n        assert_eq!(iter.next(), None);\n        let combinator = iter.next_sequence();\n        assert_eq!(combinator, Some(Combinator::PseudoElement));\n        assert_eq!(\n            iter.next(),\n            Some(&Component::LocalName(LocalName {\n                name: DummyAtom::from(\"q\"),\n                lower_name: DummyAtom::from(\"q\"),\n            }))\n        );\n        assert_eq!(iter.next(), None);\n        assert_eq!(iter.next_sequence(), None);\n    }\n\n    #[test]\n    fn test_pseudo_before_marker() {\n        let list = parse(\"::before::marker\").unwrap();\n        let selector = &list.slice()[0];\n        let mut iter = selector.iter();\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::Marker))\n        );\n        assert_eq!(iter.next(), None);\n        let combinator = iter.next_sequence();\n        assert_eq!(combinator, Some(Combinator::PseudoElement));\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::Before))\n        );\n        assert_eq!(iter.next(), None);\n        let combinator = iter.next_sequence();\n        assert_eq!(combinator, Some(Combinator::PseudoElement));\n        assert_eq!(iter.next(), None);\n        assert_eq!(iter.next_sequence(), None);\n    }\n\n    #[test]\n    fn test_pseudo_duplicate_before_after_or_marker() {\n        assert!(parse(\"::before::before\").is_err());\n        assert!(parse(\"::after::after\").is_err());\n        assert!(parse(\"::marker::marker\").is_err());\n    }\n\n    #[test]\n    fn test_pseudo_on_element_backed_pseudo() {\n        let list = parse(\"::details-content::before\").unwrap();\n        let selector = &list.slice()[0];\n        let mut iter = selector.iter();\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::Before))\n        );\n        assert_eq!(iter.next(), None);\n        let combinator = iter.next_sequence();\n        assert_eq!(combinator, Some(Combinator::PseudoElement));\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::DetailsContent))\n        );\n        assert_eq!(iter.next(), None);\n        let combinator = iter.next_sequence();\n        assert_eq!(combinator, Some(Combinator::PseudoElement));\n        assert_eq!(iter.next(), None);\n        assert_eq!(iter.next_sequence(), None);\n    }\n\n    #[test]\n    fn test_universal() {\n        let list = parse_ns(\n            \"*|*::before\",\n            &DummyParser::default_with_namespace(DummyAtom::from(\"https://mozilla.org\")),\n        )\n        .unwrap();\n        let selector = &list.slice()[0];\n        assert!(selector.is_universal());\n    }\n\n    #[test]\n    fn test_empty_pseudo_iter() {\n        let list = parse(\"::before\").unwrap();\n        let selector = &list.slice()[0];\n        assert!(selector.is_universal());\n        let mut iter = selector.iter();\n        assert_eq!(\n            iter.next(),\n            Some(&Component::PseudoElement(PseudoElement::Before))\n        );\n        assert_eq!(iter.next(), None);\n        assert_eq!(iter.next_sequence(), Some(Combinator::PseudoElement));\n        assert_eq!(iter.next(), None);\n        assert_eq!(iter.next_sequence(), None);\n    }\n\n    #[test]\n    fn test_parse_implicit_scope() {\n        assert_eq!(\n            parse_relative_expected(\".foo\", ParseRelative::ForScope, None).unwrap(),\n            SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ImplicitScope,\n                    Component::Combinator(Combinator::Descendant),\n                    Component::Class(DummyAtom::from(\"foo\")),\n                ],\n                specificity(0, 1, 0),\n                SelectorFlags::HAS_SCOPE,\n            )])\n        );\n\n        assert_eq!(\n            parse_relative_expected(\":scope .foo\", ParseRelative::ForScope, None).unwrap(),\n            SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Scope,\n                    Component::Combinator(Combinator::Descendant),\n                    Component::Class(DummyAtom::from(\"foo\")),\n                ],\n                specificity(0, 2, 0),\n                SelectorFlags::HAS_SCOPE\n            )])\n        );\n\n        assert_eq!(\n            parse_relative_expected(\"> .foo\", ParseRelative::ForScope, Some(\"> .foo\")).unwrap(),\n            SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::ImplicitScope,\n                    Component::Combinator(Combinator::Child),\n                    Component::Class(DummyAtom::from(\"foo\")),\n                ],\n                specificity(0, 1, 0),\n                SelectorFlags::HAS_SCOPE\n            )])\n        );\n\n        assert_eq!(\n            parse_relative_expected(\".foo :scope > .bar\", ParseRelative::ForScope, None).unwrap(),\n            SelectorList::from_vec(vec![Selector::from_vec(\n                vec![\n                    Component::Class(DummyAtom::from(\"foo\")),\n                    Component::Combinator(Combinator::Descendant),\n                    Component::Scope,\n                    Component::Combinator(Combinator::Child),\n                    Component::Class(DummyAtom::from(\"bar\")),\n                ],\n                specificity(0, 3, 0),\n                SelectorFlags::HAS_SCOPE\n            )])\n        );\n    }\n\n    struct TestVisitor {\n        seen: Vec<String>,\n    }\n\n    impl SelectorVisitor for TestVisitor {\n        type Impl = DummySelectorImpl;\n\n        fn visit_simple_selector(&mut self, s: &Component<DummySelectorImpl>) -> bool {\n            let mut dest = String::new();\n            s.to_css(&mut dest).unwrap();\n            self.seen.push(dest);\n            true\n        }\n    }\n\n    #[test]\n    fn visitor() {\n        let mut test_visitor = TestVisitor { seen: vec![] };\n        parse(\":not(:hover) ~ label\").unwrap().slice()[0].visit(&mut test_visitor);\n        assert!(test_visitor.seen.contains(&\":hover\".into()));\n\n        let mut test_visitor = TestVisitor { seen: vec![] };\n        parse(\"::before:hover\").unwrap().slice()[0].visit(&mut test_visitor);\n        assert!(test_visitor.seen.contains(&\":hover\".into()));\n    }\n}\n"
  },
  {
    "path": "selectors/relative_selector/cache.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse rustc_hash::FxHashMap;\n/// Relative selector cache. This is useful for following cases.\n/// First case is non-subject relative selector: Imagine `.anchor:has(<..>) ~ .foo`, with DOM\n/// `.anchor + .foo + .. + .foo`. Each match on `.foo` triggers `:has()` traversal that\n/// yields the same result. This is simple enough, since we just need to store\n/// the exact match on that anchor pass/fail.\n/// Second case is `querySelectorAll`: Imagine `querySelectorAll(':has(.a)')`, with DOM\n/// `div > .. > div > .a`. When the we perform the traversal at the top div,\n/// we basically end up evaluating `:has(.a)` for all anchors, which could be reused.\n/// Also consider the sibling version: `querySelectorAll(':has(~ .a)')` with DOM\n/// `div + .. + div + .a`.\n/// TODO(dshin): Second case is not yet handled. That is tracked in Bug 1845291.\nuse std::hash::Hash;\n\nuse crate::parser::{RelativeSelector, SelectorKey};\nuse crate::{tree::OpaqueElement, SelectorImpl};\n\n/// Match data for a given element and a selector.\n#[derive(Clone, Copy)]\npub enum RelativeSelectorCachedMatch {\n    /// This selector matches this element.\n    Matched,\n    /// This selector does not match this element.\n    NotMatched,\n}\n\nimpl RelativeSelectorCachedMatch {\n    /// Is the cached result a match?\n    pub fn matched(&self) -> bool {\n        matches!(*self, Self::Matched)\n    }\n}\n\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\nstruct Key {\n    element: OpaqueElement,\n    selector: SelectorKey,\n}\n\nimpl Key {\n    pub fn new<Impl: SelectorImpl>(\n        element: OpaqueElement,\n        selector: &RelativeSelector<Impl>,\n    ) -> Self {\n        Key {\n            element,\n            selector: SelectorKey::new(&selector.selector),\n        }\n    }\n}\n\n/// Cache to speed up matching of relative selectors.\n#[derive(Default)]\npub struct RelativeSelectorCache {\n    cache: FxHashMap<Key, RelativeSelectorCachedMatch>,\n}\n\nimpl RelativeSelectorCache {\n    /// Add a relative selector match into the cache.\n    pub fn add<Impl: SelectorImpl>(\n        &mut self,\n        anchor: OpaqueElement,\n        selector: &RelativeSelector<Impl>,\n        matched: RelativeSelectorCachedMatch,\n    ) {\n        self.cache.insert(Key::new(anchor, selector), matched);\n    }\n\n    /// Check if we have a cache entry for the element.\n    pub fn lookup<Impl: SelectorImpl>(\n        &mut self,\n        element: OpaqueElement,\n        selector: &RelativeSelector<Impl>,\n    ) -> Option<RelativeSelectorCachedMatch> {\n        self.cache.get(&Key::new(element, selector)).copied()\n    }\n}\n"
  },
  {
    "path": "selectors/relative_selector/filter.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n/// Bloom filter for relative selectors.\nuse rustc_hash::FxHashMap;\n\nuse crate::bloom::BloomFilter;\nuse crate::context::QuirksMode;\nuse crate::parser::{collect_selector_hashes, RelativeSelector, RelativeSelectorMatchHint};\nuse crate::tree::{Element, OpaqueElement};\nuse crate::SelectorImpl;\n\nenum Entry {\n    /// Filter lookup happened once. Construction of the filter is expensive,\n    /// so this is set when the element for subtree traversal is encountered.\n    Lookup,\n    /// Filter lookup happened more than once, and the filter for this element's\n    /// subtree traversal is constructed. Could use special handlings for pseudo-classes\n    /// such as `:hover` and `:focus`, see Bug 1845503.\n    HasFilter(Box<BloomFilter>),\n}\n\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\nenum TraversalKind {\n    Children,\n    Descendants,\n}\n\nfn add_to_filter<E: Element>(element: &E, filter: &mut BloomFilter, kind: TraversalKind) -> bool {\n    let mut child = element.first_element_child();\n    while let Some(e) = child {\n        if !e.add_element_unique_hashes(filter) {\n            return false;\n        }\n        if kind == TraversalKind::Descendants {\n            if !add_to_filter(&e, filter, kind) {\n                return false;\n            }\n        }\n        child = e.next_sibling_element();\n    }\n    true\n}\n\n#[derive(Clone, Copy, Hash, Eq, PartialEq)]\nstruct Key(OpaqueElement, TraversalKind);\n\n/// Map of bloom filters for fast-rejecting relative selectors.\n#[derive(Default)]\npub struct RelativeSelectorFilterMap {\n    map: FxHashMap<Key, Entry>,\n}\n\nfn fast_reject<Impl: SelectorImpl>(\n    selector: &RelativeSelector<Impl>,\n    quirks_mode: QuirksMode,\n    filter: &BloomFilter,\n) -> bool {\n    let mut hashes = [0u32; 4];\n    let mut len = 0;\n    // For inner selectors, we only collect from the single rightmost compound.\n    // This is because inner selectors can cause breakouts: e.g. `.anchor:has(:is(.a .b) .c)`\n    // can match when `.a` is the ancestor of `.anchor`. Including `.a` would possibly fast\n    // reject the subtree for not having `.a`, even if the selector would match.\n    // Technically, if the selector's traversal is non-sibling subtree, we can traverse\n    // inner selectors up to the point where a descendant/child combinator is encountered\n    // (e.g. In `.anchor:has(:is(.a ~ .b) .c)`, `.a` is guaranteed to be the a descendant\n    // of `.anchor`). While that can be separately handled, well, this is simpler.\n    collect_selector_hashes(\n        selector.selector.iter(),\n        quirks_mode,\n        &mut hashes,\n        &mut len,\n        |s| s.iter(),\n    );\n    for i in 0..len {\n        if !filter.might_contain_hash(hashes[i]) {\n            // Definitely rejected.\n            return true;\n        }\n    }\n    false\n}\n\nimpl RelativeSelectorFilterMap {\n    fn get_filter<E: Element>(&mut self, element: &E, kind: TraversalKind) -> Option<&BloomFilter> {\n        // Insert flag to indicate that we looked up the filter once, and\n        // create the filter if and only if that flag is there.\n        let key = Key(element.opaque(), kind);\n        let entry = self\n            .map\n            .entry(key)\n            .and_modify(|entry| {\n                if !matches!(entry, Entry::Lookup) {\n                    return;\n                }\n                let mut filter = BloomFilter::new();\n                // Go through all children/descendants of this element and add their hashes.\n                if add_to_filter(element, &mut filter, kind) {\n                    *entry = Entry::HasFilter(Box::new(filter));\n                }\n            })\n            .or_insert(Entry::Lookup);\n        match entry {\n            Entry::Lookup => None,\n            Entry::HasFilter(ref filter) => Some(filter.as_ref()),\n        }\n    }\n\n    /// Potentially reject the given selector for this element.\n    /// This may seem redundant in presence of the cache, but the cache keys into the\n    /// selector-element pair specifically, while this filter keys to the element\n    /// and the traversal kind, so it is useful for handling multiple selectors\n    /// that effectively end up looking at the same(-ish, for siblings) subtree.\n    pub fn fast_reject<Impl: SelectorImpl, E: Element>(\n        &mut self,\n        element: &E,\n        selector: &RelativeSelector<Impl>,\n        quirks_mode: QuirksMode,\n    ) -> bool {\n        if matches!(\n            selector.match_hint,\n            RelativeSelectorMatchHint::InNextSibling\n        ) {\n            // Don't bother.\n            return false;\n        }\n        let is_sibling = matches!(\n            selector.match_hint,\n            RelativeSelectorMatchHint::InSibling\n                | RelativeSelectorMatchHint::InNextSiblingSubtree\n                | RelativeSelectorMatchHint::InSiblingSubtree\n        );\n        let is_subtree = matches!(\n            selector.match_hint,\n            RelativeSelectorMatchHint::InSubtree\n                | RelativeSelectorMatchHint::InNextSiblingSubtree\n                | RelativeSelectorMatchHint::InSiblingSubtree\n        );\n        let kind = if is_subtree {\n            TraversalKind::Descendants\n        } else {\n            TraversalKind::Children\n        };\n        if is_sibling {\n            // Contain the entirety of the parent's children/subtree in the filter, and use that.\n            // This is less likely to reject, especially for sibling subtree matches; however, it's less\n            // expensive memory-wise, compared to storing filters for each sibling.\n            element.parent_element().map_or(false, |parent| {\n                self.get_filter(&parent, kind)\n                    .map_or(false, |filter| fast_reject(selector, quirks_mode, filter))\n            })\n        } else {\n            self.get_filter(element, kind)\n                .map_or(false, |filter| fast_reject(selector, quirks_mode, filter))\n        }\n    }\n}\n"
  },
  {
    "path": "selectors/relative_selector/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\npub mod cache;\npub mod filter;\n"
  },
  {
    "path": "selectors/sink.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Small helpers to abstract over different containers.\n#![deny(missing_docs)]\n\nuse smallvec::{Array, SmallVec};\n\n/// A trait to abstract over a `push` method that may be implemented for\n/// different kind of types.\n///\n/// Used to abstract over `Array`, `SmallVec` and `Vec`, and also to implement a\n/// type which `push` method does only tweak a byte when we only need to check\n/// for the presence of something.\npub trait Push<T> {\n    /// Push a value into self.\n    fn push(&mut self, value: T);\n}\n\nimpl<T> Push<T> for Vec<T> {\n    fn push(&mut self, value: T) {\n        Vec::push(self, value);\n    }\n}\n\nimpl<A: Array> Push<A::Item> for SmallVec<A> {\n    fn push(&mut self, value: A::Item) {\n        SmallVec::push(self, value);\n    }\n}\n"
  },
  {
    "path": "selectors/tree.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency\n//! between layout and style.\n\nuse crate::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};\nuse crate::bloom::BloomFilter;\nuse crate::matching::{ElementSelectorFlags, MatchingContext};\nuse crate::parser::SelectorImpl;\nuse std::fmt::Debug;\nuse std::ptr::NonNull;\n\n/// Opaque representation of an Element, for identity comparisons.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub struct OpaqueElement(NonNull<()>);\n\nunsafe impl Send for OpaqueElement {}\n// This should be safe given that we do not provide a way to recover\n// the original reference.\nunsafe impl Sync for OpaqueElement {}\n\nimpl OpaqueElement {\n    /// Creates a new OpaqueElement from an arbitrarily-typed pointer.\n    pub fn new<T>(ptr: &T) -> Self {\n        unsafe {\n            OpaqueElement(NonNull::new_unchecked(\n                ptr as *const T as *const () as *mut (),\n            ))\n        }\n    }\n\n    /// Creates a new OpaqueElement from a type-erased non-null pointer\n    pub fn from_non_null_ptr(ptr: NonNull<()>) -> Self {\n        Self(ptr)\n    }\n\n    /// Returns a const ptr to the contained reference. Unsafe especially\n    /// since Element can be recovered and potentially-mutated.\n    pub unsafe fn as_const_ptr<T>(&self) -> *const T {\n        self.0.as_ptr() as *const T\n    }\n}\n\npub trait Element: Sized + Clone + Debug {\n    type Impl: SelectorImpl;\n\n    /// Converts self into an opaque representation.\n    fn opaque(&self) -> OpaqueElement;\n\n    fn parent_element(&self) -> Option<Self>;\n\n    /// Whether the parent node of this element is a shadow root.\n    fn parent_node_is_shadow_root(&self) -> bool;\n\n    /// The host of the containing shadow root, if any.\n    fn containing_shadow_host(&self) -> Option<Self>;\n\n    /// The parent of a given pseudo-element, after matching a pseudo-element\n    /// selector.\n    ///\n    /// This is guaranteed to be called in a pseudo-element.\n    fn pseudo_element_originating_element(&self) -> Option<Self> {\n        debug_assert!(self.is_pseudo_element());\n        self.parent_element()\n    }\n\n    /// Whether we're matching on a pseudo-element.\n    fn is_pseudo_element(&self) -> bool;\n\n    /// Skips non-element nodes\n    fn prev_sibling_element(&self) -> Option<Self>;\n\n    /// Skips non-element nodes\n    fn next_sibling_element(&self) -> Option<Self>;\n\n    /// Skips non-element nodes\n    fn first_element_child(&self) -> Option<Self>;\n\n    fn is_html_element_in_html_document(&self) -> bool;\n\n    fn has_local_name(&self, local_name: &<Self::Impl as SelectorImpl>::BorrowedLocalName) -> bool;\n\n    /// Empty string for no namespace\n    fn has_namespace(&self, ns: &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl) -> bool;\n\n    /// Whether this element and the `other` element have the same local name and namespace.\n    fn is_same_type(&self, other: &Self) -> bool;\n\n    fn attr_matches(\n        &self,\n        ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,\n        local_name: &<Self::Impl as SelectorImpl>::LocalName,\n        operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>,\n    ) -> bool;\n\n    fn has_attr_in_no_namespace(\n        &self,\n        local_name: &<Self::Impl as SelectorImpl>::LocalName,\n    ) -> bool {\n        self.attr_matches(\n            &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<Self::Impl>()),\n            local_name,\n            &AttrSelectorOperation::Exists,\n        )\n    }\n\n    fn match_non_ts_pseudo_class(\n        &self,\n        pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,\n        context: &mut MatchingContext<Self::Impl>,\n    ) -> bool;\n\n    fn match_pseudo_element(\n        &self,\n        pe: &<Self::Impl as SelectorImpl>::PseudoElement,\n        context: &mut MatchingContext<Self::Impl>,\n    ) -> bool;\n\n    /// Sets selector flags on the elemnt itself or the parent, depending on the\n    /// flags, which indicate what kind of work may need to be performed when\n    /// DOM state changes.\n    fn apply_selector_flags(&self, flags: ElementSelectorFlags);\n\n    /// Whether this element is a `link`.\n    fn is_link(&self) -> bool;\n\n    /// Returns whether the element is an HTML <slot> element.\n    fn is_html_slot_element(&self) -> bool;\n\n    /// Returns the assigned <slot> element this element is assigned to.\n    ///\n    /// Necessary for the `::slotted` pseudo-class.\n    fn assigned_slot(&self) -> Option<Self> {\n        None\n    }\n\n    fn has_id(\n        &self,\n        id: &<Self::Impl as SelectorImpl>::Identifier,\n        case_sensitivity: CaseSensitivity,\n    ) -> bool;\n\n    fn has_class(\n        &self,\n        name: &<Self::Impl as SelectorImpl>::Identifier,\n        case_sensitivity: CaseSensitivity,\n    ) -> bool;\n\n    fn has_custom_state(&self, name: &<Self::Impl as SelectorImpl>::Identifier) -> bool;\n\n    /// Returns the mapping from the `exportparts` attribute in the reverse\n    /// direction, that is, in an outer-tree -> inner-tree direction.\n    fn imported_part(\n        &self,\n        name: &<Self::Impl as SelectorImpl>::Identifier,\n    ) -> Option<<Self::Impl as SelectorImpl>::Identifier>;\n\n    fn is_part(&self, name: &<Self::Impl as SelectorImpl>::Identifier) -> bool;\n\n    /// Returns whether this element matches `:empty`.\n    ///\n    /// That is, whether it does not contain any child element or any non-zero-length text node.\n    /// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo\n    fn is_empty(&self) -> bool;\n\n    /// Returns whether this element matches `:root`,\n    /// i.e. whether it is the root element of a document.\n    ///\n    /// Note: this can be false even if `.parent_element()` is `None`\n    /// if the parent node is a `DocumentFragment`.\n    fn is_root(&self) -> bool;\n\n    /// Returns whether this element should ignore matching nth child\n    /// selector.\n    fn ignores_nth_child_selectors(&self) -> bool {\n        false\n    }\n\n    /// Add hashes unique to this element to the given filter, returning true\n    /// if any got added.\n    fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool;\n}\n"
  },
  {
    "path": "selectors/visitor.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Visitor traits for selectors.\n\n#![deny(missing_docs)]\n\nuse crate::attr::NamespaceConstraint;\nuse crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl};\nuse bitflags::bitflags;\n\n/// A trait to visit selector properties.\n///\n/// All the `visit_foo` methods return a boolean indicating whether the\n/// traversal should continue or not.\npub trait SelectorVisitor: Sized {\n    /// The selector implementation this visitor wants to visit.\n    type Impl: SelectorImpl;\n\n    /// Visit an attribute selector that may match (there are other selectors\n    /// that may never match, like those containing whitespace or the empty\n    /// string).\n    fn visit_attribute_selector(\n        &mut self,\n        _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,\n        _local_name: &<Self::Impl as SelectorImpl>::LocalName,\n        _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,\n    ) -> bool {\n        true\n    }\n\n    /// Visit a simple selector.\n    fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool {\n        true\n    }\n\n    /// Visit a nested relative selector list. The caller is responsible to call visit\n    /// into the internal selectors if / as needed.\n    ///\n    /// The default implementation skips it altogether.\n    fn visit_relative_selector_list(&mut self, _list: &[RelativeSelector<Self::Impl>]) -> bool {\n        true\n    }\n\n    /// Visit a nested selector list. The caller is responsible to call visit\n    /// into the internal selectors if / as needed.\n    ///\n    /// The default implementation does this.\n    fn visit_selector_list(\n        &mut self,\n        _list_kind: SelectorListKind,\n        list: &[Selector<Self::Impl>],\n    ) -> bool {\n        for nested in list {\n            if !nested.visit(self) {\n                return false;\n            }\n        }\n        true\n    }\n\n    /// Visits a complex selector.\n    ///\n    /// Gets the combinator to the right of the selector, or `None` if the\n    /// selector is the rightmost one.\n    fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {\n        true\n    }\n}\n\nbitflags! {\n    /// The kinds of components the visitor is visiting the selector list of, if any\n    #[derive(Clone, Copy, Default)]\n    pub struct SelectorListKind: u8 {\n        /// The visitor is inside :not(..)\n        const NEGATION = 1 << 0;\n        /// The visitor is inside :is(..)\n        const IS = 1 << 1;\n        /// The visitor is inside :where(..)\n        const WHERE = 1 << 2;\n        /// The visitor is inside :nth-child(.. of <selector list>) or\n        /// :nth-last-child(.. of <selector list>)\n        const NTH_OF = 1 << 3;\n        /// The visitor is inside :has(..)\n        const HAS = 1 << 4;\n    }\n}\n\nimpl SelectorListKind {\n    /// Construct a SelectorListKind for the corresponding component.\n    pub fn from_component<Impl: SelectorImpl>(component: &Component<Impl>) -> Self {\n        match component {\n            Component::Negation(_) => SelectorListKind::NEGATION,\n            Component::Is(_) => SelectorListKind::IS,\n            Component::Where(_) => SelectorListKind::WHERE,\n            Component::NthOf(_) => SelectorListKind::NTH_OF,\n            _ => SelectorListKind::empty(),\n        }\n    }\n\n    /// Whether the visitor is inside :not(..)\n    pub fn in_negation(&self) -> bool {\n        self.intersects(SelectorListKind::NEGATION)\n    }\n\n    /// Whether the visitor is inside :is(..)\n    pub fn in_is(&self) -> bool {\n        self.intersects(SelectorListKind::IS)\n    }\n\n    /// Whether the visitor is inside :where(..)\n    pub fn in_where(&self) -> bool {\n        self.intersects(SelectorListKind::WHERE)\n    }\n\n    /// Whether the visitor is inside :nth-child(.. of <selector list>) or\n    /// :nth-last-child(.. of <selector list>)\n    pub fn in_nth_of(&self) -> bool {\n        self.intersects(SelectorListKind::NTH_OF)\n    }\n\n    /// Whether the visitor is inside :has(..)\n    pub fn in_has(&self) -> bool {\n        self.intersects(SelectorListKind::HAS)\n    }\n\n    /// Whether this nested selector is relevant for nth-of dependencies.\n    pub fn relevant_to_nth_of_dependencies(&self) -> bool {\n        // Order of nesting for `:has` and `:nth-child(.. of ..)` doesn't matter, because:\n        // * `:has(:nth-child(.. of ..))`: The location of the anchoring element is\n        //   independent from where `:nth-child(.. of ..)` is applied.\n        // * `:nth-child(.. of :has(..))`: Invalidations inside `:has` must first use the\n        //   `:has` machinary to find the anchor, then carry out the remaining invalidation.\n        self.in_nth_of() && !self.in_has()\n    }\n}\n"
  },
  {
    "path": "servo_arc/Cargo.toml",
    "content": "[package]\nname = \"servo_arc\"\nversion = \"0.4.3\"\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MIT OR Apache-2.0\"\nrepository = \"https://github.com/servo/stylo\"\ndescription = \"A fork of std::sync::Arc with some extra functionality and without weak references\"\nedition = \"2021\"\nreadme = \"../README.md\"\n\n[lib]\nname = \"servo_arc\"\npath = \"lib.rs\"\n\n[features]\ndefault = [\"track_alloc_size\"]\ngecko_refcount_logging = []\nservo = [\"serde\", \"track_alloc_size\"]\ntrack_alloc_size = []\n\n[dependencies]\nserde = { version = \"1.0\", optional = true }\nstable_deref_trait = \"1.0.0\"\n"
  },
  {
    "path": "servo_arc/LICENSE-APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"[]\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "servo_arc/LICENSE-MIT",
    "content": "Permission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "servo_arc/lib.rs",
    "content": "// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT\n// file at the top-level directory of this distribution and at\n// http://rust-lang.org/COPYRIGHT.\n//\n// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\n// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\n// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\n// option. This file may not be copied, modified, or distributed\n// except according to those terms.\n\n//! Fork of Arc for Servo. This has the following advantages over std::sync::Arc:\n//!\n//! * We don't waste storage on the weak reference count.\n//! * We don't do extra RMU operations to handle the possibility of weak references.\n//! * We can experiment with arena allocation (todo).\n//! * We can add methods to support our custom use cases [1].\n//! * We have support for dynamically-sized types (see from_header_and_iter).\n//! * We have support for thin arcs to unsized types (see ThinArc).\n//! * We have support for references to static data, which don't do any\n//!   refcounting.\n//!\n//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883\n\n// The semantics of `Arc` are already documented in the Rust docs, so we don't\n// duplicate those here.\n#![allow(missing_docs)]\n\n#[cfg(feature = \"servo\")]\nuse serde::{Deserialize, Serialize};\nuse stable_deref_trait::{CloneStableDeref, StableDeref};\nuse std::alloc::{self, Layout};\nuse std::borrow;\nuse std::cmp::Ordering;\nuse std::fmt;\nuse std::hash::{Hash, Hasher};\nuse std::marker::PhantomData;\nuse std::mem::{self, align_of, size_of};\nuse std::ops::{Deref, DerefMut};\nuse std::os::raw::c_void;\nuse std::process;\nuse std::ptr;\nuse std::sync::atomic;\nuse std::sync::atomic::Ordering::{Acquire, Relaxed, Release};\nuse std::{isize, usize};\n\n/// A soft limit on the amount of references that may be made to an `Arc`.\n///\n/// Going above this limit will abort your program (although not\n/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references.\nconst MAX_REFCOUNT: usize = (isize::MAX) as usize;\n\n/// Special refcount value that means the data is not reference counted,\n/// and that the `Arc` is really acting as a read-only static reference.\nconst STATIC_REFCOUNT: usize = usize::MAX;\n\n/// An atomically reference counted shared pointer\n///\n/// See the documentation for [`Arc`] in the standard library. Unlike the\n/// standard library `Arc`, this `Arc` does not support weak reference counting.\n///\n/// See the discussion in https://github.com/rust-lang/rust/pull/60594 for the\n/// usage of PhantomData.\n///\n/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html\n///\n/// cbindgen:derive-eq=false\n/// cbindgen:derive-neq=false\n#[repr(C)]\npub struct Arc<T: ?Sized> {\n    p: ptr::NonNull<ArcInner<T>>,\n    phantom: PhantomData<T>,\n}\n\n/// An `Arc` that is known to be uniquely owned\n///\n/// When `Arc`s are constructed, they are known to be\n/// uniquely owned. In such a case it is safe to mutate\n/// the contents of the `Arc`. Normally, one would just handle\n/// this by mutating the data on the stack before allocating the\n/// `Arc`, however it's possible the data is large or unsized\n/// and you need to heap-allocate it earlier in such a way\n/// that it can be freely converted into a regular `Arc` once you're\n/// done.\n///\n/// `UniqueArc` exists for this purpose, when constructed it performs\n/// the same allocations necessary for an `Arc`, however it allows mutable access.\n/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc`\n/// out of it.\n///\n/// Ignore the doctest below there's no way to skip building with refcount\n/// logging during doc tests (see rust-lang/rust#45599).\n///\n/// ```rust,ignore\n/// # use servo_arc::UniqueArc;\n/// let data = [1, 2, 3, 4, 5];\n/// let mut x = UniqueArc::new(data);\n/// x[4] = 7; // mutate!\n/// let y = x.shareable(); // y is an Arc<T>\n/// ```\npub struct UniqueArc<T: ?Sized>(Arc<T>);\n\nimpl<T> UniqueArc<T> {\n    #[inline]\n    /// Construct a new UniqueArc\n    pub fn new(data: T) -> Self {\n        UniqueArc(Arc::new(data))\n    }\n\n    /// Construct an uninitialized arc\n    #[inline]\n    pub fn new_uninit() -> UniqueArc<mem::MaybeUninit<T>> {\n        unsafe {\n            let layout = Layout::new::<ArcInner<mem::MaybeUninit<T>>>();\n            let ptr = alloc::alloc(layout);\n            let mut p = ptr::NonNull::new(ptr)\n                .unwrap_or_else(|| alloc::handle_alloc_error(layout))\n                .cast::<ArcInner<mem::MaybeUninit<T>>>();\n            ptr::write(&mut p.as_mut().count, atomic::AtomicUsize::new(1));\n            #[cfg(feature = \"track_alloc_size\")]\n            ptr::write(&mut p.as_mut().alloc_size, layout.size());\n\n            #[cfg(feature = \"gecko_refcount_logging\")]\n            {\n                NS_LogCtor(p.as_ptr() as *mut _, b\"ServoArc\\0\".as_ptr() as *const _, 8)\n            }\n\n            UniqueArc(Arc {\n                p,\n                phantom: PhantomData,\n            })\n        }\n    }\n\n    #[inline]\n    /// Convert to a shareable Arc<T> once we're done mutating it\n    pub fn shareable(self) -> Arc<T> {\n        self.0\n    }\n}\n\nimpl<T> UniqueArc<mem::MaybeUninit<T>> {\n    /// Convert to an initialized Arc.\n    #[inline]\n    pub unsafe fn assume_init(this: Self) -> UniqueArc<T> {\n        UniqueArc(Arc {\n            p: mem::ManuallyDrop::new(this).0.p.cast(),\n            phantom: PhantomData,\n        })\n    }\n}\n\nimpl<T> Deref for UniqueArc<T> {\n    type Target = T;\n    fn deref(&self) -> &T {\n        &*self.0\n    }\n}\n\nimpl<T> DerefMut for UniqueArc<T> {\n    fn deref_mut(&mut self) -> &mut T {\n        // We know this to be uniquely owned\n        unsafe { &mut (*self.0.ptr()).data }\n    }\n}\n\nunsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}\nunsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}\n\n/// The object allocated by an Arc<T>\n///\n/// See https://github.com/mozilla/cbindgen/issues/937 for the derive-{eq,neq}=false. But we don't\n/// use those anyways so we can just disable them.\n/// cbindgen:derive-eq=false\n/// cbindgen:derive-neq=false\n#[repr(C)]\nstruct ArcInner<T: ?Sized> {\n    count: atomic::AtomicUsize,\n    // NOTE(emilio): This needs to be here so that HeaderSlice<> is deallocated properly if the\n    // allocator relies on getting the right Layout. We don't need to track the right alignment,\n    // since we know that statically.\n    //\n    // This member could be completely avoided once min_specialization feature is stable (by\n    // implementing a trait for HeaderSlice that gives you the right layout). For now, servo-only\n    // since Gecko doesn't need it (its allocator doesn't need the size for the alignments we care\n    // about). See https://github.com/rust-lang/rust/issues/31844.\n    #[cfg(feature = \"track_alloc_size\")]\n    alloc_size: usize,\n    data: T,\n}\n\nunsafe impl<T: ?Sized + Sync + Send> Send for ArcInner<T> {}\nunsafe impl<T: ?Sized + Sync + Send> Sync for ArcInner<T> {}\n\n/// Computes the offset of the data field within ArcInner.\nfn data_offset<T>() -> usize {\n    let size = size_of::<ArcInner<()>>();\n    let align = align_of::<T>();\n    // https://github.com/rust-lang/rust/blob/1.36.0/src/libcore/alloc.rs#L187-L207\n    size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)\n}\n\nimpl<T> Arc<T> {\n    /// Construct an `Arc<T>`\n    #[inline]\n    pub fn new(data: T) -> Self {\n        let layout = Layout::new::<ArcInner<T>>();\n        let p = unsafe {\n            let ptr = ptr::NonNull::new(alloc::alloc(layout))\n                .unwrap_or_else(|| alloc::handle_alloc_error(layout))\n                .cast::<ArcInner<T>>();\n            ptr::write(\n                ptr.as_ptr(),\n                ArcInner {\n                    count: atomic::AtomicUsize::new(1),\n                    #[cfg(feature = \"track_alloc_size\")]\n                    alloc_size: layout.size(),\n                    data,\n                },\n            );\n            ptr\n        };\n\n        #[cfg(feature = \"gecko_refcount_logging\")]\n        unsafe {\n            // FIXME(emilio): Would be so amazing to have\n            // std::intrinsics::type_name() around, so that we could also report\n            // a real size.\n            NS_LogCtor(p.as_ptr() as *mut _, b\"ServoArc\\0\".as_ptr() as *const _, 8);\n        }\n\n        Arc {\n            p,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Construct an intentionally-leaked arc.\n    #[inline]\n    pub fn new_leaked(data: T) -> Self {\n        let arc = Self::new(data);\n        arc.mark_as_intentionally_leaked();\n        arc\n    }\n\n    /// Convert the Arc<T> to a raw pointer, suitable for use across FFI\n    ///\n    /// Note: This returns a pointer to the data T, which is offset in the allocation.\n    #[inline]\n    pub fn into_raw(this: Self) -> *const T {\n        let ptr = unsafe { &((*this.ptr()).data) as *const _ };\n        mem::forget(this);\n        ptr\n    }\n\n    /// Reconstruct the Arc<T> from a raw pointer obtained from into_raw()\n    ///\n    /// Note: This raw pointer will be offset in the allocation and must be preceded\n    /// by the atomic count.\n    #[inline]\n    pub unsafe fn from_raw(ptr: *const T) -> Self {\n        // To find the corresponding pointer to the `ArcInner` we need\n        // to subtract the offset of the `data` field from the pointer.\n        let ptr = (ptr as *const u8).sub(data_offset::<T>());\n        Arc {\n            p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Like from_raw, but returns an addrefed arc instead.\n    #[inline]\n    pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self {\n        let arc = Self::from_raw(ptr);\n        mem::forget(arc.clone());\n        arc\n    }\n\n    /// Create a new static Arc<T> (one that won't reference count the object)\n    /// and place it in the allocation provided by the specified `alloc`\n    /// function.\n    ///\n    /// `alloc` must return a pointer into a static allocation suitable for\n    /// storing data with the `Layout` passed into it. The pointer returned by\n    /// `alloc` will not be freed.\n    #[inline]\n    pub unsafe fn new_static<F>(alloc: F, data: T) -> Arc<T>\n    where\n        F: FnOnce(Layout) -> *mut u8,\n    {\n        let layout = Layout::new::<ArcInner<T>>();\n        let ptr = alloc(layout) as *mut ArcInner<T>;\n\n        let x = ArcInner {\n            count: atomic::AtomicUsize::new(STATIC_REFCOUNT),\n            #[cfg(feature = \"track_alloc_size\")]\n            alloc_size: layout.size(),\n            data,\n        };\n\n        ptr::write(ptr, x);\n\n        Arc {\n            p: ptr::NonNull::new_unchecked(ptr),\n            phantom: PhantomData,\n        }\n    }\n\n    /// Produce a pointer to the data that can be converted back\n    /// to an Arc. This is basically an `&Arc<T>`, without the extra indirection.\n    /// It has the benefits of an `&T` but also knows about the underlying refcount\n    /// and can be converted into more `Arc<T>`s if necessary.\n    #[inline]\n    pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {\n        ArcBorrow(&**self)\n    }\n\n    /// Returns the address on the heap of the Arc itself -- not the T within it -- for memory\n    /// reporting.\n    ///\n    /// If this is a static reference, this returns null.\n    pub fn heap_ptr(&self) -> *const c_void {\n        if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {\n            ptr::null()\n        } else {\n            self.p.as_ptr() as *const ArcInner<T> as *const c_void\n        }\n    }\n}\n\nimpl<T: ?Sized> Arc<T> {\n    #[inline]\n    fn inner(&self) -> &ArcInner<T> {\n        // This unsafety is ok because while this arc is alive we're guaranteed\n        // that the inner pointer is valid. Furthermore, we know that the\n        // `ArcInner` structure itself is `Sync` because the inner data is\n        // `Sync` as well, so we're ok loaning out an immutable pointer to these\n        // contents.\n        unsafe { &*self.ptr() }\n    }\n\n    #[inline(always)]\n    fn record_drop(&self) {\n        #[cfg(feature = \"gecko_refcount_logging\")]\n        unsafe {\n            NS_LogDtor(self.ptr() as *mut _, b\"ServoArc\\0\".as_ptr() as *const _, 8);\n        }\n    }\n\n    /// Marks this `Arc` as intentionally leaked for the purposes of refcount\n    /// logging.\n    ///\n    /// It's a logic error to call this more than once, but it's not unsafe, as\n    /// it'd just report negative leaks.\n    #[inline(always)]\n    pub fn mark_as_intentionally_leaked(&self) {\n        self.record_drop();\n    }\n\n    // Non-inlined part of `drop`. Just invokes the destructor and calls the\n    // refcount logging machinery if enabled.\n    #[inline(never)]\n    unsafe fn drop_slow(&mut self) {\n        self.record_drop();\n        let inner = self.ptr();\n\n        let layout = Layout::for_value(&*inner);\n        #[cfg(feature = \"track_alloc_size\")]\n        let layout = Layout::from_size_align_unchecked((*inner).alloc_size, layout.align());\n\n        std::ptr::drop_in_place(inner);\n        alloc::dealloc(inner as *mut _, layout);\n    }\n\n    /// Test pointer equality between the two Arcs, i.e. they must be the _same_\n    /// allocation\n    #[inline]\n    pub fn ptr_eq(this: &Self, other: &Self) -> bool {\n        this.raw_ptr() == other.raw_ptr()\n    }\n\n    fn ptr(&self) -> *mut ArcInner<T> {\n        self.p.as_ptr()\n    }\n\n    /// Returns a raw ptr to the underlying allocation.\n    pub fn raw_ptr(&self) -> ptr::NonNull<()> {\n        self.p.cast()\n    }\n}\n\n#[cfg(feature = \"gecko_refcount_logging\")]\nextern \"C\" {\n    fn NS_LogCtor(\n        aPtr: *mut std::os::raw::c_void,\n        aTypeName: *const std::os::raw::c_char,\n        aSize: u32,\n    );\n    fn NS_LogDtor(\n        aPtr: *mut std::os::raw::c_void,\n        aTypeName: *const std::os::raw::c_char,\n        aSize: u32,\n    );\n}\n\nimpl<T: ?Sized> Clone for Arc<T> {\n    #[inline]\n    fn clone(&self) -> Self {\n        // NOTE(emilio): If you change anything here, make sure that the\n        // implementation in layout/style/ServoStyleConstsInlines.h matches!\n        //\n        // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since\n        // `count` never changes between STATIC_REFCOUNT and other values.\n        if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {\n            // Using a relaxed ordering is alright here, as knowledge of the\n            // original reference prevents other threads from erroneously deleting\n            // the object.\n            //\n            // As explained in the [Boost documentation][1], Increasing the\n            // reference counter can always be done with memory_order_relaxed: New\n            // references to an object can only be formed from an existing\n            // reference, and passing an existing reference from one thread to\n            // another must already provide any required synchronization.\n            //\n            // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)\n            let old_size = self.inner().count.fetch_add(1, Relaxed);\n\n            // However we need to guard against massive refcounts in case someone\n            // is `mem::forget`ing Arcs. If we don't do this the count can overflow\n            // and users will use-after free. We racily saturate to `isize::MAX` on\n            // the assumption that there aren't ~2 billion threads incrementing\n            // the reference count at once. This branch will never be taken in\n            // any realistic program.\n            //\n            // We abort because such a program is incredibly degenerate, and we\n            // don't care to support it.\n            if old_size > MAX_REFCOUNT {\n                process::abort();\n            }\n        }\n\n        unsafe {\n            Arc {\n                p: ptr::NonNull::new_unchecked(self.ptr()),\n                phantom: PhantomData,\n            }\n        }\n    }\n}\n\nimpl<T: ?Sized> Deref for Arc<T> {\n    type Target = T;\n\n    #[inline]\n    fn deref(&self) -> &T {\n        &self.inner().data\n    }\n}\n\nimpl<T: Clone> Arc<T> {\n    /// Makes a mutable reference to the `Arc`, cloning if necessary\n    ///\n    /// This is functionally equivalent to [`Arc::make_mut`][mm] from the standard library.\n    ///\n    /// If this `Arc` is uniquely owned, `make_mut()` will provide a mutable\n    /// reference to the contents. If not, `make_mut()` will create a _new_ `Arc`\n    /// with a copy of the contents, update `this` to point to it, and provide\n    /// a mutable reference to its contents.\n    ///\n    /// This is useful for implementing copy-on-write schemes where you wish to\n    /// avoid copying things if your `Arc` is not shared.\n    ///\n    /// [mm]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.make_mut\n    #[inline]\n    pub fn make_mut(this: &mut Self) -> &mut T {\n        if !this.is_unique() {\n            // Another pointer exists; clone\n            *this = Arc::new((**this).clone());\n        }\n\n        unsafe {\n            // This unsafety is ok because we're guaranteed that the pointer\n            // returned is the *only* pointer that will ever be returned to T. Our\n            // reference count is guaranteed to be 1 at this point, and we required\n            // the Arc itself to be `mut`, so we're returning the only possible\n            // reference to the inner data.\n            &mut (*this.ptr()).data\n        }\n    }\n}\n\nimpl<T: ?Sized> Arc<T> {\n    /// Provides mutable access to the contents _if_ the `Arc` is uniquely owned.\n    #[inline]\n    pub fn get_mut(this: &mut Self) -> Option<&mut T> {\n        if this.is_unique() {\n            unsafe {\n                // See make_mut() for documentation of the threadsafety here.\n                Some(&mut (*this.ptr()).data)\n            }\n        } else {\n            None\n        }\n    }\n\n    /// Whether or not the `Arc` is a static reference.\n    #[inline]\n    pub fn is_static(&self) -> bool {\n        // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since\n        // `count` never changes between STATIC_REFCOUNT and other values.\n        self.inner().count.load(Relaxed) == STATIC_REFCOUNT\n    }\n\n    /// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not\n    /// a static reference.\n    #[inline]\n    pub fn is_unique(&self) -> bool {\n        // See the extensive discussion in [1] for why this needs to be Acquire.\n        //\n        // [1] https://github.com/servo/servo/issues/21186\n        self.inner().count.load(Acquire) == 1\n    }\n}\n\nimpl<T: ?Sized> Drop for Arc<T> {\n    #[inline]\n    fn drop(&mut self) {\n        // NOTE(emilio): If you change anything here, make sure that the\n        // implementation in layout/style/ServoStyleConstsInlines.h matches!\n        if self.is_static() {\n            return;\n        }\n\n        // Because `fetch_sub` is already atomic, we do not need to synchronize\n        // with other threads unless we are going to delete the object.\n        if self.inner().count.fetch_sub(1, Release) != 1 {\n            return;\n        }\n\n        // FIXME(bholley): Use the updated comment when [2] is merged.\n        //\n        // This load is needed to prevent reordering of use of the data and\n        // deletion of the data.  Because it is marked `Release`, the decreasing\n        // of the reference count synchronizes with this `Acquire` load. This\n        // means that use of the data happens before decreasing the reference\n        // count, which happens before this load, which happens before the\n        // deletion of the data.\n        //\n        // As explained in the [Boost documentation][1],\n        //\n        // > It is important to enforce any possible access to the object in one\n        // > thread (through an existing reference) to *happen before* deleting\n        // > the object in a different thread. This is achieved by a \"release\"\n        // > operation after dropping a reference (any access to the object\n        // > through this reference must obviously happened before), and an\n        // > \"acquire\" operation before deleting the object.\n        //\n        // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)\n        // [2]: https://github.com/rust-lang/rust/pull/41714\n        self.inner().count.load(Acquire);\n\n        unsafe {\n            self.drop_slow();\n        }\n    }\n}\n\nimpl<T: ?Sized + PartialEq> PartialEq for Arc<T> {\n    fn eq(&self, other: &Arc<T>) -> bool {\n        Self::ptr_eq(self, other) || *(*self) == *(*other)\n    }\n\n    fn ne(&self, other: &Arc<T>) -> bool {\n        !Self::ptr_eq(self, other) && *(*self) != *(*other)\n    }\n}\n\nimpl<T: ?Sized + PartialOrd> PartialOrd for Arc<T> {\n    fn partial_cmp(&self, other: &Arc<T>) -> Option<Ordering> {\n        (**self).partial_cmp(&**other)\n    }\n\n    fn lt(&self, other: &Arc<T>) -> bool {\n        *(*self) < *(*other)\n    }\n\n    fn le(&self, other: &Arc<T>) -> bool {\n        *(*self) <= *(*other)\n    }\n\n    fn gt(&self, other: &Arc<T>) -> bool {\n        *(*self) > *(*other)\n    }\n\n    fn ge(&self, other: &Arc<T>) -> bool {\n        *(*self) >= *(*other)\n    }\n}\nimpl<T: ?Sized + Ord> Ord for Arc<T> {\n    fn cmp(&self, other: &Arc<T>) -> Ordering {\n        (**self).cmp(&**other)\n    }\n}\nimpl<T: ?Sized + Eq> Eq for Arc<T> {}\n\nimpl<T: ?Sized + fmt::Display> fmt::Display for Arc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Display::fmt(&**self, f)\n    }\n}\n\nimpl<T: ?Sized + fmt::Debug> fmt::Debug for Arc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Debug::fmt(&**self, f)\n    }\n}\n\nimpl<T: ?Sized> fmt::Pointer for Arc<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.ptr(), f)\n    }\n}\n\nimpl<T: Default> Default for Arc<T> {\n    fn default() -> Arc<T> {\n        Arc::new(Default::default())\n    }\n}\n\nimpl<T: ?Sized + Hash> Hash for Arc<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        (**self).hash(state)\n    }\n}\n\nimpl<T> From<T> for Arc<T> {\n    #[inline]\n    fn from(t: T) -> Self {\n        Arc::new(t)\n    }\n}\n\nimpl<T: ?Sized> borrow::Borrow<T> for Arc<T> {\n    #[inline]\n    fn borrow(&self) -> &T {\n        &**self\n    }\n}\n\nimpl<T: ?Sized> AsRef<T> for Arc<T> {\n    #[inline]\n    fn as_ref(&self) -> &T {\n        &**self\n    }\n}\n\nunsafe impl<T: ?Sized> StableDeref for Arc<T> {}\nunsafe impl<T: ?Sized> CloneStableDeref for Arc<T> {}\n\n#[cfg(feature = \"servo\")]\nimpl<'de, T: Deserialize<'de>> Deserialize<'de> for Arc<T> {\n    fn deserialize<D>(deserializer: D) -> Result<Arc<T>, D::Error>\n    where\n        D: ::serde::de::Deserializer<'de>,\n    {\n        T::deserialize(deserializer).map(Arc::new)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<T: Serialize> Serialize for Arc<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: ::serde::ser::Serializer,\n    {\n        (**self).serialize(serializer)\n    }\n}\n\n/// Structure to allow Arc-managing some fixed-sized data and a variably-sized\n/// slice in a single allocation.\n///\n/// cbindgen:derive-eq=false\n/// cbindgen:derive-neq=false\n#[derive(Eq)]\n#[repr(C)]\npub struct HeaderSlice<H, T> {\n    /// The fixed-sized data.\n    pub header: H,\n\n    /// The length of the slice at our end.\n    len: usize,\n\n    /// The dynamically-sized data.\n    data: [T; 0],\n}\n\nimpl<H: PartialEq, T: PartialEq> PartialEq for HeaderSlice<H, T> {\n    fn eq(&self, other: &Self) -> bool {\n        self.header == other.header && self.slice() == other.slice()\n    }\n}\n\nimpl<H, T> Drop for HeaderSlice<H, T> {\n    fn drop(&mut self) {\n        unsafe {\n            let mut ptr = self.data_mut();\n            for _ in 0..self.len {\n                std::ptr::drop_in_place(ptr);\n                ptr = ptr.offset(1);\n            }\n        }\n    }\n}\n\nimpl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for HeaderSlice<H, T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_struct(\"HeaderSlice\")\n            .field(\"header\", &self.header)\n            .field(\"slice\", &self.slice())\n            .finish()\n    }\n}\n\nimpl<H, T> HeaderSlice<H, T> {\n    /// Returns the dynamically sized slice in this HeaderSlice.\n    #[inline(always)]\n    pub fn slice(&self) -> &[T] {\n        unsafe { std::slice::from_raw_parts(self.data(), self.len) }\n    }\n\n    #[inline(always)]\n    fn data(&self) -> *const T {\n        std::ptr::addr_of!(self.data) as *const _\n    }\n\n    #[inline(always)]\n    fn data_mut(&mut self) -> *mut T {\n        std::ptr::addr_of_mut!(self.data) as *mut _\n    }\n\n    /// Returns the dynamically sized slice in this HeaderSlice.\n    #[inline(always)]\n    pub fn slice_mut(&mut self) -> &mut [T] {\n        unsafe { std::slice::from_raw_parts_mut(self.data_mut(), self.len) }\n    }\n\n    /// Returns the len of the slice.\n    #[inline(always)]\n    pub fn len(&self) -> usize {\n        self.len\n    }\n}\n\nimpl<H, T> Arc<HeaderSlice<H, T>> {\n    /// Creates an Arc for a HeaderSlice using the given header struct and\n    /// iterator to generate the slice.\n    ///\n    /// `is_static` indicates whether to create a static Arc.\n    ///\n    /// `alloc` is used to get a pointer to the memory into which the\n    /// dynamically sized ArcInner<HeaderSlice<H, T>> value will be\n    /// written.  If `is_static` is true, then `alloc` must return a\n    /// pointer into some static memory allocation.  If it is false,\n    /// then `alloc` must return an allocation that can be dellocated\n    /// by calling Box::from_raw::<ArcInner<HeaderSlice<H, T>>> on it.\n    #[inline]\n    pub fn from_header_and_iter_alloc<F, I>(\n        alloc: F,\n        header: H,\n        mut items: I,\n        num_items: usize,\n        is_static: bool,\n    ) -> Self\n    where\n        F: FnOnce(Layout) -> *mut u8,\n        I: Iterator<Item = T>,\n    {\n        assert_ne!(size_of::<T>(), 0, \"Need to think about ZST\");\n\n        let layout = Layout::new::<ArcInner<HeaderSlice<H, T>>>();\n        debug_assert!(layout.align() >= align_of::<T>());\n        debug_assert!(layout.align() >= align_of::<usize>());\n        let array_layout = Layout::array::<T>(num_items).expect(\"Overflow\");\n        let (layout, _offset) = layout.extend(array_layout).expect(\"Overflow\");\n        let p = unsafe {\n            // Allocate the buffer.\n            let buffer = alloc(layout);\n            let mut p = ptr::NonNull::new(buffer)\n                .unwrap_or_else(|| alloc::handle_alloc_error(layout))\n                .cast::<ArcInner<HeaderSlice<H, T>>>();\n\n            // Write the data.\n            //\n            // Note that any panics here (i.e. from the iterator) are safe, since\n            // we'll just leak the uninitialized memory.\n            let count = if is_static {\n                atomic::AtomicUsize::new(STATIC_REFCOUNT)\n            } else {\n                atomic::AtomicUsize::new(1)\n            };\n            ptr::write(&mut p.as_mut().count, count);\n            #[cfg(feature = \"track_alloc_size\")]\n            ptr::write(&mut p.as_mut().alloc_size, layout.size());\n            ptr::write(&mut p.as_mut().data.header, header);\n            ptr::write(&mut p.as_mut().data.len, num_items);\n            if num_items != 0 {\n                let mut current = std::ptr::addr_of_mut!(p.as_mut().data.data) as *mut T;\n                for _ in 0..num_items {\n                    ptr::write(\n                        current,\n                        items\n                            .next()\n                            .expect(\"ExactSizeIterator over-reported length\"),\n                    );\n                    current = current.offset(1);\n                }\n                // We should have consumed the buffer exactly, maybe accounting\n                // for some padding from the alignment.\n                debug_assert!(\n                    (buffer.add(layout.size()) as usize - current as *mut u8 as usize)\n                        < layout.align()\n                );\n            }\n            assert!(\n                items.next().is_none(),\n                \"ExactSizeIterator under-reported length\"\n            );\n            p\n        };\n        #[cfg(feature = \"gecko_refcount_logging\")]\n        unsafe {\n            if !is_static {\n                // FIXME(emilio): Would be so amazing to have\n                // std::intrinsics::type_name() around.\n                NS_LogCtor(p.as_ptr() as *mut _, b\"ServoArc\\0\".as_ptr() as *const _, 8)\n            }\n        }\n\n        // Return the fat Arc.\n        assert_eq!(\n            size_of::<Self>(),\n            size_of::<usize>(),\n            \"The Arc should be thin\"\n        );\n\n        Arc {\n            p,\n            phantom: PhantomData,\n        }\n    }\n\n    /// Creates an Arc for a HeaderSlice using the given header struct and iterator to generate the\n    /// slice. Panics if num_items doesn't match the number of items.\n    #[inline]\n    pub fn from_header_and_iter_with_size<I>(header: H, items: I, num_items: usize) -> Self\n    where\n        I: Iterator<Item = T>,\n    {\n        Arc::from_header_and_iter_alloc(\n            |layout| unsafe { alloc::alloc(layout) },\n            header,\n            items,\n            num_items,\n            /* is_static = */ false,\n        )\n    }\n\n    /// Creates an Arc for a HeaderSlice using the given header struct and\n    /// iterator to generate the slice. The resulting Arc will be fat.\n    #[inline]\n    pub fn from_header_and_iter<I>(header: H, items: I) -> Self\n    where\n        I: Iterator<Item = T> + ExactSizeIterator,\n    {\n        let len = items.len();\n        Self::from_header_and_iter_with_size(header, items, len)\n    }\n}\n\n/// This is functionally equivalent to Arc<(H, [T])>\n///\n/// When you create an `Arc` containing a dynamically sized type like a slice, the `Arc` is\n/// represented on the stack as a \"fat pointer\", where the length of the slice is stored alongside\n/// the `Arc`'s pointer. In some situations you may wish to have a thin pointer instead, perhaps\n/// for FFI compatibility or space efficiency. `ThinArc` solves this by storing the length in the\n/// allocation itself, via `HeaderSlice`.\npub type ThinArc<H, T> = Arc<HeaderSlice<H, T>>;\n\n/// See `ArcUnion`. This is a version that works for `ThinArc`s.\npub type ThinArcUnion<H1, T1, H2, T2> = ArcUnion<HeaderSlice<H1, T1>, HeaderSlice<H2, T2>>;\n\nimpl<H, T> UniqueArc<HeaderSlice<H, T>> {\n    #[inline]\n    pub fn from_header_and_iter<I>(header: H, items: I) -> Self\n    where\n        I: Iterator<Item = T> + ExactSizeIterator,\n    {\n        Self(Arc::from_header_and_iter(header, items))\n    }\n\n    #[inline]\n    pub fn from_header_and_iter_with_size<I>(header: H, items: I, num_items: usize) -> Self\n    where\n        I: Iterator<Item = T>,\n    {\n        Self(Arc::from_header_and_iter_with_size(\n            header, items, num_items,\n        ))\n    }\n\n    /// Returns a mutable reference to the header.\n    pub fn header_mut(&mut self) -> &mut H {\n        // We know this to be uniquely owned\n        unsafe { &mut (*self.0.ptr()).data.header }\n    }\n\n    /// Returns a mutable reference to the slice.\n    pub fn data_mut(&mut self) -> &mut [T] {\n        // We know this to be uniquely owned\n        unsafe { (*self.0.ptr()).data.slice_mut() }\n    }\n}\n\n/// A \"borrowed `Arc`\". This is a pointer to\n/// a T that is known to have been allocated within an\n/// `Arc`.\n///\n/// This is equivalent in guarantees to `&Arc<T>`, however it is\n/// a bit more flexible. To obtain an `&Arc<T>` you must have\n/// an `Arc<T>` instance somewhere pinned down until we're done with it.\n/// It's also a direct pointer to `T`, so using this involves less pointer-chasing\n///\n/// However, C++ code may hand us refcounted things as pointers to T directly,\n/// so we have to conjure up a temporary `Arc` on the stack each time.\n///\n/// `ArcBorrow` lets us deal with borrows of known-refcounted objects\n/// without needing to worry about where the `Arc<T>` is.\n#[derive(Debug, Eq, PartialEq)]\npub struct ArcBorrow<'a, T: 'a>(&'a T);\n\nimpl<'a, T> Copy for ArcBorrow<'a, T> {}\nimpl<'a, T> Clone for ArcBorrow<'a, T> {\n    #[inline]\n    fn clone(&self) -> Self {\n        *self\n    }\n}\n\nimpl<'a, T> ArcBorrow<'a, T> {\n    /// Clone this as an `Arc<T>`. This bumps the refcount.\n    #[inline]\n    pub fn clone_arc(&self) -> Arc<T> {\n        let arc = unsafe { Arc::from_raw(self.0) };\n        // addref it!\n        mem::forget(arc.clone());\n        arc\n    }\n\n    /// For constructing from a reference known to be Arc-backed,\n    /// e.g. if we obtain such a reference over FFI\n    #[inline]\n    pub unsafe fn from_ref(r: &'a T) -> Self {\n        ArcBorrow(r)\n    }\n\n    /// Compare two `ArcBorrow`s via pointer equality. Will only return\n    /// true if they come from the same allocation\n    pub fn ptr_eq(this: &Self, other: &Self) -> bool {\n        this.0 as *const T == other.0 as *const T\n    }\n\n    /// Temporarily converts |self| into a bonafide Arc and exposes it to the\n    /// provided callback. The refcount is not modified.\n    #[inline]\n    pub fn with_arc<F, U>(&self, f: F) -> U\n    where\n        F: FnOnce(&Arc<T>) -> U,\n        T: 'static,\n    {\n        // Synthesize transient Arc, which never touches the refcount.\n        let transient = unsafe { mem::ManuallyDrop::new(Arc::from_raw(self.0)) };\n\n        // Expose the transient Arc to the callback, which may clone it if it wants.\n        let result = f(&transient);\n\n        // Forward the result.\n        result\n    }\n\n    /// Similar to deref, but uses the lifetime |a| rather than the lifetime of\n    /// self, which is incompatible with the signature of the Deref trait.\n    #[inline]\n    pub fn get(&self) -> &'a T {\n        self.0\n    }\n}\n\nimpl<'a, T> Deref for ArcBorrow<'a, T> {\n    type Target = T;\n\n    #[inline]\n    fn deref(&self) -> &T {\n        self.0\n    }\n}\n\n/// A tagged union that can represent `Arc<A>` or `Arc<B>` while only consuming a\n/// single word. The type is also `NonNull`, and thus can be stored in an Option\n/// without increasing size.\n///\n/// This is functionally equivalent to\n/// `enum ArcUnion<A, B> { First(Arc<A>), Second(Arc<B>)` but only takes up\n/// up a single word of stack space.\n///\n/// This could probably be extended to support four types if necessary.\npub struct ArcUnion<A, B> {\n    p: ptr::NonNull<()>,\n    phantom_a: PhantomData<A>,\n    phantom_b: PhantomData<B>,\n}\n\nunsafe impl<A: Sync + Send, B: Send + Sync> Send for ArcUnion<A, B> {}\nunsafe impl<A: Sync + Send, B: Send + Sync> Sync for ArcUnion<A, B> {}\n\nimpl<A: PartialEq, B: PartialEq> PartialEq for ArcUnion<A, B> {\n    fn eq(&self, other: &Self) -> bool {\n        use crate::ArcUnionBorrow::*;\n        match (self.borrow(), other.borrow()) {\n            (First(x), First(y)) => x == y,\n            (Second(x), Second(y)) => x == y,\n            (_, _) => false,\n        }\n    }\n}\n\nimpl<A: Eq, B: Eq> Eq for ArcUnion<A, B> {}\n\n/// This represents a borrow of an `ArcUnion`.\n#[derive(Debug)]\npub enum ArcUnionBorrow<'a, A: 'a, B: 'a> {\n    First(ArcBorrow<'a, A>),\n    Second(ArcBorrow<'a, B>),\n}\n\nimpl<A, B> ArcUnion<A, B> {\n    unsafe fn new(ptr: *mut ()) -> Self {\n        ArcUnion {\n            p: ptr::NonNull::new_unchecked(ptr),\n            phantom_a: PhantomData,\n            phantom_b: PhantomData,\n        }\n    }\n\n    /// Returns true if the two values are pointer-equal.\n    #[inline]\n    pub fn ptr_eq(this: &Self, other: &Self) -> bool {\n        this.p == other.p\n    }\n\n    #[inline]\n    pub fn ptr(&self) -> ptr::NonNull<()> {\n        self.p\n    }\n\n    /// Returns an enum representing a borrow of either A or B.\n    #[inline]\n    pub fn borrow(&self) -> ArcUnionBorrow<'_, A, B> {\n        if self.is_first() {\n            let ptr = self.p.as_ptr() as *const ArcInner<A>;\n            let borrow = unsafe { ArcBorrow::from_ref(&(*ptr).data) };\n            ArcUnionBorrow::First(borrow)\n        } else {\n            let ptr = ((self.p.as_ptr() as usize) & !0x1) as *const ArcInner<B>;\n            let borrow = unsafe { ArcBorrow::from_ref(&(*ptr).data) };\n            ArcUnionBorrow::Second(borrow)\n        }\n    }\n\n    /// Creates an `ArcUnion` from an instance of the first type.\n    pub fn from_first(other: Arc<A>) -> Self {\n        let union = unsafe { Self::new(other.ptr() as *mut _) };\n        mem::forget(other);\n        union\n    }\n\n    /// Creates an `ArcUnion` from an instance of the second type.\n    pub fn from_second(other: Arc<B>) -> Self {\n        let union = unsafe { Self::new(((other.ptr() as usize) | 0x1) as *mut _) };\n        mem::forget(other);\n        union\n    }\n\n    /// Returns true if this `ArcUnion` contains the first type.\n    pub fn is_first(&self) -> bool {\n        self.p.as_ptr() as usize & 0x1 == 0\n    }\n\n    /// Returns true if this `ArcUnion` contains the second type.\n    pub fn is_second(&self) -> bool {\n        !self.is_first()\n    }\n\n    /// Returns a borrow of the first type if applicable, otherwise `None`.\n    pub fn as_first(&self) -> Option<ArcBorrow<'_, A>> {\n        match self.borrow() {\n            ArcUnionBorrow::First(x) => Some(x),\n            ArcUnionBorrow::Second(_) => None,\n        }\n    }\n\n    /// Returns a borrow of the second type if applicable, otherwise None.\n    pub fn as_second(&self) -> Option<ArcBorrow<'_, B>> {\n        match self.borrow() {\n            ArcUnionBorrow::First(_) => None,\n            ArcUnionBorrow::Second(x) => Some(x),\n        }\n    }\n}\n\nimpl<A, B> Clone for ArcUnion<A, B> {\n    fn clone(&self) -> Self {\n        match self.borrow() {\n            ArcUnionBorrow::First(x) => ArcUnion::from_first(x.clone_arc()),\n            ArcUnionBorrow::Second(x) => ArcUnion::from_second(x.clone_arc()),\n        }\n    }\n}\n\nimpl<A, B> Drop for ArcUnion<A, B> {\n    fn drop(&mut self) {\n        match self.borrow() {\n            ArcUnionBorrow::First(x) => unsafe {\n                let _ = Arc::from_raw(&*x);\n            },\n            ArcUnionBorrow::Second(x) => unsafe {\n                let _ = Arc::from_raw(&*x);\n            },\n        }\n    }\n}\n\nimpl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for ArcUnion<A, B> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Debug::fmt(&self.borrow(), f)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::{Arc, ThinArc};\n    use std::clone::Clone;\n    use std::ops::Drop;\n    use std::sync::atomic;\n    use std::sync::atomic::Ordering::{Acquire, SeqCst};\n\n    #[derive(PartialEq)]\n    struct Canary(*mut atomic::AtomicUsize);\n\n    impl Drop for Canary {\n        fn drop(&mut self) {\n            unsafe {\n                (*self.0).fetch_add(1, SeqCst);\n            }\n        }\n    }\n\n    #[test]\n    fn empty_thin() {\n        let x = Arc::from_header_and_iter(100u32, std::iter::empty::<i32>());\n        assert_eq!(x.header, 100);\n        assert!(x.slice().is_empty());\n    }\n\n    #[test]\n    fn thin_assert_padding() {\n        #[derive(Clone, Default)]\n        #[repr(C)]\n        struct Padded {\n            i: u16,\n        }\n\n        // The header will have more alignment than `Padded`\n        let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];\n        let a = ThinArc::from_header_and_iter(0i32, items.into_iter());\n        assert_eq!(a.len(), 2);\n        assert_eq!(a.slice()[0].i, 0xdead);\n        assert_eq!(a.slice()[1].i, 0xbeef);\n    }\n\n    #[test]\n    fn slices_and_thin() {\n        let mut canary = atomic::AtomicUsize::new(0);\n        let c = Canary(&mut canary as *mut atomic::AtomicUsize);\n        let v = vec![5, 6];\n        {\n            let x = Arc::from_header_and_iter(c, v.into_iter());\n            let _ = x.clone();\n            let _ = x == x;\n        }\n        assert_eq!(canary.load(Acquire), 1);\n    }\n}\n"
  },
  {
    "path": "shell.nix",
    "content": "with import (builtins.fetchTarball {\n  url = \"https://github.com/NixOS/nixpkgs/archive/46ae0210ce163b3cba6c7da08840c1d63de9c701.tar.gz\";\n}) {};\nstdenv.mkDerivation rec {\n  name = \"style-sync-shell\";\n}\n"
  },
  {
    "path": "start-rebase.sh",
    "content": "#!/bin/sh\n# Usage: start-rebase.sh <new base> [extra git-rebase(1) arguments ...]\n# Equivalent to git rebase --onto <new base> <last old upstream commit>.\nset -eu\n\nnew_base=$1; shift\nfirst_commit=$(git log --pretty=\\%H --grep='Servo initial downstream commit')\nold_base=$first_commit~\n\ngit rebase --onto \"$new_base\" \"$old_base\" \"$@\"\n"
  },
  {
    "path": "style/Cargo.toml",
    "content": "[package]\nname = \"stylo\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/servo/stylo\"\nedition = \"2021\"\ndescription = \"The Stylo CSS engine\"\nreadme = \"../README.md\"\n\nbuild = \"build.rs\"\n\n# https://github.com/rust-lang/cargo/issues/3544\nlinks = \"servo_style_crate\"\n\n[lib]\nname = \"style\"\npath = \"lib.rs\"\ndoctest = false\n\n[features]\ndefault = [\"servo\"]\ngecko = [\n    \"bindgen\",\n    \"malloc_size_of/gecko\",\n    \"mozbuild\",\n    \"nsstring\",\n    \"regex\",\n    \"serde\",\n    \"style_traits/gecko\",\n    \"toml\",\n    \"selectors/to_shmem\",\n    \"to_shmem/gecko\",\n]\nservo = [\n    \"cssparser/serde\",\n    \"encoding_rs\",\n    \"malloc_size_of/servo\",\n    \"web_atoms\",\n    \"mime\",\n    \"serde\",\n    \"servo_arc/servo\",\n    \"stylo_atoms\",\n    \"string_cache\",\n    \"style_traits/servo\",\n    \"url\",\n    \"selectors/to_shmem\",\n    \"to_shmem/servo\",\n]\ngecko_debug = []\ngecko_refcount_logging = []\nnsstring = []\n\n[dependencies]\napp_units = \"0.7.8\"\narrayvec = \"0.7\"\natomic_refcell = \"0.1\"\nbitflags = \"2\"\nbyteorder = \"1.0\"\ncssparser = \"0.37\"\nderive_more = { version = \"2\", features = [\"add\", \"add_assign\", \"deref\", \"deref_mut\", \"from\"] }\ndom = { workspace = true }\nnew_debug_unreachable = \"1.0\"\nencoding_rs = {version = \"0.8\", optional = true}\neuclid = \"0.22\"\nrustc-hash = \"2.1.1\"\nicu_segmenter = { version = \">= 1.5, <= 2.*\", default-features = false, features = [\"auto\", \"compiled_data\"] }\nindexmap = {version = \"2\", features = [\"std\"]}\nitertools = \"0.14\"\nitoa = \"1.0\"\nlog = \"0.4\"\nmalloc_size_of = { workspace = true }\nmalloc_size_of_derive = \"0.1\"\nweb_atoms = { version = \"0.2.0\", optional = true }\nmime = { version = \"0.3.13\", optional = true }\nnum_cpus = {version = \"1.1.0\"}\nnum-integer = \"0.1\"\nnum-traits = \"0.2\"\nnum-derive = \"0.4\"\nparking_lot = \"0.12\"\nprecomputed-hash = \"0.1.1\"\nrayon = \"1\"\nrayon-core = \"1\"\nselectors = { workspace = true }\nserde = {version = \"1.0\", optional = true, features = [\"derive\"]}\nservo_arc = { workspace = true}\nstylo_atoms = { workspace = true, optional = true}\nsmallbitvec = \"2.3.0\"\nsmallvec = \"1.0\"\nstatic_assertions = \"1.1\"\nstatic_prefs = { workspace = true}\nstring_cache = { version = \"0.9\", optional = true }\nstrum = \"0.28\"\nstrum_macros = \"0.28\"\nstyle_derive = { workspace = true }\nstyle_traits = { workspace = true }\nto_shmem = { workspace = true}\nto_shmem_derive = { workspace = true }\nthin-vec = \"0.2.1\"\nuluru = \"3.0\"\nvoid = \"1.0.2\"\nurl = { version = \"2.5\", optional = true, features = [\"serde\"] }\n\n[build-dependencies]\nlog = { version = \"0.4\", features = [\"std\"] }\nbindgen = {version = \"0.69\", optional = true, default-features = false}\nregex = {version = \"1.0\", optional = true, default-features = false, features = [\"perf\", \"std\"]}\nwalkdir = \"2.1.4\"\ntoml = {version = \"0.5\", optional = true, default-features = false}\nmozbuild = {version = \"0.1\", optional = true}\n"
  },
  {
    "path": "style/README.md",
    "content": "servo-style\n===========\n\nStyle system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing.\n\n * [Documentation](https://book.servo.org/architecture/style.html).\n"
  },
  {
    "path": "style/applicable_declarations.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Applicable declarations management.\n\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::rule_tree::{CascadeLevel, RuleCascadeFlags, StyleSource};\nuse crate::shared_lock::Locked;\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\n\n/// List of applicable declarations. This is a transient structure that shuttles\n/// declarations between selector matching and inserting into the rule tree, and\n/// therefore we want to avoid heap-allocation where possible.\n///\n/// In measurements on wikipedia, we pretty much never have more than 8 applicable\n/// declarations, so we could consider making this 8 entries instead of 16.\n/// However, it may depend a lot on workload, and stack space is cheap.\npub type ApplicableDeclarationList = SmallVec<[ApplicableDeclarationBlock; 16]>;\n\n/// Blink uses 18 bits to store source order, and does not check overflow [1].\n/// That's a limit that could be reached in realistic webpages, so we use\n/// 24 bits and enforce defined behavior in the overflow case.\n///\n/// Note that right now this restriction could be lifted if wanted (because we\n/// no longer stash the cascade level in the remaining bits), but we keep it in\n/// place in case we come up with a use-case for them, lacking reports of the\n/// current limit being too small.\n///\n/// [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/css/\n///     RuleSet.h?l=128&rcl=90140ab80b84d0f889abc253410f44ed54ae04f3\nconst SOURCE_ORDER_BITS: usize = 24;\nconst SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;\nconst SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX;\n\n/// The cascade-level+layer order of this declaration.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\npub struct CascadePriority {\n    cascade_level: CascadeLevel,\n    flags: RuleCascadeFlags,\n    layer_order: LayerOrder,\n}\n\nconst_assert_eq!(\n    std::mem::size_of::<CascadePriority>(),\n    std::mem::size_of::<u32>()\n);\n\nimpl PartialOrd for CascadePriority {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for CascadePriority {\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        self.cascade_level.cmp(&other.cascade_level).then_with(|| {\n            let ordering = self.layer_order.cmp(&other.layer_order);\n            if ordering == std::cmp::Ordering::Equal {\n                return ordering;\n            }\n            // https://drafts.csswg.org/css-cascade-5/#cascade-layering\n            //\n            //     Cascade layers (like declarations) are ordered by order\n            //     of appearance. When comparing declarations that belong to\n            //     different layers, then for normal rules the declaration\n            //     whose cascade layer is last wins, and for important rules\n            //     the declaration whose cascade layer is first wins.\n            //\n            // But the style attribute layer for some reason is special.\n            if self.cascade_level.is_important()\n                && !self.layer_order.is_style_attribute_layer()\n                && !other.layer_order.is_style_attribute_layer()\n            {\n                ordering.reverse()\n            } else {\n                ordering\n            }\n        })\n    }\n}\n\n/// The kind of revert that we're applying.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum RevertKind {\n    /// revert\n    Origin,\n    /// revert-layer\n    Layer,\n    /// revert-rule\n    Rule,\n}\n\nimpl CascadePriority {\n    /// Construct a new CascadePriority for a given (level, order, flags) triple.\n    pub fn new(\n        cascade_level: CascadeLevel,\n        layer_order: LayerOrder,\n        flags: RuleCascadeFlags,\n    ) -> Self {\n        Self {\n            cascade_level,\n            flags,\n            layer_order,\n        }\n    }\n\n    /// Returns the flags.\n    #[inline]\n    pub fn flags(&self) -> RuleCascadeFlags {\n        self.flags\n    }\n\n    /// Set given flags.\n    #[inline]\n    pub fn set_flags(&mut self, flags: RuleCascadeFlags) {\n        self.flags.insert(flags);\n    }\n\n    /// Returns the layer order.\n    #[inline]\n    pub fn layer_order(&self) -> LayerOrder {\n        self.layer_order\n    }\n\n    /// Returns the cascade level.\n    #[inline]\n    pub fn cascade_level(&self) -> CascadeLevel {\n        self.cascade_level\n    }\n\n    /// Whether this declaration should be allowed if `revert` / `revert-layer` / `revert-rule`\n    /// have been specified on a given origin.\n    ///\n    /// `self` is the priority at which the revert has been specified.\n    pub fn allows_when_reverted(&self, other: &Self, kind: RevertKind) -> bool {\n        match kind {\n            RevertKind::Origin => {\n                other.cascade_level.origin().origin() < self.cascade_level.origin().origin()\n            },\n            RevertKind::Layer => other.unimportant() < self.unimportant(),\n            // Any other declaration for the same property we apply in the cascade needs to come\n            // from another rule effectively.\n            RevertKind::Rule => true,\n        }\n    }\n\n    /// Convert this priority from \"important\" to \"non-important\", if needed.\n    pub fn unimportant(&self) -> Self {\n        Self {\n            cascade_level: self.cascade_level.unimportant(),\n            flags: self.flags,\n            layer_order: self.layer_order,\n        }\n    }\n\n    /// Convert this priority from \"non-important\" to \"important\", if needed.\n    pub fn important(&self) -> Self {\n        Self {\n            cascade_level: self.cascade_level.important(),\n            flags: self.flags,\n            layer_order: self.layer_order,\n        }\n    }\n\n    /// The same tree, in author origin, at the root layer.\n    pub fn same_tree_author_normal_at_root_layer() -> Self {\n        Self::new(\n            CascadeLevel::same_tree_author_normal(),\n            LayerOrder::root(),\n            RuleCascadeFlags::empty(),\n        )\n    }\n}\n\n/// Proximity to the scope root.\n///\n/// https://drafts.csswg.org/css-cascade-6/#cascade-proximity\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\npub struct ScopeProximity(u16);\n\nimpl PartialOrd for ScopeProximity {\n    #[inline]\n    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl Ord for ScopeProximity {\n    fn cmp(&self, other: &Self) -> std::cmp::Ordering {\n        // Lower proximity to scope root wins\n        other.0.cmp(&self.0)\n    }\n}\n\n/// Sacrifice the largest possible value for infinity. This makes the comparison\n/// trivial.\nconst PROXIMITY_INFINITY: u16 = u16::MAX;\n\nimpl ScopeProximity {\n    /// Construct a new scope proximity.\n    pub fn new(proximity: usize) -> Self {\n        if cfg!(debug_assertions) && proximity >= PROXIMITY_INFINITY as usize {\n            warn!(\"Proximity out of bounds\");\n        }\n        Self(proximity.clamp(0, (PROXIMITY_INFINITY - 1) as usize) as u16)\n    }\n\n    /// Create a scope proximity for delcarations outside of any scope root.\n    pub fn infinity() -> Self {\n        Self(PROXIMITY_INFINITY)\n    }\n\n    /// If the proximity is finite, get the value.\n    pub fn get(&self) -> Option<u16> {\n        (self.0 != PROXIMITY_INFINITY).then(|| self.0)\n    }\n}\n\n/// A property declaration together with its precedence among rules of equal\n/// specificity so that we can sort them.\n///\n/// This represents the declarations in a given declaration block for a given\n/// importance.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq)]\npub struct ApplicableDeclarationBlock {\n    /// The style source, either a style rule, or a property declaration block.\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub source: StyleSource,\n    /// Order of appearance in which this rule appears - Set to 0 if not relevant\n    /// (e.g. Declaration from `style=\"/*...*/\"`, presentation hints, animations\n    /// - See `CascadePriority` instead).\n    source_order: u32,\n    /// The specificity of the selector.\n    pub specificity: u32,\n    /// The proximity to the scope root.\n    pub scope_proximity: ScopeProximity,\n    /// The cascade priority of the rule.\n    pub cascade_priority: CascadePriority,\n}\n\nimpl ApplicableDeclarationBlock {\n    /// Constructs an applicable declaration block from a given property\n    /// declaration block and importance.\n    #[inline]\n    pub fn from_declarations(\n        declarations: Arc<Locked<PropertyDeclarationBlock>>,\n        level: CascadeLevel,\n        layer_order: LayerOrder,\n    ) -> Self {\n        ApplicableDeclarationBlock {\n            source: StyleSource::from_declarations(declarations),\n            source_order: 0,\n            specificity: 0,\n            scope_proximity: ScopeProximity::infinity(),\n            cascade_priority: CascadePriority::new(level, layer_order, RuleCascadeFlags::empty()),\n        }\n    }\n\n    /// Constructs an applicable declaration block from the given components.\n    #[inline]\n    pub fn new(\n        source: StyleSource,\n        source_order: u32,\n        level: CascadeLevel,\n        specificity: u32,\n        layer_order: LayerOrder,\n        scope_proximity: ScopeProximity,\n        flags: RuleCascadeFlags,\n    ) -> Self {\n        ApplicableDeclarationBlock {\n            source,\n            source_order: source_order & SOURCE_ORDER_MASK,\n            specificity,\n            scope_proximity,\n            cascade_priority: CascadePriority::new(level, layer_order, flags),\n        }\n    }\n\n    /// Returns the source order of the block.\n    #[inline]\n    pub fn source_order(&self) -> u32 {\n        self.source_order\n    }\n\n    /// Returns the cascade level of the block.\n    #[inline]\n    pub fn level(&self) -> CascadeLevel {\n        self.cascade_priority.cascade_level()\n    }\n\n    /// Returns the cascade level of the block.\n    #[inline]\n    pub fn layer_order(&self) -> LayerOrder {\n        self.cascade_priority.layer_order()\n    }\n\n    /// Returns the scope proximity of the block.\n    #[inline]\n    pub fn scope_proximity(&self) -> ScopeProximity {\n        self.scope_proximity\n    }\n\n    /// Convenience method to consume self and return the right thing for the\n    /// rule tree to iterate over.\n    #[inline]\n    pub fn for_rule_tree(self) -> (StyleSource, CascadePriority) {\n        (self.source, self.cascade_priority)\n    }\n\n    /// Return the key used to sort applicable declarations.\n    #[inline]\n    pub fn sort_key(&self) -> (LayerOrder, u32, ScopeProximity, u32) {\n        (\n            self.layer_order(),\n            self.specificity,\n            self.scope_proximity(),\n            self.source_order(),\n        )\n    }\n}\n\n// Size of this struct determines sorting and selector-matching performance.\nsize_of_test!(ApplicableDeclarationBlock, 24);\n"
  },
  {
    "path": "style/author_styles.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A set of author stylesheets and their computed representation, such as the\n//! ones used for ShadowRoot.\n\nuse crate::derives::*;\nuse crate::invalidation::stylesheets::StylesheetInvalidationSet;\nuse crate::shared_lock::SharedRwLockReadGuard;\nuse crate::stylesheet_set::AuthorStylesheetSet;\nuse crate::stylesheets::StylesheetInDocument;\nuse crate::stylist::CascadeData;\nuse crate::stylist::Stylist;\nuse servo_arc::Arc;\nuse std::sync::LazyLock;\n\n/// A set of author stylesheets and their computed representation, such as the\n/// ones used for ShadowRoot.\n#[derive(MallocSizeOf)]\npub struct GenericAuthorStyles<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The sheet collection, which holds the sheet pointers, the invalidations,\n    /// and all that stuff.\n    pub stylesheets: AuthorStylesheetSet<S>,\n    /// The actual cascade data computed from the stylesheets.\n    #[ignore_malloc_size_of = \"Measured as part of the stylist\"]\n    pub data: Arc<CascadeData>,\n}\n\npub use self::GenericAuthorStyles as AuthorStyles;\n\nstatic EMPTY_CASCADE_DATA: LazyLock<Arc<CascadeData>> =\n    LazyLock::new(|| Arc::new_leaked(CascadeData::new()));\n\nimpl<S> GenericAuthorStyles<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Create an empty AuthorStyles.\n    #[inline]\n    pub fn new() -> Self {\n        Self {\n            stylesheets: AuthorStylesheetSet::new(),\n            data: EMPTY_CASCADE_DATA.clone(),\n        }\n    }\n\n    /// Flush the pending sheet changes, updating `data` as appropriate.\n    #[inline]\n    pub fn flush(\n        &mut self,\n        stylist: &mut Stylist,\n        guard: &SharedRwLockReadGuard,\n    ) -> StylesheetInvalidationSet {\n        let (flusher, mut invalidations) = self.stylesheets.flush();\n        let result = stylist.rebuild_author_data(\n            &self.data,\n            flusher.sheets,\n            guard,\n            &mut invalidations.cascade_data_difference,\n        );\n        if let Ok(Some(new_data)) = result {\n            self.data = new_data;\n        }\n        invalidations\n    }\n}\n"
  },
  {
    "path": "style/bezier.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parametric Bézier curves.\n//!\n//! This is based on `WebCore/platform/graphics/UnitBezier.h` in WebKit.\n\n#![deny(missing_docs)]\n\nuse crate::values::CSSFloat;\n\nconst NEWTON_METHOD_ITERATIONS: u8 = 8;\n\n/// A unit cubic Bézier curve, used for timing functions in CSS transitions and animations.\npub struct Bezier {\n    ax: f64,\n    bx: f64,\n    cx: f64,\n    ay: f64,\n    by: f64,\n    cy: f64,\n}\n\nimpl Bezier {\n    /// Calculate the output of a unit cubic Bézier curve from the two middle control points.\n    ///\n    /// X coordinate is time, Y coordinate is function advancement.\n    /// The nominal range for both is 0 to 1.\n    ///\n    /// The start and end points are always (0, 0) and (1, 1) so that a transition or animation\n    /// starts at 0% and ends at 100%.\n    pub fn calculate_bezier_output(\n        progress: f64,\n        epsilon: f64,\n        x1: f32,\n        y1: f32,\n        x2: f32,\n        y2: f32,\n    ) -> f64 {\n        // Check for a linear curve.\n        if x1 == y1 && x2 == y2 {\n            return progress;\n        }\n\n        // Ensure that we return 0 or 1 on both edges.\n        if progress == 0.0 {\n            return 0.0;\n        }\n        if progress == 1.0 {\n            return 1.0;\n        }\n\n        // For negative values, try to extrapolate with tangent (p1 - p0) or,\n        // if p1 is coincident with p0, with (p2 - p0).\n        if progress < 0.0 {\n            if x1 > 0.0 {\n                return progress * y1 as f64 / x1 as f64;\n            }\n            if y1 == 0.0 && x2 > 0.0 {\n                return progress * y2 as f64 / x2 as f64;\n            }\n            // If we can't calculate a sensible tangent, don't extrapolate at all.\n            return 0.0;\n        }\n\n        // For values greater than 1, try to extrapolate with tangent (p2 - p3) or,\n        // if p2 is coincident with p3, with (p1 - p3).\n        if progress > 1.0 {\n            if x2 < 1.0 {\n                return 1.0 + (progress - 1.0) * (y2 as f64 - 1.0) / (x2 as f64 - 1.0);\n            }\n            if y2 == 1.0 && x1 < 1.0 {\n                return 1.0 + (progress - 1.0) * (y1 as f64 - 1.0) / (x1 as f64 - 1.0);\n            }\n            // If we can't calculate a sensible tangent, don't extrapolate at all.\n            return 1.0;\n        }\n\n        Bezier::new(x1, y1, x2, y2).solve(progress, epsilon)\n    }\n\n    #[inline]\n    fn new(x1: CSSFloat, y1: CSSFloat, x2: CSSFloat, y2: CSSFloat) -> Bezier {\n        let cx = 3. * x1 as f64;\n        let bx = 3. * (x2 as f64 - x1 as f64) - cx;\n\n        let cy = 3. * y1 as f64;\n        let by = 3. * (y2 as f64 - y1 as f64) - cy;\n\n        Bezier {\n            ax: 1.0 - cx - bx,\n            bx: bx,\n            cx: cx,\n            ay: 1.0 - cy - by,\n            by: by,\n            cy: cy,\n        }\n    }\n\n    #[inline]\n    fn sample_curve_x(&self, t: f64) -> f64 {\n        // ax * t^3 + bx * t^2 + cx * t\n        ((self.ax * t + self.bx) * t + self.cx) * t\n    }\n\n    #[inline]\n    fn sample_curve_y(&self, t: f64) -> f64 {\n        ((self.ay * t + self.by) * t + self.cy) * t\n    }\n\n    #[inline]\n    fn sample_curve_derivative_x(&self, t: f64) -> f64 {\n        (3.0 * self.ax * t + 2.0 * self.bx) * t + self.cx\n    }\n\n    #[inline]\n    fn solve_curve_x(&self, x: f64, epsilon: f64) -> f64 {\n        // Fast path: Use Newton's method.\n        let mut t = x;\n        for _ in 0..NEWTON_METHOD_ITERATIONS {\n            let x2 = self.sample_curve_x(t);\n            if x2.approx_eq(x, epsilon) {\n                return t;\n            }\n            let dx = self.sample_curve_derivative_x(t);\n            if dx.approx_eq(0.0, 1e-6) {\n                break;\n            }\n            t -= (x2 - x) / dx;\n        }\n\n        // Slow path: Use bisection.\n        let (mut lo, mut hi, mut t) = (0.0, 1.0, x);\n\n        if t < lo {\n            return lo;\n        }\n        if t > hi {\n            return hi;\n        }\n\n        while lo < hi {\n            let x2 = self.sample_curve_x(t);\n            if x2.approx_eq(x, epsilon) {\n                return t;\n            }\n            if x > x2 {\n                lo = t\n            } else {\n                hi = t\n            }\n            t = (hi - lo) / 2.0 + lo\n        }\n\n        t\n    }\n\n    /// Solve the bezier curve for a given `x` and an `epsilon`, that should be\n    /// between zero and one.\n    #[inline]\n    fn solve(&self, x: f64, epsilon: f64) -> f64 {\n        self.sample_curve_y(self.solve_curve_x(x, epsilon))\n    }\n}\n\ntrait ApproxEq {\n    fn approx_eq(self, value: Self, epsilon: Self) -> bool;\n}\n\nimpl ApproxEq for f64 {\n    #[inline]\n    fn approx_eq(self, value: f64, epsilon: f64) -> bool {\n        (self - value).abs() < epsilon\n    }\n}\n"
  },
  {
    "path": "style/bloom.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The style bloom filter is used as an optimization when matching deep\n//! descendant selectors.\n\n#![deny(missing_docs)]\n\nuse crate::dom::{SendElement, TElement};\nuse crate::LocalName;\nuse atomic_refcell::{AtomicRefCell, AtomicRefMut};\nuse selectors::bloom::BloomFilter;\nuse smallvec::SmallVec;\n\nthread_local! {\n    /// Bloom filters are large allocations, so we store them in thread-local storage\n    /// such that they can be reused across style traversals. StyleBloom is responsible\n    /// for ensuring that the bloom filter is zeroed when it is dropped.\n    ///\n    /// We intentionally leak this from TLS because we don't have the guarantee\n    /// of TLS destructors to run in worker threads.\n    ///\n    /// Also, leaking it guarantees that we can borrow it indefinitely.\n    ///\n    /// We could change this once https://github.com/rayon-rs/rayon/issues/688\n    /// is fixed, hopefully, which point we'd need to change the filter member below to be an\n    /// arc and carry an owning reference around or so.\n    static BLOOM_KEY: &'static AtomicRefCell<BloomFilter> = Box::leak(Default::default());\n}\n\n/// A struct that allows us to fast-reject deep descendant selectors avoiding\n/// selector-matching.\n///\n/// This is implemented using a counting bloom filter, and it's a standard\n/// optimization. See Gecko's `AncestorFilter`, and Blink's and WebKit's\n/// `SelectorFilter`.\n///\n/// The constraints for Servo's style system are a bit different compared to\n/// traditional style systems given Servo does a parallel breadth-first\n/// traversal instead of a sequential depth-first traversal.\n///\n/// This implies that we need to track a bit more state than other browsers to\n/// ensure we're doing the correct thing during the traversal, and being able to\n/// apply this optimization effectively.\n///\n/// Concretely, we have a bloom filter instance per worker thread, and we track\n/// the current DOM depth in order to find a common ancestor when it doesn't\n/// match the previous element we've styled.\n///\n/// This is usually a pretty fast operation (we use to be one level deeper than\n/// the previous one), but in the case of work-stealing, we may needed to push\n/// and pop multiple elements.\n///\n/// See the `insert_parents_recovering`, where most of the magic happens.\n///\n/// Regarding thread-safety, this struct is safe because:\n///\n///  * We clear this after a restyle.\n///  * The DOM shape and attributes (and every other thing we access here) are\n///    immutable during a restyle.\n///\npub struct StyleBloom<E: TElement> {\n    /// A handle to the bloom filter from the thread upon which this StyleBloom\n    /// was created. We use AtomicRefCell so that this is all |Send|, which allows\n    /// StyleBloom to live in ThreadLocalStyleContext, which is dropped from the\n    /// parent thread.\n    filter: AtomicRefMut<'static, BloomFilter>,\n\n    /// The stack of elements that this bloom filter contains, along with the\n    /// number of hashes pushed for each element.\n    elements: SmallVec<[PushedElement<E>; 16]>,\n\n    /// Stack of hashes that have been pushed onto this filter.\n    pushed_hashes: SmallVec<[u32; 64]>,\n}\n\n/// The very rough benchmarks in the selectors crate show clear()\n/// costing about 25 times more than remove_hash(). We use this to implement\n/// clear() more efficiently when only a small number of hashes have been\n/// pushed.\n///\n/// One subtly to note is that remove_hash() will not touch the value\n/// if the filter overflowed. However, overflow can only occur if we\n/// get 255 collisions on the same hash value, and 25 < 255.\nconst MEMSET_CLEAR_THRESHOLD: usize = 25;\n\nstruct PushedElement<E: TElement> {\n    /// The element that was pushed.\n    element: SendElement<E>,\n\n    /// The number of hashes pushed for the element.\n    num_hashes: usize,\n}\n\nimpl<E: TElement> PushedElement<E> {\n    fn new(el: E, num_hashes: usize) -> Self {\n        PushedElement {\n            element: unsafe { SendElement::new(el) },\n            num_hashes,\n        }\n    }\n}\n\n/// Returns whether the attribute name is excluded from the bloom filter.\n///\n/// We do this for attributes that are very common but not commonly used in\n/// selectors.\n#[inline]\npub fn is_attr_name_excluded_from_filter(name: &LocalName) -> bool {\n    *name == local_name!(\"class\") || *name == local_name!(\"id\") || *name == local_name!(\"style\")\n}\n\n/// Gather all relevant hash for fast-reject filters from an element.\npub fn each_relevant_element_hash<E, F>(element: E, mut f: F)\nwhere\n    E: TElement,\n    F: FnMut(u32),\n{\n    f(element.local_name().get_hash());\n    f(element.namespace().get_hash());\n\n    if let Some(id) = element.id() {\n        f(id.get_hash());\n    }\n\n    element.each_class(|class| f(class.get_hash()));\n\n    element.each_attr_name(|name| {\n        if !is_attr_name_excluded_from_filter(name) {\n            f(name.get_hash())\n        }\n    });\n}\n\nimpl<E: TElement> Drop for StyleBloom<E> {\n    fn drop(&mut self) {\n        // Leave the reusable bloom filter in a zeroed state.\n        self.clear();\n    }\n}\n\nimpl<E: TElement> StyleBloom<E> {\n    /// Create an empty `StyleBloom`. Because StyleBloom acquires the thread-\n    /// local filter buffer, creating multiple live StyleBloom instances at\n    /// the same time on the same thread will panic.\n\n    // Forced out of line to limit stack frame sizes after extra inlining from\n    // https://github.com/rust-lang/rust/pull/43931\n    //\n    // See https://github.com/servo/servo/pull/18420#issuecomment-328769322\n    #[inline(never)]\n    pub fn new() -> Self {\n        let filter = BLOOM_KEY.with(|b| b.borrow_mut());\n        debug_assert!(\n            filter.is_zeroed(),\n            \"Forgot to zero the bloom filter last time\"\n        );\n        StyleBloom {\n            filter,\n            elements: Default::default(),\n            pushed_hashes: Default::default(),\n        }\n    }\n\n    /// Return the bloom filter used properly by the `selectors` crate.\n    pub fn filter(&self) -> &BloomFilter {\n        &*self.filter\n    }\n\n    /// Push an element to the bloom filter, knowing that it's a child of the\n    /// last element parent.\n    pub fn push(&mut self, element: E) {\n        if cfg!(debug_assertions) {\n            if self.elements.is_empty() {\n                assert!(element.traversal_parent().is_none());\n            }\n        }\n        self.push_internal(element);\n    }\n\n    /// Same as `push`, but without asserting, in order to use it from\n    /// `rebuild`.\n    fn push_internal(&mut self, element: E) {\n        let mut count = 0;\n        each_relevant_element_hash(element, |hash| {\n            count += 1;\n            self.filter.insert_hash(hash);\n            self.pushed_hashes.push(hash);\n        });\n        self.elements.push(PushedElement::new(element, count));\n    }\n\n    /// Pop the last element in the bloom filter and return it.\n    #[inline]\n    fn pop(&mut self) -> Option<E> {\n        let PushedElement {\n            element,\n            num_hashes,\n        } = self.elements.pop()?;\n        let popped_element = *element;\n\n        // Verify that the pushed hashes match the ones we'd get from the element.\n        let mut expected_hashes = vec![];\n        if cfg!(debug_assertions) {\n            each_relevant_element_hash(popped_element, |hash| expected_hashes.push(hash));\n        }\n\n        for _ in 0..num_hashes {\n            let hash = self.pushed_hashes.pop().unwrap();\n            debug_assert_eq!(expected_hashes.pop().unwrap(), hash);\n            self.filter.remove_hash(hash);\n        }\n\n        Some(popped_element)\n    }\n\n    /// Returns the DOM depth of elements that can be correctly\n    /// matched against the bloom filter (that is, the number of\n    /// elements in our list).\n    pub fn matching_depth(&self) -> usize {\n        self.elements.len()\n    }\n\n    /// Clears the bloom filter.\n    pub fn clear(&mut self) {\n        self.elements.clear();\n\n        if self.pushed_hashes.len() > MEMSET_CLEAR_THRESHOLD {\n            self.filter.clear();\n            self.pushed_hashes.clear();\n        } else {\n            for hash in self.pushed_hashes.drain(..) {\n                self.filter.remove_hash(hash);\n            }\n            debug_assert!(self.filter.is_zeroed());\n        }\n    }\n\n    /// Rebuilds the bloom filter up to the parent of the given element.\n    pub fn rebuild(&mut self, mut element: E) {\n        self.clear();\n\n        let mut parents_to_insert = SmallVec::<[E; 16]>::new();\n        while let Some(parent) = element.traversal_parent() {\n            parents_to_insert.push(parent);\n            element = parent;\n        }\n\n        for parent in parents_to_insert.drain(..).rev() {\n            self.push(parent);\n        }\n    }\n\n    /// In debug builds, asserts that all the parents of `element` are in the\n    /// bloom filter.\n    ///\n    /// Goes away in release builds.\n    pub fn assert_complete(&self, mut element: E) {\n        if cfg!(debug_assertions) {\n            let mut checked = 0;\n            while let Some(parent) = element.traversal_parent() {\n                assert_eq!(\n                    parent,\n                    *(self.elements[self.elements.len() - 1 - checked].element)\n                );\n                element = parent;\n                checked += 1;\n            }\n            assert_eq!(checked, self.elements.len());\n        }\n    }\n\n    /// Get the element that represents the chain of things inserted\n    /// into the filter right now.  That chain is the given element\n    /// (if any) and its ancestors.\n    #[inline]\n    pub fn current_parent(&self) -> Option<E> {\n        self.elements.last().map(|ref el| *el.element)\n    }\n\n    /// Insert the parents of an element in the bloom filter, trying to recover\n    /// the filter if the last element inserted doesn't match.\n    ///\n    /// Gets the element depth in the dom, to make it efficient, or if not\n    /// provided always rebuilds the filter from scratch.\n    ///\n    /// Returns the new bloom filter depth, that the traversal code is\n    /// responsible to keep around if it wants to get an effective filter.\n    pub fn insert_parents_recovering(&mut self, element: E, element_depth: usize) {\n        // Easy case, we're in a different restyle, or we're empty.\n        if self.elements.is_empty() {\n            self.rebuild(element);\n            return;\n        }\n\n        let traversal_parent = match element.traversal_parent() {\n            Some(parent) => parent,\n            None => {\n                // Yay, another easy case.\n                self.clear();\n                return;\n            },\n        };\n\n        if self.current_parent() == Some(traversal_parent) {\n            // Ta da, cache hit, we're all done.\n            return;\n        }\n\n        if element_depth == 0 {\n            self.clear();\n            return;\n        }\n\n        // We should've early exited above.\n        debug_assert!(\n            element_depth != 0,\n            \"We should have already cleared the bloom filter\"\n        );\n        debug_assert!(!self.elements.is_empty(), \"How! We should've just rebuilt!\");\n\n        // Now the fun begins: We have the depth of the dom and the depth of the\n        // last element inserted in the filter, let's try to find a common\n        // parent.\n        //\n        // The current depth, that is, the depth of the last element inserted in\n        // the bloom filter, is the number of elements _minus one_, that is: if\n        // there's one element, it must be the root -> depth zero.\n        let mut current_depth = self.elements.len() - 1;\n\n        // If the filter represents an element too deep in the dom, we need to\n        // pop ancestors.\n        while current_depth > element_depth - 1 {\n            self.pop().expect(\"Emilio is bad at math\");\n            current_depth -= 1;\n        }\n\n        // Now let's try to find a common parent in the bloom filter chain,\n        // starting with traversal_parent.\n        let mut common_parent = traversal_parent;\n        let mut common_parent_depth = element_depth - 1;\n\n        // Let's collect the parents we are going to need to insert once we've\n        // found the common one.\n        let mut parents_to_insert = SmallVec::<[E; 16]>::new();\n\n        // If the bloom filter still doesn't have enough elements, the common\n        // parent is up in the dom.\n        while common_parent_depth > current_depth {\n            // TODO(emilio): Seems like we could insert parents here, then\n            // reverse the slice.\n            parents_to_insert.push(common_parent);\n            common_parent = common_parent.traversal_parent().expect(\"We were lied to\");\n            common_parent_depth -= 1;\n        }\n\n        // Now the two depths are the same.\n        debug_assert_eq!(common_parent_depth, current_depth);\n\n        // Happy case: The parents match, we only need to push the ancestors\n        // we've collected and we'll never enter in this loop.\n        //\n        // Not-so-happy case: Parent's don't match, so we need to keep going up\n        // until we find a common ancestor.\n        //\n        // Gecko currently models native anonymous content that conceptually\n        // hangs off the document (such as scrollbars) as a separate subtree\n        // from the document root.\n        //\n        // Thus it's possible with Gecko that we do not find any common\n        // ancestor.\n        while *(self.elements.last().unwrap().element) != common_parent {\n            parents_to_insert.push(common_parent);\n            self.pop().unwrap();\n            common_parent = match common_parent.traversal_parent() {\n                Some(parent) => parent,\n                None => {\n                    debug_assert!(self.elements.is_empty());\n                    if cfg!(feature = \"gecko\") {\n                        break;\n                    } else {\n                        panic!(\"should have found a common ancestor\");\n                    }\n                },\n            }\n        }\n\n        // Now the parents match, so insert the stack of elements we have been\n        // collecting so far.\n        for parent in parents_to_insert.drain(..).rev() {\n            self.push(parent);\n        }\n\n        debug_assert_eq!(self.elements.len(), element_depth);\n\n        // We're done! Easy.\n    }\n}\n"
  },
  {
    "path": "style/build.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse std::env;\nuse std::path::Path;\nuse std::process::{exit, Command};\nuse std::sync::LazyLock;\nuse walkdir::WalkDir;\n\n#[cfg(feature = \"gecko\")]\nmod build_gecko;\n\n#[cfg(not(feature = \"gecko\"))]\nmod build_gecko {\n    pub fn generate() {}\n}\n\npub static PYTHON: LazyLock<String> = LazyLock::new(|| {\n    env::var(\"PYTHON3\").ok().unwrap_or_else(|| {\n        let candidates = if cfg!(windows) {\n            [\"python.exe\"]\n        } else {\n            [\"python3\"]\n        };\n        for &name in &candidates {\n            if Command::new(name)\n                .arg(\"--version\")\n                .output()\n                .ok()\n                .map_or(false, |out| out.status.success())\n            {\n                return name.to_owned();\n            }\n        }\n        panic!(\n            \"Can't find python (tried {})! Try fixing PATH or setting the PYTHON3 env var\",\n            candidates.join(\", \")\n        )\n    })\n});\n\nfn generate_properties(engine: &str) {\n    for entry in WalkDir::new(\"properties\") {\n        let entry = entry.unwrap();\n        match entry.path().extension().and_then(|e| e.to_str()) {\n            Some(\"mako\") | Some(\"rs\") | Some(\"py\") | Some(\"zip\") | Some(\"toml\") => {\n                println!(\"cargo:rerun-if-changed={}\", entry.path().display());\n            },\n            _ => {},\n        }\n    }\n\n    let script = Path::new(&env::var_os(\"CARGO_MANIFEST_DIR\").unwrap())\n        .join(\"properties\")\n        .join(\"build.py\");\n\n    let status = Command::new(&*PYTHON)\n        // `cargo publish` isn't happy with the `__pycache__` files that are created\n        // when we run the property generator.\n        //\n        // TODO(mrobinson): Is this happening because of how we run this script? It\n        // would be better to ensure are just placed in the output directory.\n        .env(\"PYTHONDONTWRITEBYTECODE\", \"1\")\n        .arg(&script)\n        .arg(engine)\n        .arg(\"style-crate\")\n        .status()\n        .unwrap();\n    if !status.success() {\n        exit(1)\n    }\n}\n\nfn main() {\n    let gecko = cfg!(feature = \"gecko\");\n    let servo = cfg!(feature = \"servo\");\n    let engine = match (gecko, servo) {\n        (true, false) => \"gecko\",\n        (false, true) => \"servo\",\n        _ => panic!(\n            \"\\n\\n\\\n             The style crate requires enabling one of its 'servo' or 'gecko' feature flags. \\\n             \\n\\n\"\n        ),\n    };\n    println!(\"cargo:rerun-if-changed=build.rs\");\n    println!(\"cargo:out_dir={}\", env::var(\"OUT_DIR\").unwrap());\n    generate_properties(engine);\n    build_gecko::generate();\n}\n"
  },
  {
    "path": "style/build_gecko.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse super::PYTHON;\nuse bindgen::{Builder, CodegenConfig};\nuse regex::Regex;\nuse std::cmp;\nuse std::collections::HashSet;\nuse std::env;\nuse std::fs::{self, File};\nuse std::io::{Read, Write};\nuse std::path::{Path, PathBuf};\nuse std::process::{exit, Command};\nuse std::slice;\nuse std::sync::LazyLock;\nuse std::sync::Mutex;\nuse std::time::SystemTime;\nuse toml;\nuse toml::value::Table;\n\nstatic OUTDIR_PATH: LazyLock<PathBuf> =\n    LazyLock::new(|| PathBuf::from(env::var_os(\"OUT_DIR\").unwrap()).join(\"gecko\"));\n\nconst STRUCTS_FILE: &'static str = \"structs.rs\";\n\nfn read_config(path: &PathBuf) -> Table {\n    println!(\"cargo:rerun-if-changed={}\", path.to_str().unwrap());\n    update_last_modified(&path);\n\n    let mut contents = String::new();\n    File::open(path)\n        .expect(\"Failed to open config file\")\n        .read_to_string(&mut contents)\n        .expect(\"Failed to read config file\");\n    match toml::from_str::<Table>(&contents) {\n        Ok(result) => result,\n        Err(e) => panic!(\"Failed to parse config file: {}\", e),\n    }\n}\n\nstatic CONFIG: LazyLock<Table> = LazyLock::new(|| {\n    // Load Gecko's binding generator config from the source tree.\n    let path = mozbuild::TOPSRCDIR.join(\"layout/style/ServoBindings.toml\");\n    read_config(&path)\n});\nstatic BINDGEN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {\n    mozbuild::config::BINDGEN_SYSTEM_FLAGS\n        .iter()\n        .chain(&mozbuild::config::NSPR_CFLAGS)\n        .chain(&mozbuild::config::MOZ_PIXMAN_CFLAGS)\n        .chain(&mozbuild::config::MOZ_ICU_CFLAGS)\n        .map(|s| s.to_string())\n        .collect()\n});\nstatic INCLUDE_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r#\"#include\\s*\"(.+?)\"\"#).unwrap());\nstatic DISTDIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| mozbuild::TOPOBJDIR.join(\"dist\"));\nstatic SEARCH_PATHS: LazyLock<Vec<PathBuf>> = LazyLock::new(|| {\n    vec![\n        DISTDIR_PATH.join(\"include\"),\n        DISTDIR_PATH.join(\"include/nspr\"),\n    ]\n});\nstatic ADDED_PATHS: LazyLock<Mutex<HashSet<PathBuf>>> =\n    LazyLock::new(|| Mutex::new(HashSet::new()));\nstatic LAST_MODIFIED: LazyLock<Mutex<SystemTime>> = LazyLock::new(|| {\n    Mutex::new(\n        get_modified_time(&env::current_exe().unwrap())\n            .expect(\"Failed to get modified time of executable\"),\n    )\n});\n\nfn get_modified_time(file: &Path) -> Option<SystemTime> {\n    file.metadata().and_then(|m| m.modified()).ok()\n}\n\nfn update_last_modified(file: &Path) {\n    let modified = get_modified_time(file).expect(\"Couldn't get file modification time\");\n    let mut last_modified = LAST_MODIFIED.lock().unwrap();\n    *last_modified = cmp::max(modified, *last_modified);\n}\n\nfn search_include(name: &str) -> Option<PathBuf> {\n    for path in SEARCH_PATHS.iter() {\n        let file = path.join(name);\n        if file.is_file() {\n            update_last_modified(&file);\n            return Some(file);\n        }\n    }\n    None\n}\n\nfn add_headers_recursively(path: PathBuf, added_paths: &mut HashSet<PathBuf>) {\n    if added_paths.contains(&path) {\n        return;\n    }\n    let mut file = File::open(&path).unwrap();\n    let mut content = String::new();\n    file.read_to_string(&mut content).unwrap();\n    added_paths.insert(path);\n    // Find all includes and add them recursively\n    for cap in INCLUDE_RE.captures_iter(&content) {\n        if let Some(path) = search_include(cap.get(1).unwrap().as_str()) {\n            add_headers_recursively(path, added_paths);\n        }\n    }\n}\n\nfn add_include(name: &str) -> String {\n    let mut added_paths = ADDED_PATHS.lock().unwrap();\n    let file = match search_include(name) {\n        Some(file) => file,\n        None => panic!(\"Include not found: {}\", name),\n    };\n    let result = String::from(file.to_str().unwrap());\n    add_headers_recursively(file, &mut *added_paths);\n    result\n}\n\ntrait BuilderExt {\n    fn get_initial_builder() -> Builder;\n    fn include<T: Into<String>>(self, file: T) -> Builder;\n}\n\nimpl BuilderExt for Builder {\n    fn get_initial_builder() -> Builder {\n        // Disable rust unions, because we replace some types inside of\n        // them.\n        let mut builder = Builder::default()\n            .size_t_is_usize(true)\n            .disable_untagged_union();\n\n        let rustfmt_path = env::var_os(\"RUSTFMT\")\n            .filter(|p| !p.is_empty())\n            .map(PathBuf::from);\n        if let Some(path) = rustfmt_path {\n            builder = builder.with_rustfmt(path);\n        }\n\n        for dir in SEARCH_PATHS.iter() {\n            builder = builder.clang_arg(\"-I\").clang_arg(dir.to_str().unwrap());\n        }\n\n        builder = builder.include(add_include(\"mozilla-config.h\"));\n\n        if env::var(\"CARGO_FEATURE_GECKO_DEBUG\").is_ok() {\n            builder = builder.clang_arg(\"-DDEBUG=1\").clang_arg(\"-DJS_DEBUG=1\");\n        }\n\n        for item in &*BINDGEN_FLAGS {\n            builder = builder.clang_arg(item);\n        }\n\n        builder\n    }\n    fn include<T: Into<String>>(self, file: T) -> Builder {\n        self.clang_arg(\"-include\").clang_arg(file)\n    }\n}\n\nstruct Fixup {\n    pat: String,\n    rep: String,\n}\n\nfn write_binding_file(builder: Builder, file: &str, fixups: &[Fixup]) {\n    let out_file = OUTDIR_PATH.join(file);\n    if let Some(modified) = get_modified_time(&out_file) {\n        // Don't generate the file if nothing it depends on was modified.\n        let last_modified = LAST_MODIFIED.lock().unwrap();\n        if *last_modified <= modified {\n            return;\n        }\n    }\n    let command_line_opts = builder.command_line_flags();\n    let result = builder.generate();\n    let mut result = match result {\n        Ok(bindings) => bindings.to_string(),\n        Err(_) => {\n            panic!(\n                \"Failed to generate bindings, flags: {:?}\",\n                command_line_opts\n            );\n        },\n    };\n\n    for fixup in fixups.iter() {\n        result = Regex::new(&fixup.pat)\n            .unwrap()\n            .replace_all(&result, &*fixup.rep)\n            .into_owned()\n            .into();\n    }\n    let bytes = result.into_bytes();\n    File::create(&out_file)\n        .unwrap()\n        .write_all(&bytes)\n        .expect(\"Unable to write output\");\n}\n\nstruct BuilderWithConfig<'a> {\n    builder: Builder,\n    config: &'a Table,\n    used_keys: HashSet<&'static str>,\n}\nimpl<'a> BuilderWithConfig<'a> {\n    fn new(builder: Builder, config: &'a Table) -> Self {\n        BuilderWithConfig {\n            builder,\n            config,\n            used_keys: HashSet::new(),\n        }\n    }\n\n    fn handle_list<F>(self, key: &'static str, func: F) -> BuilderWithConfig<'a>\n    where\n        F: FnOnce(Builder, slice::Iter<'a, toml::Value>) -> Builder,\n    {\n        let mut builder = self.builder;\n        let config = self.config;\n        let mut used_keys = self.used_keys;\n        if let Some(list) = config.get(key) {\n            used_keys.insert(key);\n            builder = func(builder, list.as_array().unwrap().as_slice().iter());\n        }\n        BuilderWithConfig {\n            builder,\n            config,\n            used_keys,\n        }\n    }\n    fn handle_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>\n    where\n        F: FnMut(Builder, &'a toml::Value) -> Builder,\n    {\n        self.handle_list(key, |b, iter| iter.fold(b, |b, item| func(b, item)))\n    }\n    fn handle_str_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>\n    where\n        F: FnMut(Builder, &'a str) -> Builder,\n    {\n        self.handle_items(key, |b, item| func(b, item.as_str().unwrap()))\n    }\n    fn handle_table_items<F>(self, key: &'static str, mut func: F) -> BuilderWithConfig<'a>\n    where\n        F: FnMut(Builder, &'a Table) -> Builder,\n    {\n        self.handle_items(key, |b, item| func(b, item.as_table().unwrap()))\n    }\n    fn handle_common(self, fixups: &mut Vec<Fixup>) -> BuilderWithConfig<'a> {\n        self.handle_str_items(\"headers\", |b, item| b.header(add_include(item)))\n            .handle_str_items(\"raw-lines\", |b, item| b.raw_line(item))\n            .handle_str_items(\"hide-types\", |b, item| b.blocklist_type(item))\n            .handle_table_items(\"fixups\", |builder, item| {\n                fixups.push(Fixup {\n                    pat: item[\"pat\"].as_str().unwrap().into(),\n                    rep: item[\"rep\"].as_str().unwrap().into(),\n                });\n                builder\n            })\n    }\n\n    fn get_builder(self) -> Builder {\n        for key in self.config.keys() {\n            if !self.used_keys.contains(key.as_str()) {\n                panic!(\"Unknown key: {}\", key);\n            }\n        }\n        self.builder\n    }\n}\n\nfn generate_structs() {\n    let builder = Builder::get_initial_builder()\n        .enable_cxx_namespaces()\n        .with_codegen_config(CodegenConfig::TYPES | CodegenConfig::VARS | CodegenConfig::FUNCTIONS);\n    let mut fixups = vec![];\n    let builder = BuilderWithConfig::new(builder, CONFIG[\"structs\"].as_table().unwrap())\n        .handle_common(&mut fixups)\n        .handle_str_items(\"allowlist-functions\", |b, item| b.allowlist_function(item))\n        .handle_str_items(\"bitfield-enums\", |b, item| b.bitfield_enum(item))\n        .handle_str_items(\"rusty-enums\", |b, item| b.rustified_enum(item))\n        .handle_str_items(\"allowlist-vars\", |b, item| b.allowlist_var(item))\n        .handle_str_items(\"allowlist-types\", |b, item| b.allowlist_type(item))\n        .handle_str_items(\"opaque-types\", |b, item| b.opaque_type(item))\n        .handle_table_items(\"cbindgen-types\", |b, item| {\n            let gecko = item[\"gecko\"].as_str().unwrap();\n            let servo = item[\"servo\"].as_str().unwrap();\n            b.blocklist_type(format!(\"mozilla::{}\", gecko))\n                .module_raw_line(\"root::mozilla\", format!(\"pub use {} as {};\", servo, gecko))\n        })\n        .handle_table_items(\"mapped-generic-types\", |builder, item| {\n            let generic = item[\"generic\"].as_bool().unwrap();\n            let gecko = item[\"gecko\"].as_str().unwrap();\n            let servo = item[\"servo\"].as_str().unwrap();\n            let gecko_name = gecko.rsplit(\"::\").next().unwrap();\n            let gecko = gecko\n                .split(\"::\")\n                .map(|s| format!(\"\\\\s*{}\\\\s*\", s))\n                .collect::<Vec<_>>()\n                .join(\"::\");\n\n            fixups.push(Fixup {\n                pat: format!(\"\\\\broot\\\\s*::\\\\s*{}\\\\b\", gecko),\n                rep: format!(\"crate::gecko_bindings::structs::{}\", gecko_name),\n            });\n            builder.blocklist_type(gecko).raw_line(format!(\n                \"pub type {0}{2} = {1}{2};\",\n                gecko_name,\n                servo,\n                if generic { \"<T>\" } else { \"\" }\n            ))\n        })\n        .get_builder();\n    write_binding_file(builder, STRUCTS_FILE, &fixups);\n}\n\nfn setup_logging() -> bool {\n    struct BuildLogger {\n        file: Option<Mutex<fs::File>>,\n        filter: String,\n    }\n\n    impl log::Log for BuildLogger {\n        fn enabled(&self, meta: &log::Metadata) -> bool {\n            self.file.is_some() && meta.target().contains(&self.filter)\n        }\n\n        fn log(&self, record: &log::Record) {\n            if !self.enabled(record.metadata()) {\n                return;\n            }\n\n            let mut file = self.file.as_ref().unwrap().lock().unwrap();\n            let _ = writeln!(\n                file,\n                \"{} - {} - {} @ {}:{}\",\n                record.level(),\n                record.target(),\n                record.args(),\n                record.file().unwrap_or(\"<unknown>\"),\n                record.line().unwrap_or(0)\n            );\n        }\n\n        fn flush(&self) {\n            if let Some(ref file) = self.file {\n                file.lock().unwrap().flush().unwrap();\n            }\n        }\n    }\n\n    if let Some(path) = env::var_os(\"STYLO_BUILD_LOG\") {\n        log::set_max_level(log::LevelFilter::Debug);\n        log::set_boxed_logger(Box::new(BuildLogger {\n            file: fs::File::create(path).ok().map(Mutex::new),\n            filter: env::var(\"STYLO_BUILD_FILTER\")\n                .ok()\n                .unwrap_or_else(|| \"bindgen\".to_owned()),\n        }))\n        .expect(\"Failed to set logger.\");\n\n        true\n    } else {\n        false\n    }\n}\n\nfn generate_atoms() {\n    let script = PathBuf::from(env::var_os(\"CARGO_MANIFEST_DIR\").unwrap())\n        .join(\"gecko\")\n        .join(\"regen_atoms.py\");\n    println!(\"cargo:rerun-if-changed={}\", script.display());\n    let status = Command::new(&*PYTHON)\n        .arg(&script)\n        .arg(DISTDIR_PATH.as_os_str())\n        .arg(OUTDIR_PATH.as_os_str())\n        .status()\n        .unwrap();\n    if !status.success() {\n        exit(1);\n    }\n}\n\npub fn generate() {\n    println!(\"cargo:rerun-if-changed=build_gecko.rs\");\n    fs::create_dir_all(&*OUTDIR_PATH).unwrap();\n    setup_logging();\n    generate_structs();\n    generate_atoms();\n\n    for path in ADDED_PATHS.lock().unwrap().iter() {\n        println!(\"cargo:rerun-if-changed={}\", path.to_str().unwrap());\n    }\n}\n"
  },
  {
    "path": "style/color/color_function.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Output of parsing a color function, e.g. rgb(..), hsl(..), color(..)\n\nuse std::fmt::Write;\n\nuse super::{\n    component::ColorComponent,\n    convert::normalize_hue,\n    parsing::{NumberOrAngleComponent, NumberOrPercentageComponent},\n    AbsoluteColor, ColorFlags, ColorSpace,\n};\nuse crate::derives::*;\nuse crate::values::{\n    computed::color::Color as ComputedColor, generics::Optional, normalize,\n    specified::color::Color as SpecifiedColor,\n};\nuse cssparser::color::{clamp_floor_256_f32, OPAQUE};\n\n/// Represents a specified color function.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]\n#[repr(u8)]\npub enum ColorFunction<OriginColor> {\n    /// <https://drafts.csswg.org/css-color-4/#rgb-functions>\n    Rgb(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // red\n        ColorComponent<NumberOrPercentageComponent>, // green\n        ColorComponent<NumberOrPercentageComponent>, // blue\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#the-hsl-notation>\n    Hsl(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrAngleComponent>,      // hue\n        ColorComponent<NumberOrPercentageComponent>, // saturation\n        ColorComponent<NumberOrPercentageComponent>, // lightness\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#the-hwb-notation>\n    Hwb(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrAngleComponent>,      // hue\n        ColorComponent<NumberOrPercentageComponent>, // whiteness\n        ColorComponent<NumberOrPercentageComponent>, // blackness\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#specifying-lab-lch>\n    Lab(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // lightness\n        ColorComponent<NumberOrPercentageComponent>, // a\n        ColorComponent<NumberOrPercentageComponent>, // b\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#specifying-lab-lch>\n    Lch(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // lightness\n        ColorComponent<NumberOrPercentageComponent>, // chroma\n        ColorComponent<NumberOrAngleComponent>,      // hue\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch>\n    Oklab(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // lightness\n        ColorComponent<NumberOrPercentageComponent>, // a\n        ColorComponent<NumberOrPercentageComponent>, // b\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch>\n    Oklch(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // lightness\n        ColorComponent<NumberOrPercentageComponent>, // chroma\n        ColorComponent<NumberOrAngleComponent>,      // hue\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n    ),\n    /// <https://drafts.csswg.org/css-color-4/#color-function>\n    Color(\n        Optional<OriginColor>,                       // origin\n        ColorComponent<NumberOrPercentageComponent>, // red / x\n        ColorComponent<NumberOrPercentageComponent>, // green / y\n        ColorComponent<NumberOrPercentageComponent>, // blue / z\n        ColorComponent<NumberOrPercentageComponent>, // alpha\n        ColorSpace,\n    ),\n}\n\nimpl ColorFunction<AbsoluteColor> {\n    /// Try to resolve into a valid absolute color.\n    pub fn resolve_to_absolute(&self) -> Result<AbsoluteColor, ()> {\n        macro_rules! alpha {\n            ($alpha:expr, $origin_color:expr) => {{\n                $alpha\n                    .resolve($origin_color)?\n                    .map(|value| normalize(value.to_number(1.0)).clamp(0.0, OPAQUE))\n            }};\n        }\n\n        Ok(match self {\n            ColorFunction::Rgb(origin_color, r, g, b, alpha) => {\n                // Use `color(srgb ...)` to serialize `rgb(...)` if an origin color is available;\n                // this is the only reason for now.\n                let use_color_syntax = origin_color.is_some();\n\n                if use_color_syntax {\n                    let origin_color = origin_color.as_ref().map(|origin| {\n                        let origin = origin.to_color_space(ColorSpace::Srgb);\n                        // Because rgb(..) syntax have components in range [0..255), we have to\n                        // map them.\n                        // NOTE: The IS_LEGACY_SRGB flag is not added back to the color, because\n                        //       we're going to return the modern color(srgb ..) syntax.\n                        AbsoluteColor::new(\n                            ColorSpace::Srgb,\n                            origin.c0().map(|v| v * 255.0),\n                            origin.c1().map(|v| v * 255.0),\n                            origin.c2().map(|v| v * 255.0),\n                            origin.alpha(),\n                        )\n                    });\n\n                    // We have to map all the components back to [0..1) range after all the\n                    // calculations.\n                    AbsoluteColor::new(\n                        ColorSpace::Srgb,\n                        r.resolve(origin_color.as_ref())?\n                            .map(|c| c.to_number(255.0) / 255.0),\n                        g.resolve(origin_color.as_ref())?\n                            .map(|c| c.to_number(255.0) / 255.0),\n                        b.resolve(origin_color.as_ref())?\n                            .map(|c| c.to_number(255.0) / 255.0),\n                        alpha!(alpha, origin_color.as_ref()),\n                    )\n                } else {\n                    #[inline]\n                    fn resolve(\n                        component: &ColorComponent<NumberOrPercentageComponent>,\n                        origin_color: Option<&AbsoluteColor>,\n                    ) -> Result<u8, ()> {\n                        Ok(clamp_floor_256_f32(\n                            component\n                                .resolve(origin_color)?\n                                .map_or(0.0, |value| value.to_number(u8::MAX as f32)),\n                        ))\n                    }\n\n                    let origin_color = origin_color.as_ref().map(|o| o.into_srgb_legacy());\n\n                    AbsoluteColor::srgb_legacy(\n                        resolve(r, origin_color.as_ref())?,\n                        resolve(g, origin_color.as_ref())?,\n                        resolve(b, origin_color.as_ref())?,\n                        alpha!(alpha, origin_color.as_ref()).unwrap_or(0.0),\n                    )\n                }\n            },\n            ColorFunction::Hsl(origin_color, h, s, l, alpha) => {\n                // Percent reference range for S and L: 0% = 0.0, 100% = 100.0\n                const LIGHTNESS_RANGE: f32 = 100.0;\n                const SATURATION_RANGE: f32 = 100.0;\n\n                // If the origin color:\n                // - was *NOT* specified, then we stick with the old way of serializing the\n                //   value to rgb(..).\n                // - was specified, we don't use the rgb(..) syntax, because we should allow the\n                //   color to be out of gamut and not clamp.\n                let use_rgb_sytax = origin_color.is_none();\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Hsl));\n\n                let mut result = AbsoluteColor::new(\n                    ColorSpace::Hsl,\n                    h.resolve(origin_color.as_ref())?\n                        .map(|angle| normalize_hue(angle.degrees())),\n                    s.resolve(origin_color.as_ref())?.map(|s| {\n                        if use_rgb_sytax {\n                            s.to_number(SATURATION_RANGE).clamp(0.0, SATURATION_RANGE)\n                        } else {\n                            s.to_number(SATURATION_RANGE)\n                        }\n                    }),\n                    l.resolve(origin_color.as_ref())?.map(|l| {\n                        if use_rgb_sytax {\n                            l.to_number(LIGHTNESS_RANGE).clamp(0.0, LIGHTNESS_RANGE)\n                        } else {\n                            l.to_number(LIGHTNESS_RANGE)\n                        }\n                    }),\n                    alpha!(alpha, origin_color.as_ref()),\n                );\n\n                if use_rgb_sytax {\n                    result.flags.insert(ColorFlags::IS_LEGACY_SRGB);\n                }\n\n                result\n            },\n            ColorFunction::Hwb(origin_color, h, w, b, alpha) => {\n                // If the origin color:\n                // - was *NOT* specified, then we stick with the old way of serializing the\n                //   value to rgb(..).\n                // - was specified, we don't use the rgb(..) syntax, because we should allow the\n                //   color to be out of gamut and not clamp.\n                let use_rgb_sytax = origin_color.is_none();\n\n                // Percent reference range for W and B: 0% = 0.0, 100% = 100.0\n                const WHITENESS_RANGE: f32 = 100.0;\n                const BLACKNESS_RANGE: f32 = 100.0;\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Hwb));\n\n                let mut result = AbsoluteColor::new(\n                    ColorSpace::Hwb,\n                    h.resolve(origin_color.as_ref())?\n                        .map(|angle| normalize_hue(angle.degrees())),\n                    w.resolve(origin_color.as_ref())?.map(|w| {\n                        if use_rgb_sytax {\n                            w.to_number(WHITENESS_RANGE).clamp(0.0, WHITENESS_RANGE)\n                        } else {\n                            w.to_number(WHITENESS_RANGE)\n                        }\n                    }),\n                    b.resolve(origin_color.as_ref())?.map(|b| {\n                        if use_rgb_sytax {\n                            b.to_number(BLACKNESS_RANGE).clamp(0.0, BLACKNESS_RANGE)\n                        } else {\n                            b.to_number(BLACKNESS_RANGE)\n                        }\n                    }),\n                    alpha!(alpha, origin_color.as_ref()),\n                );\n\n                if use_rgb_sytax {\n                    result.flags.insert(ColorFlags::IS_LEGACY_SRGB);\n                }\n\n                result\n            },\n            ColorFunction::Lab(origin_color, l, a, b, alpha) => {\n                // for L: 0% = 0.0, 100% = 100.0\n                // for a and b: -100% = -125, 100% = 125\n                const LIGHTNESS_RANGE: f32 = 100.0;\n                const A_B_RANGE: f32 = 125.0;\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Lab));\n\n                AbsoluteColor::new(\n                    ColorSpace::Lab,\n                    l.resolve(origin_color.as_ref())?\n                        .map(|l| l.to_number(LIGHTNESS_RANGE)),\n                    a.resolve(origin_color.as_ref())?\n                        .map(|a| a.to_number(A_B_RANGE)),\n                    b.resolve(origin_color.as_ref())?\n                        .map(|b| b.to_number(A_B_RANGE)),\n                    alpha!(alpha, origin_color.as_ref()),\n                )\n            },\n            ColorFunction::Lch(origin_color, l, c, h, alpha) => {\n                // for L: 0% = 0.0, 100% = 100.0\n                // for C: 0% = 0, 100% = 150\n                const LIGHTNESS_RANGE: f32 = 100.0;\n                const CHROMA_RANGE: f32 = 150.0;\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Lch));\n\n                AbsoluteColor::new(\n                    ColorSpace::Lch,\n                    l.resolve(origin_color.as_ref())?\n                        .map(|l| l.to_number(LIGHTNESS_RANGE)),\n                    c.resolve(origin_color.as_ref())?\n                        .map(|c| c.to_number(CHROMA_RANGE)),\n                    h.resolve(origin_color.as_ref())?\n                        .map(|angle| normalize_hue(angle.degrees())),\n                    alpha!(alpha, origin_color.as_ref()),\n                )\n            },\n            ColorFunction::Oklab(origin_color, l, a, b, alpha) => {\n                // for L: 0% = 0.0, 100% = 1.0\n                // for a and b: -100% = -0.4, 100% = 0.4\n                const LIGHTNESS_RANGE: f32 = 1.0;\n                const A_B_RANGE: f32 = 0.4;\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Oklab));\n\n                AbsoluteColor::new(\n                    ColorSpace::Oklab,\n                    l.resolve(origin_color.as_ref())?\n                        .map(|l| l.to_number(LIGHTNESS_RANGE)),\n                    a.resolve(origin_color.as_ref())?\n                        .map(|a| a.to_number(A_B_RANGE)),\n                    b.resolve(origin_color.as_ref())?\n                        .map(|b| b.to_number(A_B_RANGE)),\n                    alpha!(alpha, origin_color.as_ref()),\n                )\n            },\n            ColorFunction::Oklch(origin_color, l, c, h, alpha) => {\n                // for L: 0% = 0.0, 100% = 1.0\n                // for C: 0% = 0.0 100% = 0.4\n                const LIGHTNESS_RANGE: f32 = 1.0;\n                const CHROMA_RANGE: f32 = 0.4;\n\n                let origin_color = origin_color\n                    .as_ref()\n                    .map(|o| o.to_color_space(ColorSpace::Oklch));\n\n                AbsoluteColor::new(\n                    ColorSpace::Oklch,\n                    l.resolve(origin_color.as_ref())?\n                        .map(|l| l.to_number(LIGHTNESS_RANGE)),\n                    c.resolve(origin_color.as_ref())?\n                        .map(|c| c.to_number(CHROMA_RANGE)),\n                    h.resolve(origin_color.as_ref())?\n                        .map(|angle| normalize_hue(angle.degrees())),\n                    alpha!(alpha, origin_color.as_ref()),\n                )\n            },\n            ColorFunction::Color(origin_color, r, g, b, alpha, color_space) => {\n                let origin_color = origin_color.as_ref().map(|o| {\n                    let mut result = o.to_color_space(*color_space);\n\n                    // If the origin color was a `rgb(..)` function, we should\n                    // make sure it doesn't have the legacy flag any more so\n                    // that it is recognized as a `color(srgb ..)` function.\n                    result.flags.set(ColorFlags::IS_LEGACY_SRGB, false);\n\n                    result\n                });\n\n                AbsoluteColor::new(\n                    *color_space,\n                    r.resolve(origin_color.as_ref())?.map(|c| c.to_number(1.0)),\n                    g.resolve(origin_color.as_ref())?.map(|c| c.to_number(1.0)),\n                    b.resolve(origin_color.as_ref())?.map(|c| c.to_number(1.0)),\n                    alpha!(alpha, origin_color.as_ref()),\n                )\n            },\n        })\n    }\n}\n\nimpl ColorFunction<SpecifiedColor> {\n    /// Return true if the color funciton has an origin color specified.\n    pub fn has_origin_color(&self) -> bool {\n        match self {\n            Self::Rgb(origin_color, ..)\n            | Self::Hsl(origin_color, ..)\n            | Self::Hwb(origin_color, ..)\n            | Self::Lab(origin_color, ..)\n            | Self::Lch(origin_color, ..)\n            | Self::Oklab(origin_color, ..)\n            | Self::Oklch(origin_color, ..)\n            | Self::Color(origin_color, ..) => origin_color.is_some(),\n        }\n    }\n\n    /// Try to resolve the color function to an [`AbsoluteColor`] that does not\n    /// contain any variables (currentcolor, color components, etc.).\n    pub fn resolve_to_absolute(&self) -> Result<AbsoluteColor, ()> {\n        // Map the color function to one with an absolute origin color.\n        self.map_origin_color(|o| o.resolve_to_absolute())?\n            .resolve_to_absolute()\n    }\n}\n\nimpl<Color> ColorFunction<Color> {\n    /// Map the origin color to another type.\n    pub fn map_origin_color<U>(\n        &self,\n        f: impl FnOnce(&Color) -> Result<U, ()>,\n    ) -> Result<ColorFunction<U>, ()> {\n        macro_rules! map {\n            ($f:ident, $o:expr, $c0:expr, $c1:expr, $c2:expr, $alpha:expr) => {{\n                ColorFunction::$f(\n                    match $o.as_ref() {\n                        Some(c) => Some(f(c)?),\n                        None => None,\n                    }\n                    .into(),\n                    $c0.clone(),\n                    $c1.clone(),\n                    $c2.clone(),\n                    $alpha.clone(),\n                )\n            }};\n        }\n        Ok(match self {\n            ColorFunction::Rgb(o, c0, c1, c2, alpha) => map!(Rgb, o, c0, c1, c2, alpha),\n            ColorFunction::Hsl(o, c0, c1, c2, alpha) => map!(Hsl, o, c0, c1, c2, alpha),\n            ColorFunction::Hwb(o, c0, c1, c2, alpha) => map!(Hwb, o, c0, c1, c2, alpha),\n            ColorFunction::Lab(o, c0, c1, c2, alpha) => map!(Lab, o, c0, c1, c2, alpha),\n            ColorFunction::Lch(o, c0, c1, c2, alpha) => map!(Lch, o, c0, c1, c2, alpha),\n            ColorFunction::Oklab(o, c0, c1, c2, alpha) => map!(Oklab, o, c0, c1, c2, alpha),\n            ColorFunction::Oklch(o, c0, c1, c2, alpha) => map!(Oklch, o, c0, c1, c2, alpha),\n            ColorFunction::Color(o, c0, c1, c2, alpha, color_space) => ColorFunction::Color(\n                match o.as_ref() {\n                    Some(c) => Some(f(c)?),\n                    None => None,\n                }\n                .into(),\n                c0.clone(),\n                c1.clone(),\n                c2.clone(),\n                alpha.clone(),\n                color_space.clone(),\n            ),\n        })\n    }\n}\n\nimpl ColorFunction<ComputedColor> {\n    /// Resolve a computed color function to an absolute computed color.\n    pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {\n        // Map the color function to one with an absolute origin color.\n        let resolvable = self\n            .map_origin_color(|o| Ok(o.resolve_to_absolute(current_color)))\n            .unwrap();\n        match resolvable.resolve_to_absolute() {\n            Ok(color) => color,\n            Err(..) => {\n                debug_assert!(\n                    false,\n                    \"the color could not be resolved even with a currentcolor specified?\"\n                );\n                AbsoluteColor::TRANSPARENT_BLACK\n            },\n        }\n    }\n}\n\nimpl<C: style_traits::ToCss> style_traits::ToCss for ColorFunction<C> {\n    fn to_css<W>(&self, dest: &mut style_traits::CssWriter<W>) -> std::fmt::Result\n    where\n        W: std::fmt::Write,\n    {\n        let (origin_color, alpha) = match self {\n            Self::Rgb(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"rgb(\")?;\n                (origin_color, alpha)\n            },\n            Self::Hsl(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"hsl(\")?;\n                (origin_color, alpha)\n            },\n            Self::Hwb(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"hwb(\")?;\n                (origin_color, alpha)\n            },\n            Self::Lab(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"lab(\")?;\n                (origin_color, alpha)\n            },\n            Self::Lch(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"lch(\")?;\n                (origin_color, alpha)\n            },\n            Self::Oklab(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"oklab(\")?;\n                (origin_color, alpha)\n            },\n            Self::Oklch(origin_color, _, _, _, alpha) => {\n                dest.write_str(\"oklch(\")?;\n                (origin_color, alpha)\n            },\n            Self::Color(origin_color, _, _, _, alpha, _) => {\n                dest.write_str(\"color(\")?;\n                (origin_color, alpha)\n            },\n        };\n\n        if let Optional::Some(origin_color) = origin_color {\n            dest.write_str(\"from \")?;\n            origin_color.to_css(dest)?;\n            dest.write_str(\" \")?;\n        }\n\n        let is_opaque = if let ColorComponent::Value(value) = *alpha {\n            value.to_number(OPAQUE) == OPAQUE\n        } else {\n            false\n        };\n\n        macro_rules! serialize_alpha {\n            ($alpha_component:expr) => {{\n                if !is_opaque && !matches!($alpha_component, ColorComponent::AlphaOmitted) {\n                    dest.write_str(\" / \")?;\n                    $alpha_component.to_css(dest)?;\n                }\n            }};\n        }\n\n        macro_rules! serialize_components {\n            ($c0:expr, $c1:expr, $c2:expr) => {{\n                debug_assert!(!matches!($c0, ColorComponent::AlphaOmitted));\n                debug_assert!(!matches!($c1, ColorComponent::AlphaOmitted));\n                debug_assert!(!matches!($c2, ColorComponent::AlphaOmitted));\n\n                $c0.to_css(dest)?;\n                dest.write_str(\" \")?;\n                $c1.to_css(dest)?;\n                dest.write_str(\" \")?;\n                $c2.to_css(dest)?;\n            }};\n        }\n\n        match self {\n            Self::Rgb(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Hsl(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Hwb(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Lab(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Lch(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Oklab(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Oklch(_, c0, c1, c2, alpha) => {\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n            Self::Color(_, c0, c1, c2, alpha, color_space) => {\n                color_space.to_css(dest)?;\n                dest.write_str(\" \")?;\n                serialize_components!(c0, c1, c2);\n                serialize_alpha!(alpha);\n            },\n        }\n\n        dest.write_str(\")\")\n    }\n}\n"
  },
  {
    "path": "style/color/component.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parse/serialize and resolve a single color component.\n\nuse std::fmt::Write;\n\nuse super::{\n    parsing::{rcs_enabled, ChannelKeyword},\n    AbsoluteColor,\n};\nuse crate::derives::*;\nuse crate::{\n    parser::ParserContext,\n    values::{\n        animated::ToAnimatedValue,\n        generics::calc::{CalcUnits, GenericCalcNode},\n        specified::calc::{AllowParse, Leaf},\n    },\n};\nuse cssparser::{color::OPAQUE, Parser, Token};\nuse style_traits::{ParseError, ToCss};\n\n/// A single color component.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\n#[repr(u8)]\npub enum ColorComponent<ValueType> {\n    /// The \"none\" keyword.\n    None,\n    /// A absolute value.\n    Value(ValueType),\n    /// A channel keyword, e.g. `r`, `l`, `alpha`, etc.\n    ChannelKeyword(ChannelKeyword),\n    /// A calc() value.\n    Calc(Box<GenericCalcNode<Leaf>>),\n    /// Used when alpha components are not specified.\n    AlphaOmitted,\n}\n\nimpl<ValueType> ColorComponent<ValueType> {\n    /// Return true if the component is \"none\".\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        matches!(self, Self::None)\n    }\n}\n\n/// An utility trait that allows the construction of [ColorComponent]\n/// `ValueType`'s after parsing a color component.\npub trait ColorComponentType: Sized + Clone {\n    // TODO(tlouw): This function should be named according to the rules in the spec\n    //              stating that all the values coming from color components are\n    //              numbers and that each has their own rules dependeing on types.\n    /// Construct a new component from a single value.\n    fn from_value(value: f32) -> Self;\n\n    /// Return the [CalcUnits] flags that the impl can handle.\n    fn units() -> CalcUnits;\n\n    /// Try to create a new component from the given token.\n    fn try_from_token(token: &Token) -> Result<Self, ()>;\n\n    /// Try to create a new component from the given [CalcNodeLeaf] that was\n    /// resolved from a [CalcNode].\n    fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()>;\n}\n\nimpl<ValueType: ColorComponentType> ColorComponent<ValueType> {\n    /// Parse a single [ColorComponent].\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_none: bool,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n\n        match *input.next()? {\n            Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case(\"none\") => {\n                Ok(ColorComponent::None)\n            },\n            ref t @ Token::Ident(ref ident) => {\n                let Ok(channel_keyword) = ChannelKeyword::from_ident(ident) else {\n                    return Err(location.new_unexpected_token_error(t.clone()));\n                };\n                Ok(ColorComponent::ChannelKeyword(channel_keyword))\n            },\n            Token::Function(ref name) => {\n                let function = GenericCalcNode::math_function(context, name, location)?;\n                let allow = AllowParse::new(if rcs_enabled() {\n                    ValueType::units() | CalcUnits::COLOR_COMPONENT\n                } else {\n                    ValueType::units()\n                });\n                let mut node = GenericCalcNode::parse(context, input, function, allow)?;\n\n                // TODO(tlouw): We only have to simplify the node when we have to store it, but we\n                //              only know if we have to store it much later when the whole color\n                //              can't be resolved to absolute at which point the calc nodes are\n                //              burried deep in a [ColorFunction] struct.\n                node.simplify_and_sort();\n\n                Ok(Self::Calc(Box::new(node)))\n            },\n            ref t => ValueType::try_from_token(t)\n                .map(Self::Value)\n                .map_err(|_| location.new_unexpected_token_error(t.clone())),\n        }\n    }\n\n    /// Resolve a [ColorComponent] into a float.  None is \"none\".\n    pub fn resolve(&self, origin_color: Option<&AbsoluteColor>) -> Result<Option<ValueType>, ()> {\n        Ok(match self {\n            ColorComponent::None => None,\n            ColorComponent::Value(value) => Some(value.clone()),\n            ColorComponent::ChannelKeyword(channel_keyword) => match origin_color {\n                Some(origin_color) => {\n                    let value = origin_color.get_component_by_channel_keyword(*channel_keyword)?;\n                    Some(ValueType::from_value(value.unwrap_or(0.0)))\n                },\n                None => return Err(()),\n            },\n            ColorComponent::Calc(node) => {\n                let Ok(resolved_leaf) = node.resolve_map(|leaf| {\n                    Ok(match leaf {\n                        Leaf::ColorComponent(channel_keyword) => match origin_color {\n                            Some(origin_color) => {\n                                let value = origin_color\n                                    .get_component_by_channel_keyword(*channel_keyword)?;\n                                Leaf::Number(value.unwrap_or(0.0))\n                            },\n                            None => return Err(()),\n                        },\n                        l => l.clone(),\n                    })\n                }) else {\n                    return Err(());\n                };\n\n                Some(ValueType::try_from_leaf(&resolved_leaf)?)\n            },\n            ColorComponent::AlphaOmitted => {\n                if let Some(origin_color) = origin_color {\n                    // <https://drafts.csswg.org/css-color-5/#rcs-intro>\n                    // If the alpha value of the relative color is omitted, it defaults to that of\n                    // the origin color (rather than defaulting to 100%, as it does in the absolute\n                    // syntax).\n                    origin_color.alpha().map(ValueType::from_value)\n                } else {\n                    Some(ValueType::from_value(OPAQUE))\n                }\n            },\n        })\n    }\n}\n\nimpl<ValueType: ToCss> ToCss for ColorComponent<ValueType> {\n    fn to_css<W>(&self, dest: &mut style_traits::CssWriter<W>) -> std::fmt::Result\n    where\n        W: Write,\n    {\n        match self {\n            ColorComponent::None => dest.write_str(\"none\")?,\n            ColorComponent::Value(value) => value.to_css(dest)?,\n            ColorComponent::ChannelKeyword(channel_keyword) => channel_keyword.to_css(dest)?,\n            ColorComponent::Calc(node) => {\n                // When we only have a channel keyword in a leaf node, we should serialize it with\n                // calc(..), except when one of the rgb color space functions are used, e.g.\n                // rgb(..), hsl(..) or hwb(..) for historical reasons.\n                // <https://github.com/web-platform-tests/wpt/issues/47921>\n                node.to_css(dest)?;\n            },\n            ColorComponent::AlphaOmitted => {\n                debug_assert!(false, \"can't serialize an omitted alpha component\");\n            },\n        }\n\n        Ok(())\n    }\n}\n\nimpl<ValueType> ToAnimatedValue for ColorComponent<ValueType> {\n    type AnimatedValue = Self;\n\n    fn to_animated_value(self, _context: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self\n    }\n\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated\n    }\n}\n"
  },
  {
    "path": "style/color/convert.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Color conversion algorithms.\n//!\n//! Algorithms, matrices and constants are from the [color-4] specification,\n//! unless otherwise specified:\n//!\n//! https://drafts.csswg.org/css-color-4/#color-conversion-code\n//!\n//! NOTE: Matrices has to be transposed from the examples in the spec for use\n//! with the `euclid` library.\n\nuse crate::color::ColorComponents;\nuse crate::values::normalize;\n\ntype Transform = euclid::default::Transform3D<f32>;\ntype Vector = euclid::default::Vector3D<f32>;\n\n/// Normalize hue into [0, 360).\n#[inline]\npub fn normalize_hue(hue: f32) -> f32 {\n    hue - 360. * (hue / 360.).floor()\n}\n\n/// Calculate the hue from RGB components and return it along with the min and\n/// max RGB values.\n#[inline]\nfn rgb_to_hue_min_max(red: f32, green: f32, blue: f32) -> (f32, f32, f32) {\n    let max = red.max(green).max(blue);\n    let min = red.min(green).min(blue);\n\n    let delta = max - min;\n\n    let hue = if delta != 0.0 {\n        60.0 * if max == red {\n            (green - blue) / delta + if green < blue { 6.0 } else { 0.0 }\n        } else if max == green {\n            (blue - red) / delta + 2.0\n        } else {\n            (red - green) / delta + 4.0\n        }\n    } else {\n        f32::NAN\n    };\n\n    (hue, min, max)\n}\n\n/// Convert from HSL notation to RGB notation.\n/// https://drafts.csswg.org/css-color-4/#hsl-to-rgb\n#[inline]\npub fn hsl_to_rgb(from: &ColorComponents) -> ColorComponents {\n    fn hue_to_rgb(t1: f32, t2: f32, hue: f32) -> f32 {\n        let hue = normalize_hue(hue);\n\n        if hue * 6.0 < 360.0 {\n            t1 + (t2 - t1) * hue / 60.0\n        } else if hue * 2.0 < 360.0 {\n            t2\n        } else if hue * 3.0 < 720.0 {\n            t1 + (t2 - t1) * (240.0 - hue) / 60.0\n        } else {\n            t1\n        }\n    }\n\n    // Convert missing components to 0.0.\n    let ColorComponents(hue, saturation, lightness) = from.map(normalize);\n    let saturation = saturation / 100.0;\n    let lightness = lightness / 100.0;\n\n    let t2 = if lightness <= 0.5 {\n        lightness * (saturation + 1.0)\n    } else {\n        lightness + saturation - lightness * saturation\n    };\n    let t1 = lightness * 2.0 - t2;\n\n    ColorComponents(\n        hue_to_rgb(t1, t2, hue + 120.0),\n        hue_to_rgb(t1, t2, hue),\n        hue_to_rgb(t1, t2, hue - 120.0),\n    )\n}\n\n/// Convert from RGB notation to HSL notation.\n/// https://drafts.csswg.org/css-color-4/#rgb-to-hsl\npub fn rgb_to_hsl(from: &ColorComponents) -> ColorComponents {\n    let ColorComponents(red, green, blue) = *from;\n\n    let (hue, min, max) = rgb_to_hue_min_max(red, green, blue);\n\n    let lightness = (min + max) / 2.0;\n    let delta = max - min;\n\n    let saturation = if delta != 0.0 {\n        if lightness == 0.0 || lightness == 1.0 {\n            0.0\n        } else {\n            (max - lightness) / lightness.min(1.0 - lightness)\n        }\n    } else {\n        0.0\n    };\n\n    ColorComponents(hue, saturation * 100.0, lightness * 100.0)\n}\n\n/// Convert from HWB notation to RGB notation.\n/// https://drafts.csswg.org/css-color-4/#hwb-to-rgb\n#[inline]\npub fn hwb_to_rgb(from: &ColorComponents) -> ColorComponents {\n    // Convert missing components to 0.0.\n    let ColorComponents(hue, whiteness, blackness) = from.map(normalize);\n\n    let whiteness = whiteness / 100.0;\n    let blackness = blackness / 100.0;\n\n    if whiteness + blackness >= 1.0 {\n        let gray = whiteness / (whiteness + blackness);\n        return ColorComponents(gray, gray, gray);\n    }\n\n    let x = 1.0 - whiteness - blackness;\n    hsl_to_rgb(&ColorComponents(hue, 100.0, 50.0)).map(|v| v * x + whiteness)\n}\n\n/// Convert from RGB notation to HWB notation.\n/// https://drafts.csswg.org/css-color-4/#rgb-to-hwb\n#[inline]\npub fn rgb_to_hwb(from: &ColorComponents) -> ColorComponents {\n    let ColorComponents(red, green, blue) = *from;\n\n    let (hue, min, max) = rgb_to_hue_min_max(red, green, blue);\n\n    let whiteness = min;\n    let blackness = 1.0 - max;\n\n    ColorComponents(hue, whiteness * 100.0, blackness * 100.0)\n}\n\n/// Calculate an epsilon for a specified range.\n#[inline]\npub fn epsilon_for_range(min: f32, max: f32) -> f32 {\n    (max - min) / 1.0e5\n}\n\n/// Convert from the rectangular orthogonal to the cylindrical polar coordinate\n/// system. This is used to convert (ok)lab to (ok)lch.\n/// <https://drafts.csswg.org/css-color-4/#lab-to-lch>\n#[inline]\npub fn orthogonal_to_polar(from: &ColorComponents, e: f32) -> ColorComponents {\n    let ColorComponents(lightness, a, b) = *from;\n\n    let chroma = (a * a + b * b).sqrt();\n\n    let hue = if a.abs() < e && b.abs() < e {\n        // For extremely small values of a and b ... the reported hue angle\n        // swinging about wildly and being essentially random ... this means\n        // the hue is powerless, and treated as missing when converted into LCH\n        // or Oklch.\n        f32::NAN\n    } else if chroma.abs() < e {\n        // Very small chroma values make the hue component powerless.\n        f32::NAN\n    } else {\n        normalize_hue(b.atan2(a).to_degrees())\n    };\n\n    ColorComponents(lightness, chroma, hue)\n}\n\n/// Convert from the cylindrical polar to the rectangular orthogonal coordinate\n/// system. This is used to convert (ok)lch to (ok)lab.\n/// <https://drafts.csswg.org/css-color-4/#lch-to-lab>\n#[inline]\npub fn polar_to_orthogonal(from: &ColorComponents) -> ColorComponents {\n    let ColorComponents(lightness, chroma, hue) = *from;\n\n    // A missing hue component results in an achromatic color.\n    if hue.is_nan() {\n        return ColorComponents(lightness, 0.0, 0.0);\n    }\n\n    let hue = hue.to_radians();\n    let a = chroma * hue.cos();\n    let b = chroma * hue.sin();\n\n    ColorComponents(lightness, a, b)\n}\n\n#[inline]\nfn transform(from: &ColorComponents, mat: &Transform) -> ColorComponents {\n    let result = mat.transform_vector3d(Vector::new(from.0, from.1, from.2));\n    ColorComponents(result.x, result.y, result.z)\n}\n\nfn xyz_d65_to_xyz_d50(from: &ColorComponents) -> ColorComponents {\n    #[rustfmt::skip]\n    const MAT: Transform = Transform::new(\n         1.0479298208405488,    0.029627815688159344, -0.009243058152591178, 0.0,\n         0.022946793341019088,  0.990434484573249,     0.015055144896577895, 0.0,\n        -0.05019222954313557,  -0.01707382502938514,   0.7518742899580008,   0.0,\n         0.0,                   0.0,                   0.0,                  1.0,\n    );\n\n    transform(from, &MAT)\n}\n\nfn xyz_d50_to_xyz_d65(from: &ColorComponents) -> ColorComponents {\n    #[rustfmt::skip]\n    const MAT: Transform = Transform::new(\n         0.9554734527042182,   -0.028369706963208136,  0.012314001688319899, 0.0,\n        -0.023098536874261423,  1.0099954580058226,   -0.020507696433477912, 0.0,\n         0.0632593086610217,    0.021041398966943008,  1.3303659366080753,   0.0,\n         0.0,                   0.0,                   0.0,                  1.0,\n    );\n\n    transform(from, &MAT)\n}\n\n/// A reference white that is used during color conversion.\npub enum WhitePoint {\n    /// D50 white reference.\n    D50,\n    /// D65 white reference.\n    D65,\n}\n\nimpl WhitePoint {\n    const fn values(&self) -> ColorComponents {\n        // <https://drafts.csswg.org/css-color-4/#color-conversion-code>\n        match self {\n            // [0.3457 / 0.3585, 1.00000, (1.0 - 0.3457 - 0.3585) / 0.3585]\n            WhitePoint::D50 => ColorComponents(0.9642956764295677, 1.0, 0.8251046025104602),\n            // [0.3127 / 0.3290, 1.00000, (1.0 - 0.3127 - 0.3290) / 0.3290]\n            WhitePoint::D65 => ColorComponents(0.9504559270516716, 1.0, 1.0890577507598784),\n        }\n    }\n}\n\nfn convert_white_point(from: WhitePoint, to: WhitePoint, components: &mut ColorComponents) {\n    match (from, to) {\n        (WhitePoint::D50, WhitePoint::D65) => *components = xyz_d50_to_xyz_d65(components),\n        (WhitePoint::D65, WhitePoint::D50) => *components = xyz_d65_to_xyz_d50(components),\n        _ => {},\n    }\n}\n\n/// A trait that allows conversion of color spaces to and from XYZ coordinate\n/// space with a specified white point.\n///\n/// Allows following the specified method of converting between color spaces:\n/// - Convert to values to sRGB linear light.\n/// - Convert to XYZ coordinate space.\n/// - Adjust white point to target white point.\n/// - Convert to sRGB linear light in target color space.\n/// - Convert to sRGB gamma encoded in target color space.\n///\n/// https://drafts.csswg.org/css-color-4/#color-conversion\npub trait ColorSpaceConversion {\n    /// The white point that the implementer is represented in.\n    const WHITE_POINT: WhitePoint;\n\n    /// Convert the components from sRGB gamma encoded values to sRGB linear\n    /// light values.\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents;\n\n    /// Convert the components from sRGB linear light values to XYZ coordinate\n    /// space.\n    fn to_xyz(from: &ColorComponents) -> ColorComponents;\n\n    /// Convert the components from XYZ coordinate space to sRGB linear light\n    /// values.\n    fn from_xyz(from: &ColorComponents) -> ColorComponents;\n\n    /// Convert the components from sRGB linear light values to sRGB gamma\n    /// encoded values.\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents;\n}\n\n/// Convert the color components from the specified color space to XYZ and\n/// return the components and the white point they are in.\npub fn to_xyz<From: ColorSpaceConversion>(from: &ColorComponents) -> (ColorComponents, WhitePoint) {\n    // Convert the color components where in-gamut values are in the range\n    // [0 - 1] to linear light (un-companded) form.\n    let result = From::to_linear_light(from);\n\n    // Convert the color components from the source color space to XYZ.\n    (From::to_xyz(&result), From::WHITE_POINT)\n}\n\n/// Convert the color components from XYZ at the given white point to the\n/// specified color space.\npub fn from_xyz<To: ColorSpaceConversion>(\n    from: &ColorComponents,\n    white_point: WhitePoint,\n) -> ColorComponents {\n    let mut xyz = from.clone();\n\n    // Convert the white point if needed.\n    convert_white_point(white_point, To::WHITE_POINT, &mut xyz);\n\n    // Convert the color from XYZ to the target color space.\n    let result = To::from_xyz(&xyz);\n\n    // Convert the color components of linear-light values in the range\n    // [0 - 1] to a gamma corrected form.\n    To::to_gamma_encoded(&result)\n}\n\n/// The sRGB color space.\n/// https://drafts.csswg.org/css-color-4/#predefined-sRGB\npub struct Srgb;\n\nimpl Srgb {\n    #[rustfmt::skip]\n    const TO_XYZ: Transform = Transform::new(\n        0.4123907992659595,  0.21263900587151036, 0.01933081871559185, 0.0,\n        0.35758433938387796, 0.7151686787677559,  0.11919477979462599, 0.0,\n        0.1804807884018343,  0.07219231536073371, 0.9505321522496606,  0.0,\n        0.0,                 0.0,                 0.0,                 1.0,\n    );\n\n    #[rustfmt::skip]\n    const FROM_XYZ: Transform = Transform::new(\n         3.2409699419045213, -0.9692436362808798,  0.05563007969699361, 0.0,\n        -1.5373831775700935,  1.8759675015077206, -0.20397695888897657, 0.0,\n        -0.4986107602930033,  0.04155505740717561, 1.0569715142428786,  0.0,\n         0.0,                 0.0,                 0.0,                 1.0,\n    );\n}\n\nimpl ColorSpaceConversion for Srgb {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|value| {\n            let abs = value.abs();\n\n            if abs < 0.04045 {\n                value / 12.92\n            } else {\n                value.signum() * ((abs + 0.055) / 1.055).powf(2.4)\n            }\n        })\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::FROM_XYZ)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|value| {\n            let abs = value.abs();\n\n            if abs > 0.0031308 {\n                value.signum() * (1.055 * abs.powf(1.0 / 2.4) - 0.055)\n            } else {\n                12.92 * value\n            }\n        })\n    }\n}\n\n/// Color specified with hue, saturation and lightness components.\npub struct Hsl;\n\nimpl ColorSpaceConversion for Hsl {\n    const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_linear_light(&hsl_to_rgb(from))\n    }\n\n    #[inline]\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_xyz(from)\n    }\n\n    #[inline]\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::from_xyz(from)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        rgb_to_hsl(&Srgb::to_gamma_encoded(from))\n    }\n}\n\n/// Color specified with hue, whiteness and blackness components.\npub struct Hwb;\n\nimpl ColorSpaceConversion for Hwb {\n    const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_linear_light(&hwb_to_rgb(from))\n    }\n\n    #[inline]\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_xyz(from)\n    }\n\n    #[inline]\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::from_xyz(from)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        rgb_to_hwb(&Srgb::to_gamma_encoded(from))\n    }\n}\n\n/// The same as sRGB color space, except the transfer function is linear light.\n/// https://drafts.csswg.org/css-color-4/#predefined-sRGB-linear\npub struct SrgbLinear;\n\nimpl ColorSpaceConversion for SrgbLinear {\n    const WHITE_POINT: WhitePoint = Srgb::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        // Already in linear light form.\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_xyz(from)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        Srgb::from_xyz(from)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        // Stay in linear light form.\n        from.clone()\n    }\n}\n\n/// The Display-P3 color space.\n/// https://drafts.csswg.org/css-color-4/#predefined-display-p3\npub struct DisplayP3;\n\nimpl DisplayP3 {\n    #[rustfmt::skip]\n    const TO_XYZ: Transform = Transform::new(\n        0.48657094864821626, 0.22897456406974884, 0.0,                  0.0,\n        0.26566769316909294, 0.6917385218365062,  0.045113381858902575, 0.0,\n        0.1982172852343625,  0.079286914093745,   1.0439443689009757,   0.0,\n        0.0,                 0.0,                 0.0,                  1.0,\n    );\n\n    #[rustfmt::skip]\n    const FROM_XYZ: Transform = Transform::new(\n         2.4934969119414245,  -0.829488969561575,    0.035845830243784335, 0.0,\n        -0.9313836179191236,   1.7626640603183468,  -0.07617238926804171,  0.0,\n        -0.40271078445071684,  0.02362468584194359,  0.9568845240076873,   0.0,\n         0.0,                  0.0,                  0.0,                  1.0,\n    );\n}\n\nimpl ColorSpaceConversion for DisplayP3 {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_linear_light(from)\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::FROM_XYZ)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        Srgb::to_gamma_encoded(from)\n    }\n}\n\n/// The Display-P3-linear color space. This is basically display-p3 without gamma encoding.\npub struct DisplayP3Linear;\nimpl ColorSpaceConversion for DisplayP3Linear {\n    const WHITE_POINT: WhitePoint = DisplayP3::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        *from\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        DisplayP3::to_xyz(from)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        DisplayP3::from_xyz(from)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        *from\n    }\n}\n\n/// The a98-rgb color space.\n/// https://drafts.csswg.org/css-color-4/#predefined-a98-rgb\npub struct A98Rgb;\n\nimpl A98Rgb {\n    #[rustfmt::skip]\n    const TO_XYZ: Transform = Transform::new(\n        0.5766690429101308,  0.29734497525053616, 0.027031361386412378, 0.0,\n        0.18555823790654627, 0.627363566255466,   0.07068885253582714,  0.0,\n        0.18822864623499472, 0.07529145849399789, 0.9913375368376389,   0.0,\n        0.0,                 0.0,                 0.0,                  1.0,\n    );\n\n    #[rustfmt::skip]\n    const FROM_XYZ: Transform = Transform::new(\n         2.041587903810746,  -0.9692436362808798,   0.013444280632031024, 0.0,\n        -0.5650069742788596,  1.8759675015077206,  -0.11836239223101824,  0.0,\n        -0.3447313507783295,  0.04155505740717561,  1.0151749943912054,   0.0,\n         0.0,                 0.0,                  0.0,                  1.0,\n    );\n}\n\nimpl ColorSpaceConversion for A98Rgb {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|v| v.signum() * v.abs().powf(2.19921875))\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::FROM_XYZ)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n            .map(|v| v.signum() * v.abs().powf(0.4547069271758437))\n    }\n}\n\n/// The ProPhoto RGB color space.\n/// https://drafts.csswg.org/css-color-4/#predefined-prophoto-rgb\npub struct ProphotoRgb;\n\nimpl ProphotoRgb {\n    #[rustfmt::skip]\n    const TO_XYZ: Transform = Transform::new(\n        0.7977604896723027,  0.2880711282292934,     0.0,                0.0,\n        0.13518583717574031, 0.7118432178101014,     0.0,                0.0,\n        0.0313493495815248,  0.00008565396060525902, 0.8251046025104601, 0.0,\n        0.0,                 0.0,                    0.0,                1.0,\n    );\n\n    #[rustfmt::skip]\n    const FROM_XYZ: Transform = Transform::new(\n         1.3457989731028281,  -0.5446224939028347,  0.0,                0.0,\n        -0.25558010007997534,  1.5082327413132781,  0.0,                0.0,\n        -0.05110628506753401,  0.02053603239147973, 1.2119675456389454, 0.0,\n         0.0,                  0.0,                 0.0,                1.0,\n    );\n}\n\nimpl ColorSpaceConversion for ProphotoRgb {\n    const WHITE_POINT: WhitePoint = WhitePoint::D50;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|value| {\n            const ET2: f32 = 16.0 / 512.0;\n\n            let abs = value.abs();\n\n            if abs <= ET2 {\n                value / 16.0\n            } else {\n                value.signum() * abs.powf(1.8)\n            }\n        })\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::FROM_XYZ)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        const ET: f32 = 1.0 / 512.0;\n\n        from.clone().map(|v| {\n            let abs = v.abs();\n            if abs >= ET {\n                v.signum() * abs.powf(1.0 / 1.8)\n            } else {\n                16.0 * v\n            }\n        })\n    }\n}\n\n/// The Rec.2020 color space.\n/// https://drafts.csswg.org/css-color-4/#predefined-rec2020\npub struct Rec2020;\n\nimpl Rec2020 {\n    const ALPHA: f32 = 1.09929682680944;\n    const BETA: f32 = 0.018053968510807;\n\n    #[rustfmt::skip]\n    const TO_XYZ: Transform = Transform::new(\n        0.6369580483012913,  0.26270021201126703,  0.0,                  0.0,\n        0.14461690358620838, 0.677998071518871,    0.028072693049087508, 0.0,\n        0.16888097516417205, 0.059301716469861945, 1.0609850577107909,   0.0,\n        0.0,                 0.0,                  0.0,                  1.0,\n    );\n\n    #[rustfmt::skip]\n    const FROM_XYZ: Transform = Transform::new(\n         1.7166511879712676, -0.666684351832489,    0.017639857445310915, 0.0,\n        -0.3556707837763924,  1.616481236634939,   -0.042770613257808655, 0.0,\n        -0.2533662813736598,  0.01576854581391113,  0.942103121235474,    0.0,\n         0.0,                 0.0,                  0.0,                  1.0,\n    );\n}\n\nimpl ColorSpaceConversion for Rec2020 {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|value| {\n            let abs = value.abs();\n\n            if abs < Self::BETA * 4.5 {\n                value / 4.5\n            } else {\n                value.signum() * ((abs + Self::ALPHA - 1.0) / Self::ALPHA).powf(1.0 / 0.45)\n            }\n        })\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        transform(from, &Self::FROM_XYZ)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        from.clone().map(|v| {\n            let abs = v.abs();\n\n            if abs > Self::BETA {\n                v.signum() * (Self::ALPHA * abs.powf(0.45) - (Self::ALPHA - 1.0))\n            } else {\n                4.5 * v\n            }\n        })\n    }\n}\n\n/// A color in the XYZ coordinate space with a D50 white reference.\n/// https://drafts.csswg.org/css-color-4/#predefined-xyz\npub struct XyzD50;\n\nimpl ColorSpaceConversion for XyzD50 {\n    const WHITE_POINT: WhitePoint = WhitePoint::D50;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n}\n\n/// A color in the XYZ coordinate space with a D65 white reference.\n/// https://drafts.csswg.org/css-color-4/#predefined-xyz\npub struct XyzD65;\n\nimpl ColorSpaceConversion for XyzD65 {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        from.clone()\n    }\n}\n\n/// The Lab color space.\n/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch\npub struct Lab;\n\nimpl Lab {\n    const KAPPA: f32 = 24389.0 / 27.0;\n    const EPSILON: f32 = 216.0 / 24389.0;\n}\n\nimpl ColorSpaceConversion for Lab {\n    const WHITE_POINT: WhitePoint = WhitePoint::D50;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n\n    /// Convert a CIELAB color to XYZ as specified in [1] and [2].\n    ///\n    /// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined\n    /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        let ColorComponents(lightness, a, b) = *from;\n\n        let f1 = (lightness + 16.0) / 116.0;\n        let f0 = f1 + a / 500.0;\n        let f2 = f1 - b / 200.0;\n\n        let f0_cubed = f0 * f0 * f0;\n        let x = if f0_cubed > Self::EPSILON {\n            f0_cubed\n        } else {\n            (116.0 * f0 - 16.0) / Self::KAPPA\n        };\n\n        let y = if lightness > Self::KAPPA * Self::EPSILON {\n            let v = (lightness + 16.0) / 116.0;\n            v * v * v\n        } else {\n            lightness / Self::KAPPA\n        };\n\n        let f2_cubed = f2 * f2 * f2;\n        let z = if f2_cubed > Self::EPSILON {\n            f2_cubed\n        } else {\n            (116.0 * f2 - 16.0) / Self::KAPPA\n        };\n\n        ColorComponents(x, y, z) * Self::WHITE_POINT.values()\n    }\n\n    /// Convert an XYZ color to LAB as specified in [1] and [2].\n    ///\n    /// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab\n    /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        let adapted = *from / Self::WHITE_POINT.values();\n\n        // 4. Convert D50-adapted XYZ to Lab.\n        let ColorComponents(f0, f1, f2) = adapted.map(|v| {\n            if v > Self::EPSILON {\n                v.cbrt()\n            } else {\n                (Self::KAPPA * v + 16.0) / 116.0\n            }\n        });\n\n        let lightness = 116.0 * f1 - 16.0;\n        let a = 500.0 * (f0 - f1);\n        let b = 200.0 * (f1 - f2);\n\n        ColorComponents(lightness, a, b)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n}\n\n/// The Lch color space.\n/// https://drafts.csswg.org/css-color-4/#specifying-lab-lch\npub struct Lch;\n\nimpl ColorSpaceConversion for Lch {\n    const WHITE_POINT: WhitePoint = Lab::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        // Convert LCH to Lab first.\n        let lab = polar_to_orthogonal(from);\n\n        // Then convert the Lab to XYZ.\n        Lab::to_xyz(&lab)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        // First convert the XYZ to LAB.\n        let lab = Lab::from_xyz(&from);\n\n        // Then convert the Lab to LCH.\n        orthogonal_to_polar(&lab, epsilon_for_range(0.0, 100.0))\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n}\n\n/// The Oklab color space.\n/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch\npub struct Oklab;\n\nimpl Oklab {\n    #[rustfmt::skip]\n    const XYZ_TO_LMS: Transform = Transform::new(\n         0.8190224432164319,  0.0329836671980271,  0.048177199566046255, 0.0,\n         0.3619062562801221,  0.9292868468965546,  0.26423952494422764,  0.0,\n        -0.12887378261216414, 0.03614466816999844, 0.6335478258136937,   0.0,\n         0.0,                 0.0,                 0.0,                  1.0,\n    );\n\n    #[rustfmt::skip]\n    const LMS_TO_OKLAB: Transform = Transform::new(\n         0.2104542553,  1.9779984951,  0.0259040371, 0.0,\n         0.7936177850, -2.4285922050,  0.7827717662, 0.0,\n        -0.0040720468,  0.4505937099, -0.8086757660, 0.0,\n         0.0,           0.0,           0.0,          1.0,\n    );\n\n    #[rustfmt::skip]\n    const LMS_TO_XYZ: Transform = Transform::new(\n         1.2268798733741557,  -0.04057576262431372, -0.07637294974672142, 0.0,\n        -0.5578149965554813,   1.1122868293970594,  -0.4214933239627914,  0.0,\n         0.28139105017721583, -0.07171106666151701,  1.5869240244272418,  0.0,\n         0.0,                  0.0,                  0.0,                 1.0,\n    );\n\n    #[rustfmt::skip]\n    const OKLAB_TO_LMS: Transform = Transform::new(\n        0.99999999845051981432,  1.0000000088817607767,    1.0000000546724109177,   0.0,\n        0.39633779217376785678, -0.1055613423236563494,   -0.089484182094965759684, 0.0,\n        0.21580375806075880339, -0.063854174771705903402, -1.2914855378640917399,   0.0,\n        0.0,                     0.0,                      0.0,                     1.0,\n    );\n}\n\nimpl ColorSpaceConversion for Oklab {\n    const WHITE_POINT: WhitePoint = WhitePoint::D65;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        let lms = transform(&from, &Self::OKLAB_TO_LMS);\n        let lms = lms.map(|v| v * v * v);\n        transform(&lms, &Self::LMS_TO_XYZ)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        let lms = transform(&from, &Self::XYZ_TO_LMS);\n        let lms = lms.map(|v| v.cbrt());\n        transform(&lms, &Self::LMS_TO_OKLAB)\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n}\n\n/// The Oklch color space.\n/// https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch\npub struct Oklch;\n\nimpl ColorSpaceConversion for Oklch {\n    const WHITE_POINT: WhitePoint = Oklab::WHITE_POINT;\n\n    fn to_linear_light(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n\n    fn to_xyz(from: &ColorComponents) -> ColorComponents {\n        // First convert OkLCH to Oklab.\n        let oklab = polar_to_orthogonal(from);\n\n        // Then convert Oklab to XYZ.\n        Oklab::to_xyz(&oklab)\n    }\n\n    fn from_xyz(from: &ColorComponents) -> ColorComponents {\n        // First convert XYZ to Oklab.\n        let lab = Oklab::from_xyz(&from);\n\n        // Then convert Oklab to OkLCH.\n        orthogonal_to_polar(&lab, epsilon_for_range(0.0, 1.0))\n    }\n\n    fn to_gamma_encoded(from: &ColorComponents) -> ColorComponents {\n        // No need for conversion.\n        from.clone()\n    }\n}\n"
  },
  {
    "path": "style/color/mix.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Color mixing/interpolation.\n\nuse super::{AbsoluteColor, ColorFlags, ColorSpace};\nuse crate::color::ColorMixItemList;\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::color::ColorMixFlags;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// A hue-interpolation-method as defined in [1].\n///\n/// [1]: https://drafts.csswg.org/css-color-4/#typedef-hue-interpolation-method\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum HueInterpolationMethod {\n    /// https://drafts.csswg.org/css-color-4/#shorter\n    Shorter,\n    /// https://drafts.csswg.org/css-color-4/#longer\n    Longer,\n    /// https://drafts.csswg.org/css-color-4/#increasing\n    Increasing,\n    /// https://drafts.csswg.org/css-color-4/#decreasing\n    Decreasing,\n    /// https://drafts.csswg.org/css-color-4/#specified\n    Specified,\n}\n\n/// https://drafts.csswg.org/css-color-4/#color-interpolation-method\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToShmem,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n)]\n#[repr(C)]\npub struct ColorInterpolationMethod {\n    /// The color-space the interpolation should be done in.\n    pub space: ColorSpace,\n    /// The hue interpolation method.\n    pub hue: HueInterpolationMethod,\n}\n\nimpl ColorInterpolationMethod {\n    /// Returns the srgb interpolation method.\n    pub const fn srgb() -> Self {\n        Self {\n            space: ColorSpace::Srgb,\n            hue: HueInterpolationMethod::Shorter,\n        }\n    }\n\n    /// Return the oklab interpolation method used for default color\n    /// interpolcation.\n    pub const fn oklab() -> Self {\n        Self {\n            space: ColorSpace::Oklab,\n            hue: HueInterpolationMethod::Shorter,\n        }\n    }\n\n    /// Return true if the this is the default method.\n    pub fn is_default(&self) -> bool {\n        self.space == ColorSpace::Oklab\n    }\n\n    /// Decides the best method for interpolating between the given colors.\n    /// https://drafts.csswg.org/css-color-4/#interpolation-space\n    pub fn best_interpolation_between(left: &AbsoluteColor, right: &AbsoluteColor) -> Self {\n        // The default color space to use for interpolation is Oklab. However,\n        // if either of the colors are in legacy rgb(), hsl() or hwb(), then\n        // interpolation is done in sRGB.\n        if !left.is_legacy_syntax() || !right.is_legacy_syntax() {\n            Self::default()\n        } else {\n            Self::srgb()\n        }\n    }\n}\n\nimpl Default for ColorInterpolationMethod {\n    fn default() -> Self {\n        Self::oklab()\n    }\n}\n\nimpl Parse for ColorInterpolationMethod {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_ident_matching(\"in\")?;\n        let space = ColorSpace::parse(input)?;\n        // https://drafts.csswg.org/css-color-4/#hue-interpolation\n        //     Unless otherwise specified, if no specific hue interpolation\n        //     algorithm is selected by the host syntax, the default is shorter.\n        let hue = if space.is_polar() {\n            input\n                .try_parse(|input| -> Result<_, ParseError<'i>> {\n                    let hue = HueInterpolationMethod::parse(input)?;\n                    input.expect_ident_matching(\"hue\")?;\n                    Ok(hue)\n                })\n                .unwrap_or(HueInterpolationMethod::Shorter)\n        } else {\n            HueInterpolationMethod::Shorter\n        };\n        Ok(Self { space, hue })\n    }\n}\n\nimpl ToCss for ColorInterpolationMethod {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"in \")?;\n        self.space.to_css(dest)?;\n        if self.hue != HueInterpolationMethod::Shorter {\n            dest.write_char(' ')?;\n            self.hue.to_css(dest)?;\n            dest.write_str(\" hue\")?;\n        }\n        Ok(())\n    }\n}\n\n/// A color and its weight for use in a color mix.\npub struct ColorMixItem {\n    /// The color being mixed.\n    pub color: AbsoluteColor,\n    /// How much this color contributes to the final mix.\n    pub weight: f32,\n}\n\nimpl ColorMixItem {\n    /// Create a new color item for mixing.\n    #[inline]\n    pub fn new(color: AbsoluteColor, weight: f32) -> Self {\n        Self { color, weight }\n    }\n}\n\n/// Mix N colors into one (left-to-right fold).\npub fn mix_many(\n    interpolation: ColorInterpolationMethod,\n    items: impl IntoIterator<Item = ColorMixItem>,\n    flags: ColorMixFlags,\n) -> AbsoluteColor {\n    let items = items.into_iter().collect::<ColorMixItemList<_>>();\n\n    // Match the behavior when the sum of weights equal 0.\n    if items.is_empty() {\n        return AbsoluteColor::TRANSPARENT_BLACK.to_color_space(interpolation.space);\n    }\n\n    let normalize = flags.contains(ColorMixFlags::NORMALIZE_WEIGHTS);\n    let mut weight_scale = 1.0;\n    let mut alpha_multiplier = 1.0;\n    if normalize {\n        // https://drafts.csswg.org/css-color-5/#color-mix-percent-norm\n        let sum: f32 = items.iter().map(|item| item.weight).sum();\n        if sum == 0.0 {\n            return AbsoluteColor::TRANSPARENT_BLACK.to_color_space(interpolation.space);\n        }\n        if (sum - 1.0).abs() > f32::EPSILON {\n            weight_scale = 1.0 / sum;\n            if sum < 1.0 {\n                alpha_multiplier = sum;\n            }\n        }\n    }\n\n    // We can unwrap here, because we already checked for no items.\n    let (first, rest) = items.split_first().unwrap();\n    let mut accumulated_color = convert_for_mix(&first.color, interpolation.space);\n    let mut accumulated_weight = first.weight * weight_scale;\n\n    for item in rest {\n        let weight = item.weight * weight_scale;\n        let combined = accumulated_weight + weight;\n        if combined == 0.0 {\n            // If both are 0, this fold doesn't contribute anything to the result.\n            continue;\n        }\n        let right = convert_for_mix(&item.color, interpolation.space);\n\n        let (left_weight, right_weight) = if normalize {\n            (accumulated_weight / combined, weight / combined)\n        } else {\n            (accumulated_weight, weight)\n        };\n\n        accumulated_color = mix_with_weights(\n            &accumulated_color,\n            left_weight,\n            &right,\n            right_weight,\n            interpolation.hue,\n        );\n        accumulated_weight = combined;\n    }\n\n    let components = accumulated_color.raw_components();\n    let alpha = components[3] * alpha_multiplier;\n\n    // FIXME: In rare cases we end up with 0.999995 in the alpha channel,\n    //        so we reduce the precision to avoid serializing to\n    //        rgba(?, ?, ?, 1).  This is not ideal, so we should look into\n    //        ways to avoid it. Maybe pre-multiply all color components and\n    //        then divide after calculations?\n    let alpha = (alpha.clamp(0.0, 1.0) * 1000.0).round() / 1000.0;\n\n    let mut result = AbsoluteColor::new(\n        interpolation.space,\n        components[0],\n        components[1],\n        components[2],\n        alpha,\n    );\n    result.flags = accumulated_color.flags;\n\n    if flags.contains(ColorMixFlags::RESULT_IN_MODERN_SYNTAX) {\n        // If the result *MUST* be in modern syntax, then make sure it is in a\n        // color space that allows the modern syntax. So hsl and hwb will be\n        // converted to srgb.\n        if result.is_legacy_syntax() {\n            result.to_color_space(ColorSpace::Srgb)\n        } else {\n            result\n        }\n    } else if items.iter().all(|item| item.color.is_legacy_syntax()) {\n        // If both sides of the mix is legacy then convert the result back into\n        // legacy.\n        result.into_srgb_legacy()\n    } else {\n        result\n    }\n}\n\n/// What the outcome of each component should be in a mix result.\n#[derive(Clone, Copy, PartialEq)]\n#[repr(u8)]\nenum ComponentMixOutcome {\n    /// Mix the left and right sides to give the result.\n    Mix,\n    /// Carry the left side forward to the result.\n    UseLeft,\n    /// Carry the right side forward to the result.\n    UseRight,\n    /// The resulting component should also be none.\n    None,\n}\n\nimpl ComponentMixOutcome {\n    fn from_colors(\n        left: &AbsoluteColor,\n        right: &AbsoluteColor,\n        flags_to_check: ColorFlags,\n    ) -> Self {\n        match (\n            left.flags.contains(flags_to_check),\n            right.flags.contains(flags_to_check),\n        ) {\n            (true, true) => Self::None,\n            (true, false) => Self::UseRight,\n            (false, true) => Self::UseLeft,\n            (false, false) => Self::Mix,\n        }\n    }\n}\n\nimpl AbsoluteColor {\n    /// Calculate the flags that should be carried forward a color before converting\n    /// it to the interpolation color space according to:\n    /// <https://drafts.csswg.org/css-color-4/#interpolation-missing>\n    fn carry_forward_analogous_missing_components(&mut self, source: &AbsoluteColor) {\n        use ColorFlags as F;\n        use ColorSpace as S;\n\n        if source.color_space == self.color_space {\n            return;\n        }\n\n        // Reds             r, x\n        // Greens           g, y\n        // Blues            b, z\n        if source.color_space.is_rgb_or_xyz_like() && self.color_space.is_rgb_or_xyz_like() {\n            return;\n        }\n\n        // Lightness        L\n        if matches!(source.color_space, S::Lab | S::Lch | S::Oklab | S::Oklch) {\n            if matches!(self.color_space, S::Lab | S::Lch | S::Oklab | S::Oklch) {\n                self.flags |= source.flags & F::C0_IS_NONE;\n            } else if matches!(self.color_space, S::Hsl) {\n                if source.flags.contains(F::C0_IS_NONE) {\n                    self.flags.insert(F::C2_IS_NONE)\n                }\n            }\n        } else if matches!(source.color_space, S::Hsl)\n            && matches!(self.color_space, S::Lab | S::Lch | S::Oklab | S::Oklch)\n        {\n            if source.flags.contains(F::C2_IS_NONE) {\n                self.flags.insert(F::C0_IS_NONE)\n            }\n        }\n\n        // Colorfulness     C, S\n        if matches!(source.color_space, S::Hsl | S::Lch | S::Oklch)\n            && matches!(self.color_space, S::Hsl | S::Lch | S::Oklch)\n        {\n            self.flags |= source.flags & F::C1_IS_NONE;\n        }\n\n        // Hue              H\n        if matches!(source.color_space, S::Hsl | S::Hwb) {\n            if matches!(self.color_space, S::Hsl | S::Hwb) {\n                self.flags |= source.flags & F::C0_IS_NONE;\n            } else if matches!(self.color_space, S::Lch | S::Oklch) {\n                if source.flags.contains(F::C0_IS_NONE) {\n                    self.flags.insert(F::C2_IS_NONE)\n                }\n            }\n        } else if matches!(source.color_space, S::Lch | S::Oklch) {\n            if matches!(self.color_space, S::Hsl | S::Hwb) {\n                if source.flags.contains(F::C2_IS_NONE) {\n                    self.flags.insert(F::C0_IS_NONE)\n                }\n            } else if matches!(self.color_space, S::Lch | S::Oklch) {\n                self.flags |= source.flags & F::C2_IS_NONE;\n            }\n        }\n\n        // Opponent         a, a\n        // Opponent         b, b\n        if matches!(source.color_space, S::Lab | S::Oklab)\n            && matches!(self.color_space, S::Lab | S::Oklab)\n        {\n            self.flags |= source.flags & F::C1_IS_NONE;\n            self.flags |= source.flags & F::C2_IS_NONE;\n        }\n    }\n}\n\n/// Mix two colors already in the interpolation color space.\nfn mix_with_weights(\n    left: &AbsoluteColor,\n    left_weight: f32,\n    right: &AbsoluteColor,\n    right_weight: f32,\n    hue_interpolation: HueInterpolationMethod,\n) -> AbsoluteColor {\n    debug_assert!(right.color_space == left.color_space);\n    let color_space = left.color_space;\n\n    let outcomes = [\n        ComponentMixOutcome::from_colors(&left, &right, ColorFlags::C0_IS_NONE),\n        ComponentMixOutcome::from_colors(&left, &right, ColorFlags::C1_IS_NONE),\n        ComponentMixOutcome::from_colors(&left, &right, ColorFlags::C2_IS_NONE),\n        ComponentMixOutcome::from_colors(&left, &right, ColorFlags::ALPHA_IS_NONE),\n    ];\n\n    // Convert both sides into just components.\n    let left = left.raw_components();\n    let right = right.raw_components();\n\n    let (result, result_flags) = interpolate_premultiplied(\n        &left,\n        left_weight,\n        &right,\n        right_weight,\n        color_space.hue_index(),\n        hue_interpolation,\n        &outcomes,\n    );\n\n    let mut result = AbsoluteColor::new(color_space, result[0], result[1], result[2], result[3]);\n    result.flags = result_flags;\n    result\n}\n\nfn convert_for_mix(color: &AbsoluteColor, color_space: ColorSpace) -> AbsoluteColor {\n    let mut converted = color.to_color_space(color_space);\n    converted.carry_forward_analogous_missing_components(color);\n    converted\n}\n\nfn interpolate_premultiplied_component(\n    left: f32,\n    left_weight: f32,\n    left_alpha: f32,\n    right: f32,\n    right_weight: f32,\n    right_alpha: f32,\n) -> f32 {\n    left * left_weight * left_alpha + right * right_weight * right_alpha\n}\n\n// Normalize hue into [0, 360)\n#[inline]\nfn normalize_hue(v: f32) -> f32 {\n    v - 360. * (v / 360.).floor()\n}\n\nfn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolationMethod) {\n    // Adjust the hue angle as per\n    // https://drafts.csswg.org/css-color/#hue-interpolation.\n    //\n    // If both hue angles are NAN, they should be set to 0. Otherwise, if a\n    // single hue angle is NAN, it should use the other hue angle.\n    if left.is_nan() {\n        if right.is_nan() {\n            *left = 0.;\n            *right = 0.;\n        } else {\n            *left = *right;\n        }\n    } else if right.is_nan() {\n        *right = *left;\n    }\n\n    if hue_interpolation == HueInterpolationMethod::Specified {\n        // Angles are not adjusted. They are interpolated like any other\n        // component.\n        return;\n    }\n\n    *left = normalize_hue(*left);\n    *right = normalize_hue(*right);\n\n    match hue_interpolation {\n        // https://drafts.csswg.org/css-color/#shorter\n        HueInterpolationMethod::Shorter => {\n            let delta = *right - *left;\n\n            if delta > 180. {\n                *left += 360.;\n            } else if delta < -180. {\n                *right += 360.;\n            }\n        },\n        // https://drafts.csswg.org/css-color/#longer\n        HueInterpolationMethod::Longer => {\n            let delta = *right - *left;\n            if 0. < delta && delta < 180. {\n                *left += 360.;\n            } else if -180. < delta && delta <= 0. {\n                *right += 360.;\n            }\n        },\n        // https://drafts.csswg.org/css-color/#increasing\n        HueInterpolationMethod::Increasing => {\n            if *right < *left {\n                *right += 360.;\n            }\n        },\n        // https://drafts.csswg.org/css-color/#decreasing\n        HueInterpolationMethod::Decreasing => {\n            if *left < *right {\n                *left += 360.;\n            }\n        },\n        HueInterpolationMethod::Specified => unreachable!(\"Handled above\"),\n    }\n}\n\nfn interpolate_hue(\n    mut left: f32,\n    left_weight: f32,\n    mut right: f32,\n    right_weight: f32,\n    hue_interpolation: HueInterpolationMethod,\n) -> f32 {\n    adjust_hue(&mut left, &mut right, hue_interpolation);\n    left * left_weight + right * right_weight\n}\n\nstruct InterpolatedAlpha {\n    /// The adjusted left alpha value.\n    left: f32,\n    /// The adjusted right alpha value.\n    right: f32,\n    /// The interpolated alpha value.\n    interpolated: f32,\n    /// Whether the alpha component should be `none`.\n    is_none: bool,\n}\n\nfn interpolate_alpha(\n    left: f32,\n    left_weight: f32,\n    right: f32,\n    right_weight: f32,\n    outcome: ComponentMixOutcome,\n) -> InterpolatedAlpha {\n    // <https://drafts.csswg.org/css-color-4/#interpolation-missing>\n    let mut result = match outcome {\n        ComponentMixOutcome::Mix => {\n            let interpolated = left * left_weight + right * right_weight;\n            InterpolatedAlpha {\n                left,\n                right,\n                interpolated,\n                is_none: false,\n            }\n        },\n        ComponentMixOutcome::UseLeft => InterpolatedAlpha {\n            left,\n            right: left,\n            interpolated: left,\n            is_none: false,\n        },\n        ComponentMixOutcome::UseRight => InterpolatedAlpha {\n            left: right,\n            right,\n            interpolated: right,\n            is_none: false,\n        },\n        ComponentMixOutcome::None => InterpolatedAlpha {\n            left: 1.0,\n            right: 1.0,\n            interpolated: 0.0,\n            is_none: true,\n        },\n    };\n\n    // Clip all alpha values to [0.0..1.0].\n    result.left = result.left.clamp(0.0, 1.0);\n    result.right = result.right.clamp(0.0, 1.0);\n    result.interpolated = result.interpolated.clamp(0.0, 1.0);\n\n    result\n}\n\nfn interpolate_premultiplied(\n    left: &[f32; 4],\n    left_weight: f32,\n    right: &[f32; 4],\n    right_weight: f32,\n    hue_index: Option<usize>,\n    hue_interpolation: HueInterpolationMethod,\n    outcomes: &[ComponentMixOutcome; 4],\n) -> ([f32; 4], ColorFlags) {\n    let alpha = interpolate_alpha(left[3], left_weight, right[3], right_weight, outcomes[3]);\n    let mut flags = if alpha.is_none {\n        ColorFlags::ALPHA_IS_NONE\n    } else {\n        ColorFlags::empty()\n    };\n\n    let mut result = [0.; 4];\n\n    for i in 0..3 {\n        match outcomes[i] {\n            ComponentMixOutcome::Mix => {\n                let is_hue = hue_index == Some(i);\n                result[i] = if is_hue {\n                    normalize_hue(interpolate_hue(\n                        left[i],\n                        left_weight,\n                        right[i],\n                        right_weight,\n                        hue_interpolation,\n                    ))\n                } else {\n                    let interpolated = interpolate_premultiplied_component(\n                        left[i],\n                        left_weight,\n                        alpha.left,\n                        right[i],\n                        right_weight,\n                        alpha.right,\n                    );\n\n                    if alpha.interpolated == 0.0 {\n                        interpolated\n                    } else {\n                        interpolated / alpha.interpolated\n                    }\n                };\n            },\n            ComponentMixOutcome::UseLeft | ComponentMixOutcome::UseRight => {\n                let used_component = if outcomes[i] == ComponentMixOutcome::UseLeft {\n                    left[i]\n                } else {\n                    right[i]\n                };\n                result[i] = if hue_interpolation == HueInterpolationMethod::Longer\n                    && hue_index == Some(i)\n                {\n                    // If \"longer hue\" interpolation is required, we have to actually do\n                    // the computation even if we're using the same value at both ends,\n                    // so that interpolating from the starting hue back to the same value\n                    // produces a full cycle, rather than a constant hue.\n                    normalize_hue(interpolate_hue(\n                        used_component,\n                        left_weight,\n                        used_component,\n                        right_weight,\n                        hue_interpolation,\n                    ))\n                } else {\n                    used_component\n                };\n            },\n            ComponentMixOutcome::None => {\n                result[i] = 0.0;\n                match i {\n                    0 => flags.insert(ColorFlags::C0_IS_NONE),\n                    1 => flags.insert(ColorFlags::C1_IS_NONE),\n                    2 => flags.insert(ColorFlags::C2_IS_NONE),\n                    _ => unreachable!(),\n                }\n            },\n        }\n    }\n    result[3] = alpha.interpolated;\n\n    (result, flags)\n}\n"
  },
  {
    "path": "style/color/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Color support functions.\n\n/// cbindgen:ignore\npub mod convert;\n\nmod color_function;\npub mod component;\npub mod mix;\npub mod parsing;\nmod to_css;\n\nuse self::parsing::ChannelKeyword;\nuse crate::derives::*;\npub use color_function::*;\nuse component::ColorComponent;\nuse cssparser::color::PredefinedColorSpace;\n\n/// Number of color-mix items to reserve on the stack to avoid heap allocations.\npub const PRE_ALLOCATED_COLOR_MIX_ITEMS: usize = 3;\n\n/// Conveniece type to use for collecting color mix items.\npub type ColorMixItemList<T> = smallvec::SmallVec<[T; PRE_ALLOCATED_COLOR_MIX_ITEMS]>;\n\n/// The 3 components that make up a color.  (Does not include the alpha component)\n#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct ColorComponents(pub f32, pub f32, pub f32);\n\nimpl ColorComponents {\n    /// Apply a function to each of the 3 components of the color.\n    #[must_use]\n    pub fn map(self, f: impl Fn(f32) -> f32) -> Self {\n        Self(f(self.0), f(self.1), f(self.2))\n    }\n}\n\nimpl std::ops::Mul for ColorComponents {\n    type Output = Self;\n\n    fn mul(self, rhs: Self) -> Self::Output {\n        Self(self.0 * rhs.0, self.1 * rhs.1, self.2 * rhs.2)\n    }\n}\n\nimpl std::ops::Div for ColorComponents {\n    type Output = Self;\n\n    fn div(self, rhs: Self) -> Self::Output {\n        Self(self.0 / rhs.0, self.1 / rhs.1, self.2 / rhs.2)\n    }\n}\n\n/// A color space representation in the CSS specification.\n///\n/// https://drafts.csswg.org/css-color-4/#typedef-color-space\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(u8)]\npub enum ColorSpace {\n    /// A color specified in the sRGB color space with either the rgb/rgba(..)\n    /// functions or the newer color(srgb ..) function. If the color(..)\n    /// function is used, the AS_COLOR_FUNCTION flag will be set. Examples:\n    /// \"color(srgb 0.691 0.139 0.259)\", \"rgb(176, 35, 66)\"\n    Srgb = 0,\n    /// A color specified in the Hsl notation in the sRGB color space, e.g.\n    /// \"hsl(289.18 93.136% 65.531%)\"\n    /// https://drafts.csswg.org/css-color-4/#the-hsl-notation\n    Hsl,\n    /// A color specified in the Hwb notation in the sRGB color space, e.g.\n    /// \"hwb(740deg 20% 30%)\"\n    /// https://drafts.csswg.org/css-color-4/#the-hwb-notation\n    Hwb,\n    /// A color specified in the Lab color format, e.g.\n    /// \"lab(29.2345% 39.3825 20.0664)\".\n    /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors\n    Lab,\n    /// A color specified in the Lch color format, e.g.\n    /// \"lch(29.2345% 44.2 27)\".\n    /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors\n    Lch,\n    /// A color specified in the Oklab color format, e.g.\n    /// \"oklab(40.101% 0.1147 0.0453)\".\n    /// https://w3c.github.io/csswg-drafts/css-color-4/#lab-colors\n    Oklab,\n    /// A color specified in the Oklch color format, e.g.\n    /// \"oklch(40.101% 0.12332 21.555)\".\n    /// https://w3c.github.io/csswg-drafts/css-color-4/#lch-colors\n    Oklch,\n    /// A color specified with the color(..) function and the \"srgb-linear\"\n    /// color space, e.g. \"color(srgb-linear 0.435 0.017 0.055)\".\n    SrgbLinear,\n    /// A color specified with the color(..) function and the \"display-p3\"\n    /// color space, e.g. \"color(display-p3 0.84 0.19 0.72)\".\n    DisplayP3,\n    /// A color specified with the color(..) function and the \"display-p3-linear\"\n    /// color space.\n    DisplayP3Linear,\n    /// A color specified with the color(..) function and the \"a98-rgb\" color\n    /// space, e.g. \"color(a98-rgb 0.44091 0.49971 0.37408)\".\n    A98Rgb,\n    /// A color specified with the color(..) function and the \"prophoto-rgb\"\n    /// color space, e.g. \"color(prophoto-rgb 0.36589 0.41717 0.31333)\".\n    ProphotoRgb,\n    /// A color specified with the color(..) function and the \"rec2020\" color\n    /// space, e.g. \"color(rec2020 0.42210 0.47580 0.35605)\".\n    Rec2020,\n    /// A color specified with the color(..) function and the \"xyz-d50\" color\n    /// space, e.g. \"color(xyz-d50 0.2005 0.14089 0.4472)\".\n    XyzD50,\n    /// A color specified with the color(..) function and the \"xyz-d65\" or \"xyz\"\n    /// color space, e.g. \"color(xyz-d65 0.21661 0.14602 0.59452)\".\n    /// NOTE: https://drafts.csswg.org/css-color-4/#resolving-color-function-values\n    ///       specifies that `xyz` is an alias for the `xyz-d65` color space.\n    #[parse(aliases = \"xyz\")]\n    XyzD65,\n}\n\nimpl ColorSpace {\n    /// Returns whether this is a `<rectangular-color-space>`.\n    #[inline]\n    pub fn is_rectangular(&self) -> bool {\n        !self.is_polar()\n    }\n\n    /// Returns whether this is a `<polar-color-space>`.\n    #[inline]\n    pub fn is_polar(&self) -> bool {\n        matches!(self, Self::Hsl | Self::Hwb | Self::Lch | Self::Oklch)\n    }\n\n    /// Returns true if the color has RGB or XYZ components.\n    #[inline]\n    pub fn is_rgb_or_xyz_like(&self) -> bool {\n        match self {\n            Self::Srgb\n            | Self::SrgbLinear\n            | Self::DisplayP3\n            | Self::DisplayP3Linear\n            | Self::A98Rgb\n            | Self::ProphotoRgb\n            | Self::Rec2020\n            | Self::XyzD50\n            | Self::XyzD65 => true,\n            _ => false,\n        }\n    }\n\n    /// Returns an index of the hue component in the color space, otherwise\n    /// `None`.\n    #[inline]\n    pub fn hue_index(&self) -> Option<usize> {\n        match self {\n            Self::Hsl | Self::Hwb => Some(0),\n            Self::Lch | Self::Oklch => Some(2),\n\n            _ => {\n                debug_assert!(!self.is_polar());\n                None\n            },\n        }\n    }\n}\n\n/// Flags used when serializing colors.\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]\n#[cfg_attr(feature = \"serde\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct ColorFlags(u8);\nbitflags! {\n    impl ColorFlags : u8 {\n        /// Whether the 1st color component is `none`.\n        const C0_IS_NONE = 1 << 0;\n        /// Whether the 2nd color component is `none`.\n        const C1_IS_NONE = 1 << 1;\n        /// Whether the 3rd color component is `none`.\n        const C2_IS_NONE = 1 << 2;\n        /// Whether the alpha component is `none`.\n        const ALPHA_IS_NONE = 1 << 3;\n        /// Marks that this color is in the legacy color format. This flag is\n        /// only valid for the `Srgb` color space.\n        const IS_LEGACY_SRGB = 1 << 4;\n    }\n}\n\n/// An absolutely specified color, using either rgb(), rgba(), lab(), lch(),\n/// oklab(), oklch() or color().\n#[derive(Copy, Clone, Debug, MallocSizeOf, ToShmem, ToTyped)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct AbsoluteColor {\n    /// The 3 components that make up colors in any color space.\n    pub components: ColorComponents,\n    /// The alpha component of the color.\n    pub alpha: f32,\n    /// The current color space that the components represent.\n    pub color_space: ColorSpace,\n    /// Extra flags used during serialization of this color.\n    pub flags: ColorFlags,\n}\n\nimpl PartialEq for AbsoluteColor {\n    // See https://github.com/w3c/csswg-drafts/issues/13157#issuecomment-4165667681\n    fn eq(&self, other: &Self) -> bool {\n        let none_flags = ColorFlags::C0_IS_NONE\n            | ColorFlags::C1_IS_NONE\n            | ColorFlags::C2_IS_NONE\n            | ColorFlags::ALPHA_IS_NONE;\n        // If both colors have the same color-space, just compare components; note that\n        // any `none` components only match `none` in the other color.\n        if self.color_space == other.color_space {\n            return self.components == other.components\n                && self.alpha == other.alpha\n                && (self.flags & none_flags) == (other.flags & none_flags);\n        }\n        // Otherwise, if any `none` components are present in either color, return false.\n        if self.flags.union(other.flags).intersects(none_flags) {\n            return false;\n        }\n        // Otherwise, convert both colors to Oklab for comparison, and allow EPSILON\n        // difference in component values.\n        // TODO: check value of EPSILON once the spec is updated to cover this.\n        const EPSILON: f32 = 0.0001;\n        let a = self.to_color_space(ColorSpace::Oklab);\n        let b = other.to_color_space(ColorSpace::Oklab);\n        (a.components.0 - b.components.0).abs() <= EPSILON\n            && (a.components.1 - b.components.1).abs() <= EPSILON\n            && (a.components.2 - b.components.2).abs() <= EPSILON\n            && (a.alpha - b.alpha).abs() <= EPSILON\n    }\n}\n\n/// Given an [`AbsoluteColor`], return the 4 float components as the type given,\n/// e.g.:\n///\n/// ```rust\n/// let srgb = AbsoluteColor::new(ColorSpace::Srgb, 1.0, 0.0, 0.0, 0.0);\n/// let floats = color_components_as!(&srgb, [f32; 4]); // [1.0, 0.0, 0.0, 0.0]\n/// ```\nmacro_rules! color_components_as {\n    ($c:expr, $t:ty) => {{\n        // This macro is not an inline function, because we can't use the\n        // generic  type ($t) in a constant expression as per:\n        // https://github.com/rust-lang/rust/issues/76560\n        const_assert_eq!(std::mem::size_of::<$t>(), std::mem::size_of::<[f32; 4]>());\n        const_assert_eq!(std::mem::align_of::<$t>(), std::mem::align_of::<[f32; 4]>());\n        const_assert!(std::mem::size_of::<AbsoluteColor>() >= std::mem::size_of::<$t>());\n        const_assert_eq!(\n            std::mem::align_of::<AbsoluteColor>(),\n            std::mem::align_of::<$t>()\n        );\n\n        std::mem::transmute::<&ColorComponents, &$t>(&$c.components)\n    }};\n}\n\n/// Holds details about each component passed into creating a new [`AbsoluteColor`].\npub struct ComponentDetails {\n    value: f32,\n    is_none: bool,\n}\n\nimpl From<f32> for ComponentDetails {\n    fn from(value: f32) -> Self {\n        Self {\n            value,\n            is_none: false,\n        }\n    }\n}\n\nimpl From<u8> for ComponentDetails {\n    fn from(value: u8) -> Self {\n        Self {\n            value: value as f32 / 255.0,\n            is_none: false,\n        }\n    }\n}\n\nimpl From<Option<f32>> for ComponentDetails {\n    fn from(value: Option<f32>) -> Self {\n        if let Some(value) = value {\n            Self {\n                value,\n                is_none: false,\n            }\n        } else {\n            Self {\n                value: 0.0,\n                is_none: true,\n            }\n        }\n    }\n}\n\nimpl From<ColorComponent<f32>> for ComponentDetails {\n    fn from(value: ColorComponent<f32>) -> Self {\n        if let ColorComponent::Value(value) = value {\n            Self {\n                value,\n                is_none: false,\n            }\n        } else {\n            Self {\n                value: 0.0,\n                is_none: true,\n            }\n        }\n    }\n}\n\nimpl AbsoluteColor {\n    /// A fully transparent color in the legacy syntax.\n    pub const TRANSPARENT_BLACK: Self = Self {\n        components: ColorComponents(0.0, 0.0, 0.0),\n        alpha: 0.0,\n        color_space: ColorSpace::Srgb,\n        flags: ColorFlags::IS_LEGACY_SRGB,\n    };\n\n    /// An opaque black color in the legacy syntax.\n    pub const BLACK: Self = Self {\n        components: ColorComponents(0.0, 0.0, 0.0),\n        alpha: 1.0,\n        color_space: ColorSpace::Srgb,\n        flags: ColorFlags::IS_LEGACY_SRGB,\n    };\n\n    /// An opaque white color in the legacy syntax.\n    pub const WHITE: Self = Self {\n        components: ColorComponents(1.0, 1.0, 1.0),\n        alpha: 1.0,\n        color_space: ColorSpace::Srgb,\n        flags: ColorFlags::IS_LEGACY_SRGB,\n    };\n\n    /// Create a new [`AbsoluteColor`] with the given [`ColorSpace`] and\n    /// components.\n    pub fn new(\n        color_space: ColorSpace,\n        c1: impl Into<ComponentDetails>,\n        c2: impl Into<ComponentDetails>,\n        c3: impl Into<ComponentDetails>,\n        alpha: impl Into<ComponentDetails>,\n    ) -> Self {\n        let mut flags = ColorFlags::empty();\n\n        macro_rules! cd {\n            ($c:expr,$flag:expr) => {{\n                let component_details = $c.into();\n                if component_details.is_none {\n                    flags |= $flag;\n                }\n                component_details.value\n            }};\n        }\n\n        let mut components = ColorComponents(\n            cd!(c1, ColorFlags::C0_IS_NONE),\n            cd!(c2, ColorFlags::C1_IS_NONE),\n            cd!(c3, ColorFlags::C2_IS_NONE),\n        );\n\n        let alpha = cd!(alpha, ColorFlags::ALPHA_IS_NONE);\n\n        // Lightness for Lab and Lch is clamped to [0..100].\n        if matches!(color_space, ColorSpace::Lab | ColorSpace::Lch) {\n            components.0 = components.0.clamp(0.0, 100.0);\n        }\n\n        // Lightness for Oklab and Oklch is clamped to [0..1].\n        if matches!(color_space, ColorSpace::Oklab | ColorSpace::Oklch) {\n            components.0 = components.0.clamp(0.0, 1.0);\n        }\n\n        // Chroma must not be less than 0.\n        if matches!(color_space, ColorSpace::Lch | ColorSpace::Oklch) {\n            components.1 = components.1.max(0.0);\n        }\n\n        // Alpha is always clamped to [0..1].\n        let alpha = alpha.clamp(0.0, 1.0);\n\n        Self {\n            components,\n            alpha,\n            color_space,\n            flags,\n        }\n    }\n\n    /// Convert this color into the sRGB color space and set it to the legacy\n    /// syntax.\n    #[inline]\n    #[must_use]\n    pub fn into_srgb_legacy(self) -> Self {\n        let mut result = if !matches!(self.color_space, ColorSpace::Srgb) {\n            self.to_color_space(ColorSpace::Srgb)\n        } else {\n            self\n        };\n\n        // Explicitly set the flags to IS_LEGACY_SRGB only to clear out the\n        // *_IS_NONE flags, because the legacy syntax doesn't allow \"none\".\n        result.flags = ColorFlags::IS_LEGACY_SRGB;\n\n        result\n    }\n\n    /// Create a new [`AbsoluteColor`] from rgba legacy syntax values in the sRGB color space.\n    pub fn srgb_legacy(red: u8, green: u8, blue: u8, alpha: f32) -> Self {\n        let mut result = Self::new(ColorSpace::Srgb, red, green, blue, alpha);\n        result.flags = ColorFlags::IS_LEGACY_SRGB;\n        result\n    }\n\n    /// Return all the components of the color in an array.  (Includes alpha)\n    #[inline]\n    pub fn raw_components(&self) -> &[f32; 4] {\n        unsafe { color_components_as!(self, [f32; 4]) }\n    }\n\n    /// Returns true if this color is in the legacy color syntax.\n    #[inline]\n    pub fn is_legacy_syntax(&self) -> bool {\n        // rgb(), rgba(), hsl(), hsla(), hwb(), hwba()\n        match self.color_space {\n            ColorSpace::Srgb => self.flags.contains(ColorFlags::IS_LEGACY_SRGB),\n            ColorSpace::Hsl | ColorSpace::Hwb => true,\n            _ => false,\n        }\n    }\n\n    /// Returns true if this color is fully transparent.\n    #[inline]\n    pub fn is_transparent(&self) -> bool {\n        self.flags.contains(ColorFlags::ALPHA_IS_NONE) || self.alpha == 0.0\n    }\n\n    /// Return an optional first component.\n    #[inline]\n    pub fn c0(&self) -> Option<f32> {\n        if self.flags.contains(ColorFlags::C0_IS_NONE) {\n            None\n        } else {\n            Some(self.components.0)\n        }\n    }\n\n    /// Return an optional second component.\n    #[inline]\n    pub fn c1(&self) -> Option<f32> {\n        if self.flags.contains(ColorFlags::C1_IS_NONE) {\n            None\n        } else {\n            Some(self.components.1)\n        }\n    }\n\n    /// Return an optional second component.\n    #[inline]\n    pub fn c2(&self) -> Option<f32> {\n        if self.flags.contains(ColorFlags::C2_IS_NONE) {\n            None\n        } else {\n            Some(self.components.2)\n        }\n    }\n\n    /// Return an optional alpha component.\n    #[inline]\n    pub fn alpha(&self) -> Option<f32> {\n        if self.flags.contains(ColorFlags::ALPHA_IS_NONE) {\n            None\n        } else {\n            Some(self.alpha)\n        }\n    }\n\n    /// Return the value of a component by its channel keyword.\n    pub fn get_component_by_channel_keyword(\n        &self,\n        channel_keyword: ChannelKeyword,\n    ) -> Result<Option<f32>, ()> {\n        if channel_keyword == ChannelKeyword::Alpha {\n            return Ok(self.alpha());\n        }\n\n        Ok(match self.color_space {\n            ColorSpace::Srgb => {\n                if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) {\n                    match channel_keyword {\n                        ChannelKeyword::R => self.c0().map(|v| v * 255.0),\n                        ChannelKeyword::G => self.c1().map(|v| v * 255.0),\n                        ChannelKeyword::B => self.c2().map(|v| v * 255.0),\n                        _ => return Err(()),\n                    }\n                } else {\n                    match channel_keyword {\n                        ChannelKeyword::R => self.c0(),\n                        ChannelKeyword::G => self.c1(),\n                        ChannelKeyword::B => self.c2(),\n                        _ => return Err(()),\n                    }\n                }\n            },\n            ColorSpace::Hsl => match channel_keyword {\n                ChannelKeyword::H => self.c0(),\n                ChannelKeyword::S => self.c1(),\n                ChannelKeyword::L => self.c2(),\n                _ => return Err(()),\n            },\n            ColorSpace::Hwb => match channel_keyword {\n                ChannelKeyword::H => self.c0(),\n                ChannelKeyword::W => self.c1(),\n                ChannelKeyword::B => self.c2(),\n                _ => return Err(()),\n            },\n            ColorSpace::Lab | ColorSpace::Oklab => match channel_keyword {\n                ChannelKeyword::L => self.c0(),\n                ChannelKeyword::A => self.c1(),\n                ChannelKeyword::B => self.c2(),\n                _ => return Err(()),\n            },\n            ColorSpace::Lch | ColorSpace::Oklch => match channel_keyword {\n                ChannelKeyword::L => self.c0(),\n                ChannelKeyword::C => self.c1(),\n                ChannelKeyword::H => self.c2(),\n                _ => return Err(()),\n            },\n            ColorSpace::SrgbLinear\n            | ColorSpace::DisplayP3\n            | ColorSpace::DisplayP3Linear\n            | ColorSpace::A98Rgb\n            | ColorSpace::ProphotoRgb\n            | ColorSpace::Rec2020 => match channel_keyword {\n                ChannelKeyword::R => self.c0(),\n                ChannelKeyword::G => self.c1(),\n                ChannelKeyword::B => self.c2(),\n                _ => return Err(()),\n            },\n            ColorSpace::XyzD50 | ColorSpace::XyzD65 => match channel_keyword {\n                ChannelKeyword::X => self.c0(),\n                ChannelKeyword::Y => self.c1(),\n                ChannelKeyword::Z => self.c2(),\n                _ => return Err(()),\n            },\n        })\n    }\n\n    /// Convert this color to the specified color space.\n    pub fn to_color_space(&self, color_space: ColorSpace) -> Self {\n        use ColorSpace::*;\n\n        if self.color_space == color_space {\n            return self.clone();\n        }\n\n        // Conversion functions doesn't handle NAN component values, so they are\n        // converted to 0.0. They do however need to know if a component is\n        // missing, so we use NAN as the marker for that.\n        macro_rules! missing_to_nan {\n            ($c:expr) => {{\n                if let Some(v) = $c {\n                    crate::values::normalize(v)\n                } else {\n                    f32::NAN\n                }\n            }};\n        }\n\n        let components = ColorComponents(\n            missing_to_nan!(self.c0()),\n            missing_to_nan!(self.c1()),\n            missing_to_nan!(self.c2()),\n        );\n\n        let result = match (self.color_space, color_space) {\n            // We have simplified conversions that do not need to convert to XYZ\n            // first. This improves performance, because it skips at least 2\n            // matrix multiplications and reduces float rounding errors.\n            (Srgb, Hsl) => convert::rgb_to_hsl(&components),\n            (Srgb, Hwb) => convert::rgb_to_hwb(&components),\n            (Hsl, Srgb) => convert::hsl_to_rgb(&components),\n            (Hwb, Srgb) => convert::hwb_to_rgb(&components),\n            (Lab, Lch) | (Oklab, Oklch) => convert::orthogonal_to_polar(\n                &components,\n                convert::epsilon_for_range(0.0, if color_space == Lch { 100.0 } else { 1.0 }),\n            ),\n            (Lch, Lab) | (Oklch, Oklab) => convert::polar_to_orthogonal(&components),\n\n            // All other conversions need to convert to XYZ first.\n            _ => {\n                let (xyz, white_point) = match self.color_space {\n                    Lab => convert::to_xyz::<convert::Lab>(&components),\n                    Lch => convert::to_xyz::<convert::Lch>(&components),\n                    Oklab => convert::to_xyz::<convert::Oklab>(&components),\n                    Oklch => convert::to_xyz::<convert::Oklch>(&components),\n                    Srgb => convert::to_xyz::<convert::Srgb>(&components),\n                    Hsl => convert::to_xyz::<convert::Hsl>(&components),\n                    Hwb => convert::to_xyz::<convert::Hwb>(&components),\n                    SrgbLinear => convert::to_xyz::<convert::SrgbLinear>(&components),\n                    DisplayP3 => convert::to_xyz::<convert::DisplayP3>(&components),\n                    DisplayP3Linear => convert::to_xyz::<convert::DisplayP3Linear>(&components),\n                    A98Rgb => convert::to_xyz::<convert::A98Rgb>(&components),\n                    ProphotoRgb => convert::to_xyz::<convert::ProphotoRgb>(&components),\n                    Rec2020 => convert::to_xyz::<convert::Rec2020>(&components),\n                    XyzD50 => convert::to_xyz::<convert::XyzD50>(&components),\n                    XyzD65 => convert::to_xyz::<convert::XyzD65>(&components),\n                };\n\n                match color_space {\n                    Lab => convert::from_xyz::<convert::Lab>(&xyz, white_point),\n                    Lch => convert::from_xyz::<convert::Lch>(&xyz, white_point),\n                    Oklab => convert::from_xyz::<convert::Oklab>(&xyz, white_point),\n                    Oklch => convert::from_xyz::<convert::Oklch>(&xyz, white_point),\n                    Srgb => convert::from_xyz::<convert::Srgb>(&xyz, white_point),\n                    Hsl => convert::from_xyz::<convert::Hsl>(&xyz, white_point),\n                    Hwb => convert::from_xyz::<convert::Hwb>(&xyz, white_point),\n                    SrgbLinear => convert::from_xyz::<convert::SrgbLinear>(&xyz, white_point),\n                    DisplayP3 => convert::from_xyz::<convert::DisplayP3>(&xyz, white_point),\n                    DisplayP3Linear => {\n                        convert::from_xyz::<convert::DisplayP3Linear>(&xyz, white_point)\n                    },\n                    A98Rgb => convert::from_xyz::<convert::A98Rgb>(&xyz, white_point),\n                    ProphotoRgb => convert::from_xyz::<convert::ProphotoRgb>(&xyz, white_point),\n                    Rec2020 => convert::from_xyz::<convert::Rec2020>(&xyz, white_point),\n                    XyzD50 => convert::from_xyz::<convert::XyzD50>(&xyz, white_point),\n                    XyzD65 => convert::from_xyz::<convert::XyzD65>(&xyz, white_point),\n                }\n            },\n        };\n\n        // A NAN value coming from a conversion function means the the component\n        // is missing, so we convert it to None.\n        macro_rules! nan_to_missing {\n            ($v:expr) => {{\n                if $v.is_nan() {\n                    None\n                } else {\n                    Some($v)\n                }\n            }};\n        }\n\n        Self::new(\n            color_space,\n            nan_to_missing!(result.0),\n            nan_to_missing!(result.1),\n            nan_to_missing!(result.2),\n            self.alpha(),\n        )\n    }\n\n    /// Convert a color value to `nscolor`.\n    pub fn to_nscolor(&self) -> u32 {\n        let srgb = self.to_color_space(ColorSpace::Srgb);\n        u32::from_le_bytes([\n            (srgb.components.0 * 255.0).round() as u8,\n            (srgb.components.1 * 255.0).round() as u8,\n            (srgb.components.2 * 255.0).round() as u8,\n            (srgb.alpha * 255.0).round() as u8,\n        ])\n    }\n\n    /// Convert a given `nscolor` to a Servo AbsoluteColor value.\n    pub fn from_nscolor(color: u32) -> Self {\n        let [r, g, b, a] = color.to_le_bytes();\n        Self::srgb_legacy(r, g, b, a as f32 / 255.0)\n    }\n}\n\n#[test]\nfn from_nscolor_should_be_in_legacy_syntax() {\n    let result = AbsoluteColor::from_nscolor(0x336699CC);\n    assert!(result.flags.contains(ColorFlags::IS_LEGACY_SRGB));\n    assert!(result.is_legacy_syntax());\n}\n\nimpl From<PredefinedColorSpace> for ColorSpace {\n    fn from(value: PredefinedColorSpace) -> Self {\n        match value {\n            PredefinedColorSpace::Srgb => ColorSpace::Srgb,\n            PredefinedColorSpace::SrgbLinear => ColorSpace::SrgbLinear,\n            PredefinedColorSpace::DisplayP3 => ColorSpace::DisplayP3,\n            PredefinedColorSpace::DisplayP3Linear => ColorSpace::DisplayP3Linear,\n            PredefinedColorSpace::A98Rgb => ColorSpace::A98Rgb,\n            PredefinedColorSpace::ProphotoRgb => ColorSpace::ProphotoRgb,\n            PredefinedColorSpace::Rec2020 => ColorSpace::Rec2020,\n            PredefinedColorSpace::XyzD50 => ColorSpace::XyzD50,\n            PredefinedColorSpace::XyzD65 => ColorSpace::XyzD65,\n        }\n    }\n}\n"
  },
  {
    "path": "style/color/parsing.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n\n#![deny(missing_docs)]\n\n//! Parsing for CSS colors.\n\nuse super::{\n    color_function::ColorFunction,\n    component::{ColorComponent, ColorComponentType},\n    AbsoluteColor,\n};\nuse crate::derives::*;\nuse crate::{\n    parser::{Parse, ParserContext},\n    values::{\n        generics::{calc::CalcUnits, Optional},\n        specified::{angle::Angle as SpecifiedAngle, calc::Leaf, color::Color as SpecifiedColor},\n    },\n};\nuse cssparser::{\n    color::{parse_hash_color, PredefinedColorSpace, OPAQUE},\n    match_ignore_ascii_case, CowRcStr, Parser, Token,\n};\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// Returns true if the relative color syntax pref is enabled.\n#[inline]\npub fn rcs_enabled() -> bool {\n    static_prefs::pref!(\"layout.css.relative-color-syntax.enabled\")\n}\n\n/// Represents a channel keyword inside a color.\n#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, PartialOrd, ToCss, ToShmem)]\n#[repr(u8)]\npub enum ChannelKeyword {\n    /// alpha\n    Alpha,\n    /// a\n    A,\n    /// b, blackness, blue\n    B,\n    /// chroma\n    C,\n    /// green\n    G,\n    /// hue\n    H,\n    /// lightness\n    L,\n    /// red\n    R,\n    /// saturation\n    S,\n    /// whiteness\n    W,\n    /// x\n    X,\n    /// y\n    Y,\n    /// z\n    Z,\n}\n\n/// Return the named color with the given name.\n///\n/// Matching is case-insensitive in the ASCII range.\n/// CSS escaping (if relevant) should be resolved before calling this function.\n/// (For example, the value of an `Ident` token is fine.)\n#[inline]\npub fn parse_color_keyword(ident: &str) -> Result<SpecifiedColor, ()> {\n    Ok(match_ignore_ascii_case! { ident,\n        \"transparent\" => {\n            SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(0u8, 0u8, 0u8, 0.0))\n        },\n        \"currentcolor\" => SpecifiedColor::CurrentColor,\n        _ => {\n            let (r, g, b) = cssparser::color::parse_named_color(ident)?;\n            SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, OPAQUE))\n        },\n    })\n}\n\n/// Parse a CSS color using the specified [`ColorParser`] and return a new color\n/// value on success.\npub fn parse_color_with<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<SpecifiedColor, ParseError<'i>> {\n    let location = input.current_source_location();\n    let token = input.next()?;\n    match *token {\n        Token::Hash(ref value) | Token::IDHash(ref value) => parse_hash_color(value.as_bytes())\n            .map(|(r, g, b, a)| {\n                SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a))\n            }),\n        Token::Ident(ref value) => parse_color_keyword(value),\n        Token::Function(ref name) => {\n            let name = name.clone();\n            return input.parse_nested_block(|arguments| {\n                let color_function = parse_color_function(context, name, arguments)?;\n\n                if color_function.has_origin_color() {\n                    // Preserve the color as it was parsed.\n                    Ok(SpecifiedColor::ColorFunction(Box::new(color_function)))\n                } else if let Ok(resolved) = color_function.resolve_to_absolute() {\n                    Ok(SpecifiedColor::from_absolute_color(resolved))\n                } else {\n                    // This will only happen when the parsed color contains errors like calc units\n                    // that cannot be resolved at parse time, but will fail when trying to resolve\n                    // them, etc. This should be rare, but for now just failing the color value\n                    // makes sense.\n                    Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n                }\n            });\n        },\n        _ => Err(()),\n    }\n    .map_err(|()| location.new_unexpected_token_error(token.clone()))\n}\n\n/// Parse one of the color functions: rgba(), lab(), color(), etc.\n#[inline]\nfn parse_color_function<'i, 't>(\n    context: &ParserContext,\n    name: CowRcStr<'i>,\n    arguments: &mut Parser<'i, 't>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let origin_color = parse_origin_color(context, arguments)?;\n    let has_origin_color = origin_color.is_some();\n\n    let color = match_ignore_ascii_case! { &name,\n        \"rgb\" | \"rgba\" => parse_rgb(context, arguments, origin_color),\n        \"hsl\" | \"hsla\" => parse_hsl(context, arguments, origin_color),\n        \"hwb\" => parse_hwb(context, arguments, origin_color),\n        \"lab\" => parse_lab_like(context, arguments, origin_color, ColorFunction::Lab),\n        \"lch\" => parse_lch_like(context, arguments, origin_color, ColorFunction::Lch),\n        \"oklab\" => parse_lab_like(context, arguments, origin_color, ColorFunction::Oklab),\n        \"oklch\" => parse_lch_like(context, arguments, origin_color, ColorFunction::Oklch),\n        \"color\" => parse_color_with_color_space(context, arguments, origin_color),\n        _ => return Err(arguments.new_unexpected_token_error(Token::Ident(name))),\n    }?;\n\n    if has_origin_color {\n        // Validate the channels and calc expressions by trying to resolve them against\n        // transparent.\n        // FIXME(emilio, bug 1925572): This could avoid cloning, or be done earlier.\n        let abs = color\n            .map_origin_color(|_| Ok(AbsoluteColor::TRANSPARENT_BLACK))\n            .unwrap();\n        if abs.resolve_to_absolute().is_err() {\n            return Err(arguments.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n    }\n\n    arguments.expect_exhausted()?;\n\n    Ok(color)\n}\n\n/// Parse the relative color syntax \"from\" syntax `from <color>`.\nfn parse_origin_color<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n) -> Result<Option<SpecifiedColor>, ParseError<'i>> {\n    if !rcs_enabled() {\n        return Ok(None);\n    }\n\n    // Not finding the from keyword is not an error, it just means we don't\n    // have an origin color.\n    if arguments\n        .try_parse(|p| p.expect_ident_matching(\"from\"))\n        .is_err()\n    {\n        return Ok(None);\n    }\n\n    SpecifiedColor::parse(context, arguments).map(Option::Some)\n}\n\n#[inline]\nfn parse_rgb<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let maybe_red = parse_number_or_percentage(context, arguments, true)?;\n\n    // If the first component is not \"none\" and is followed by a comma, then we\n    // are parsing the legacy syntax.  Legacy syntax also doesn't support an\n    // origin color.\n    let is_legacy_syntax = origin_color.is_none()\n        && !maybe_red.is_none()\n        && arguments.try_parse(|p| p.expect_comma()).is_ok();\n\n    Ok(if is_legacy_syntax {\n        let (green, blue) = if maybe_red.could_be_percentage() {\n            let green = parse_percentage(context, arguments, false)?;\n            arguments.expect_comma()?;\n            let blue = parse_percentage(context, arguments, false)?;\n            (green, blue)\n        } else {\n            let green = parse_number(context, arguments, false)?;\n            arguments.expect_comma()?;\n            let blue = parse_number(context, arguments, false)?;\n            (green, blue)\n        };\n\n        let alpha = parse_legacy_alpha(context, arguments)?;\n\n        ColorFunction::Rgb(origin_color.into(), maybe_red, green, blue, alpha)\n    } else {\n        let green = parse_number_or_percentage(context, arguments, true)?;\n        let blue = parse_number_or_percentage(context, arguments, true)?;\n\n        let alpha = parse_modern_alpha(context, arguments)?;\n\n        ColorFunction::Rgb(origin_color.into(), maybe_red, green, blue, alpha)\n    })\n}\n\n/// Parses hsl syntax.\n///\n/// <https://drafts.csswg.org/css-color/#the-hsl-notation>\n#[inline]\nfn parse_hsl<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let hue = parse_number_or_angle(context, arguments, true)?;\n\n    // If the hue is not \"none\" and is followed by a comma, then we are parsing\n    // the legacy syntax. Legacy syntax also doesn't support an origin color.\n    let is_legacy_syntax = origin_color.is_none()\n        && !hue.is_none()\n        && arguments.try_parse(|p| p.expect_comma()).is_ok();\n\n    let (saturation, lightness, alpha) = if is_legacy_syntax {\n        let saturation = parse_percentage(context, arguments, false)?;\n        arguments.expect_comma()?;\n        let lightness = parse_percentage(context, arguments, false)?;\n        let alpha = parse_legacy_alpha(context, arguments)?;\n        (saturation, lightness, alpha)\n    } else {\n        let saturation = parse_number_or_percentage(context, arguments, true)?;\n        let lightness = parse_number_or_percentage(context, arguments, true)?;\n        let alpha = parse_modern_alpha(context, arguments)?;\n        (saturation, lightness, alpha)\n    };\n\n    Ok(ColorFunction::Hsl(\n        origin_color.into(),\n        hue,\n        saturation,\n        lightness,\n        alpha,\n    ))\n}\n\n/// Parses hwb syntax.\n///\n/// <https://drafts.csswg.org/css-color/#the-hbw-notation>\n#[inline]\nfn parse_hwb<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let hue = parse_number_or_angle(context, arguments, true)?;\n    let whiteness = parse_number_or_percentage(context, arguments, true)?;\n    let blackness = parse_number_or_percentage(context, arguments, true)?;\n\n    let alpha = parse_modern_alpha(context, arguments)?;\n\n    Ok(ColorFunction::Hwb(\n        origin_color.into(),\n        hue,\n        whiteness,\n        blackness,\n        alpha,\n    ))\n}\n\ntype IntoLabFn<Output> = fn(\n    origin: Optional<SpecifiedColor>,\n    l: ColorComponent<NumberOrPercentageComponent>,\n    a: ColorComponent<NumberOrPercentageComponent>,\n    b: ColorComponent<NumberOrPercentageComponent>,\n    alpha: ColorComponent<NumberOrPercentageComponent>,\n) -> Output;\n\n#[inline]\nfn parse_lab_like<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n    into_color: IntoLabFn<ColorFunction<SpecifiedColor>>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let lightness = parse_number_or_percentage(context, arguments, true)?;\n    let a = parse_number_or_percentage(context, arguments, true)?;\n    let b = parse_number_or_percentage(context, arguments, true)?;\n\n    let alpha = parse_modern_alpha(context, arguments)?;\n\n    Ok(into_color(origin_color.into(), lightness, a, b, alpha))\n}\n\ntype IntoLchFn<Output> = fn(\n    origin: Optional<SpecifiedColor>,\n    l: ColorComponent<NumberOrPercentageComponent>,\n    a: ColorComponent<NumberOrPercentageComponent>,\n    b: ColorComponent<NumberOrAngleComponent>,\n    alpha: ColorComponent<NumberOrPercentageComponent>,\n) -> Output;\n\n#[inline]\nfn parse_lch_like<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n    into_color: IntoLchFn<ColorFunction<SpecifiedColor>>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let lightness = parse_number_or_percentage(context, arguments, true)?;\n    let chroma = parse_number_or_percentage(context, arguments, true)?;\n    let hue = parse_number_or_angle(context, arguments, true)?;\n\n    let alpha = parse_modern_alpha(context, arguments)?;\n\n    Ok(into_color(\n        origin_color.into(),\n        lightness,\n        chroma,\n        hue,\n        alpha,\n    ))\n}\n\n/// Parse the color() function.\n#[inline]\nfn parse_color_with_color_space<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n    origin_color: Option<SpecifiedColor>,\n) -> Result<ColorFunction<SpecifiedColor>, ParseError<'i>> {\n    let color_space = PredefinedColorSpace::parse(arguments)?;\n\n    let c1 = parse_number_or_percentage(context, arguments, true)?;\n    let c2 = parse_number_or_percentage(context, arguments, true)?;\n    let c3 = parse_number_or_percentage(context, arguments, true)?;\n\n    let alpha = parse_modern_alpha(context, arguments)?;\n\n    Ok(ColorFunction::Color(\n        origin_color.into(),\n        c1,\n        c2,\n        c3,\n        alpha,\n        color_space.into(),\n    ))\n}\n\n/// Either a percentage or a number.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]\n#[repr(u8)]\npub enum NumberOrPercentageComponent {\n    /// `<number>`.\n    Number(f32),\n    /// `<percentage>`\n    /// The value as a float, divided by 100 so that the nominal range is 0.0 to 1.0.\n    Percentage(f32),\n}\n\nimpl NumberOrPercentageComponent {\n    /// Return the value as a number. Percentages will be adjusted to the range\n    /// [0..percent_basis].\n    pub fn to_number(&self, percentage_basis: f32) -> f32 {\n        match *self {\n            Self::Number(value) => value,\n            Self::Percentage(unit_value) => unit_value * percentage_basis,\n        }\n    }\n}\n\nimpl ColorComponentType for NumberOrPercentageComponent {\n    fn from_value(value: f32) -> Self {\n        Self::Number(value)\n    }\n\n    fn units() -> CalcUnits {\n        CalcUnits::PERCENTAGE\n    }\n\n    fn try_from_token(token: &Token) -> Result<Self, ()> {\n        Ok(match *token {\n            Token::Number { value, .. } => Self::Number(value),\n            Token::Percentage { unit_value, .. } => Self::Percentage(unit_value),\n            _ => {\n                return Err(());\n            },\n        })\n    }\n\n    fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()> {\n        Ok(match *leaf {\n            Leaf::Percentage(unit_value) => Self::Percentage(unit_value),\n            Leaf::Number(value) => Self::Number(value),\n            _ => return Err(()),\n        })\n    }\n}\n\n/// Either an angle or a number.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]\n#[repr(u8)]\npub enum NumberOrAngleComponent {\n    /// `<number>`.\n    Number(f32),\n    /// `<angle>`\n    /// The value as a number of degrees.\n    Angle(f32),\n}\n\nimpl NumberOrAngleComponent {\n    /// Return the angle in degrees. `NumberOrAngle::Number` is returned as\n    /// degrees, because it is the canonical unit.\n    pub fn degrees(&self) -> f32 {\n        match *self {\n            Self::Number(value) => value,\n            Self::Angle(degrees) => degrees,\n        }\n    }\n}\n\nimpl ColorComponentType for NumberOrAngleComponent {\n    fn from_value(value: f32) -> Self {\n        Self::Number(value)\n    }\n\n    fn units() -> CalcUnits {\n        CalcUnits::ANGLE\n    }\n\n    fn try_from_token(token: &Token) -> Result<Self, ()> {\n        Ok(match *token {\n            Token::Number { value, .. } => Self::Number(value),\n            Token::Dimension {\n                value, ref unit, ..\n            } => {\n                let degrees =\n                    SpecifiedAngle::parse_dimension(value, unit, /* from_calc = */ false)\n                        .map(|angle| angle.degrees())?;\n\n                NumberOrAngleComponent::Angle(degrees)\n            },\n            _ => {\n                return Err(());\n            },\n        })\n    }\n\n    fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()> {\n        Ok(match *leaf {\n            Leaf::Angle(angle) => Self::Angle(angle.degrees()),\n            Leaf::Number(value) => Self::Number(value),\n            _ => return Err(()),\n        })\n    }\n}\n\n/// The raw f32 here is for <number>.\nimpl ColorComponentType for f32 {\n    fn from_value(value: f32) -> Self {\n        value\n    }\n\n    fn units() -> CalcUnits {\n        CalcUnits::empty()\n    }\n\n    fn try_from_token(token: &Token) -> Result<Self, ()> {\n        if let Token::Number { value, .. } = *token {\n            Ok(value)\n        } else {\n            Err(())\n        }\n    }\n\n    fn try_from_leaf(leaf: &Leaf) -> Result<Self, ()> {\n        if let Leaf::Number(value) = *leaf {\n            Ok(value)\n        } else {\n            Err(())\n        }\n    }\n}\n\n/// Parse an `<number>` or `<angle>` value.\nfn parse_number_or_angle<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    allow_none: bool,\n) -> Result<ColorComponent<NumberOrAngleComponent>, ParseError<'i>> {\n    ColorComponent::parse(context, input, allow_none)\n}\n\n/// Parse a `<percentage>` value.\nfn parse_percentage<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    allow_none: bool,\n) -> Result<ColorComponent<NumberOrPercentageComponent>, ParseError<'i>> {\n    let location = input.current_source_location();\n\n    let value = ColorComponent::<NumberOrPercentageComponent>::parse(context, input, allow_none)?;\n    if !value.could_be_percentage() {\n        return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n    }\n\n    Ok(value)\n}\n\n/// Parse a `<number>` value.\nfn parse_number<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    allow_none: bool,\n) -> Result<ColorComponent<NumberOrPercentageComponent>, ParseError<'i>> {\n    let location = input.current_source_location();\n\n    let value = ColorComponent::<NumberOrPercentageComponent>::parse(context, input, allow_none)?;\n\n    if !value.could_be_number() {\n        return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n    }\n\n    Ok(value)\n}\n\n/// Parse a `<number>` or `<percentage>` value.\nfn parse_number_or_percentage<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    allow_none: bool,\n) -> Result<ColorComponent<NumberOrPercentageComponent>, ParseError<'i>> {\n    ColorComponent::parse(context, input, allow_none)\n}\n\nfn parse_legacy_alpha<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n) -> Result<ColorComponent<NumberOrPercentageComponent>, ParseError<'i>> {\n    if !arguments.is_exhausted() {\n        arguments.expect_comma()?;\n        parse_number_or_percentage(context, arguments, false)\n    } else {\n        Ok(ColorComponent::AlphaOmitted)\n    }\n}\n\nfn parse_modern_alpha<'i, 't>(\n    context: &ParserContext,\n    arguments: &mut Parser<'i, 't>,\n) -> Result<ColorComponent<NumberOrPercentageComponent>, ParseError<'i>> {\n    if !arguments.is_exhausted() {\n        arguments.expect_delim('/')?;\n        parse_number_or_percentage(context, arguments, true)\n    } else {\n        Ok(ColorComponent::AlphaOmitted)\n    }\n}\n\nimpl ColorComponent<NumberOrPercentageComponent> {\n    /// Return true if the value contained inside is/can resolve to a number.\n    /// Also returns false if the node is invalid somehow.\n    fn could_be_number(&self) -> bool {\n        match self {\n            Self::None | Self::AlphaOmitted => true,\n            Self::Value(value) => matches!(value, NumberOrPercentageComponent::Number { .. }),\n            Self::ChannelKeyword(_) => {\n                // Channel keywords always resolve to numbers.\n                true\n            },\n            Self::Calc(node) => {\n                if let Ok(unit) = node.unit() {\n                    unit.is_empty()\n                } else {\n                    false\n                }\n            },\n        }\n    }\n\n    /// Return true if the value contained inside is/can resolve to a percentage.\n    /// Also returns false if the node is invalid somehow.\n    fn could_be_percentage(&self) -> bool {\n        match self {\n            Self::None | Self::AlphaOmitted => true,\n            Self::Value(value) => matches!(value, NumberOrPercentageComponent::Percentage { .. }),\n            Self::ChannelKeyword(_) => {\n                // Channel keywords always resolve to numbers.\n                false\n            },\n            Self::Calc(node) => {\n                if let Ok(unit) = node.unit() {\n                    unit == CalcUnits::PERCENTAGE\n                } else {\n                    false\n                }\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "style/color/to_css.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Write colors into CSS strings.\n\nuse super::{\n    parsing::{NumberOrAngleComponent, NumberOrPercentageComponent},\n    AbsoluteColor, ColorFlags, ColorSpace,\n};\nuse crate::values::normalize;\nuse cssparser::color::{clamp_unit_f32, serialize_color_alpha, OPAQUE};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A [`ModernComponent`] can serialize to `none`, `nan`, `infinity` and\n/// floating point values.\nstruct ModernComponent<'a>(&'a Option<f32>);\n\nimpl<'a> ToCss for ModernComponent<'a> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if let Some(value) = self.0 {\n            if value.is_finite() {\n                value.to_css(dest)\n            } else if value.is_nan() {\n                dest.write_str(\"calc(NaN)\")\n            } else {\n                debug_assert!(value.is_infinite());\n                if value.is_sign_negative() {\n                    dest.write_str(\"calc(-infinity)\")\n                } else {\n                    dest.write_str(\"calc(infinity)\")\n                }\n            }\n        } else {\n            dest.write_str(\"none\")\n        }\n    }\n}\n\nimpl ToCss for NumberOrPercentageComponent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        use crate::values::computed::Percentage;\n\n        match self {\n            Self::Number(number) => number.to_css(dest)?,\n            Self::Percentage(percentage) => Percentage(*percentage).to_css(dest)?,\n        }\n        Ok(())\n    }\n}\n\nimpl ToCss for NumberOrAngleComponent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        use crate::values::computed::Angle;\n\n        match self {\n            Self::Number(number) => number.to_css(dest)?,\n            Self::Angle(degrees) => Angle::from_degrees(*degrees).to_css(dest)?,\n        }\n        Ok(())\n    }\n}\n\nimpl ToCss for AbsoluteColor {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match self.color_space {\n            ColorSpace::Srgb if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) => {\n                // The \"none\" keyword is not supported in the rgb/rgba legacy syntax.\n                let has_alpha = self.alpha != OPAQUE;\n\n                dest.write_str(if has_alpha { \"rgba(\" } else { \"rgb(\" })?;\n                clamp_unit_f32(self.components.0).to_css(dest)?;\n                dest.write_str(\", \")?;\n                clamp_unit_f32(self.components.1).to_css(dest)?;\n                dest.write_str(\", \")?;\n                clamp_unit_f32(self.components.2).to_css(dest)?;\n\n                // Legacy syntax does not allow none components.\n                serialize_color_alpha(dest, Some(self.alpha), true)?;\n\n                dest.write_char(')')\n            },\n            ColorSpace::Hsl | ColorSpace::Hwb => {\n                if self.flags.contains(ColorFlags::IS_LEGACY_SRGB) {\n                    self.into_srgb_legacy().to_css(dest)\n                } else {\n                    self.to_color_space(ColorSpace::Srgb).to_css(dest)\n                }\n            },\n            ColorSpace::Oklab | ColorSpace::Lab | ColorSpace::Oklch | ColorSpace::Lch => {\n                if let ColorSpace::Oklab | ColorSpace::Oklch = self.color_space {\n                    dest.write_str(\"ok\")?;\n                }\n                if let ColorSpace::Oklab | ColorSpace::Lab = self.color_space {\n                    dest.write_str(\"lab(\")?;\n                } else {\n                    dest.write_str(\"lch(\")?;\n                }\n                ModernComponent(&self.c0()).to_css(dest)?;\n                dest.write_char(' ')?;\n                ModernComponent(&self.c1()).to_css(dest)?;\n                dest.write_char(' ')?;\n                ModernComponent(&self.c2()).to_css(dest)?;\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n            _ => {\n                #[cfg(debug_assertions)]\n                match self.color_space {\n                    ColorSpace::Srgb => {\n                        debug_assert!(\n                            !self.flags.contains(ColorFlags::IS_LEGACY_SRGB),\n                            \"legacy srgb is not a color function\"\n                        );\n                    },\n                    ColorSpace::SrgbLinear\n                    | ColorSpace::DisplayP3\n                    | ColorSpace::DisplayP3Linear\n                    | ColorSpace::A98Rgb\n                    | ColorSpace::ProphotoRgb\n                    | ColorSpace::Rec2020\n                    | ColorSpace::XyzD50\n                    | ColorSpace::XyzD65 => {\n                        // These color spaces are allowed.\n                    },\n                    ColorSpace::Hsl\n                    | ColorSpace::Hwb\n                    | ColorSpace::Lab\n                    | ColorSpace::Oklab\n                    | ColorSpace::Lch\n                    | ColorSpace::Oklch => {\n                        unreachable!(\"other color spaces do not support color() syntax\")\n                    },\n                };\n\n                dest.write_str(\"color(\")?;\n                self.color_space.to_css(dest)?;\n                dest.write_char(' ')?;\n                ModernComponent(&self.c0()).to_css(dest)?;\n                dest.write_char(' ')?;\n                ModernComponent(&self.c1()).to_css(dest)?;\n                dest.write_char(' ')?;\n                ModernComponent(&self.c2()).to_css(dest)?;\n\n                serialize_color_alpha(dest, self.alpha(), false)?;\n\n                dest.write_char(')')\n            },\n        }\n    }\n}\n\nimpl AbsoluteColor {\n    /// Write a string to `dest` that represents a color as an author would\n    /// enter it.\n    /// NOTE: The format of the output is NOT according to any specification,\n    /// but makes assumptions about the best ways that authors would want to\n    /// enter color values in style sheets, devtools, etc.\n    pub fn write_author_preferred_value<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        macro_rules! precision {\n            ($v:expr) => {{\n                ($v * 100.0).round() / 100.0\n            }};\n        }\n        macro_rules! number {\n            ($c:expr) => {{\n                if let Some(v) = $c.map(normalize) {\n                    precision!(v).to_css(dest)?;\n                } else {\n                    write!(dest, \"none\")?;\n                }\n            }};\n        }\n        macro_rules! percentage {\n            ($c:expr) => {{\n                if let Some(v) = $c.map(normalize) {\n                    precision!(v).to_css(dest)?;\n                    dest.write_char('%')?;\n                } else {\n                    write!(dest, \"none\")?;\n                }\n            }};\n        }\n        macro_rules! unit_percentage {\n            ($c:expr) => {{\n                if let Some(v) = $c.map(normalize) {\n                    precision!(v * 100.0).to_css(dest)?;\n                    dest.write_char('%')?;\n                } else {\n                    write!(dest, \"none\")?;\n                }\n            }};\n        }\n        macro_rules! angle {\n            ($c:expr) => {{\n                if let Some(v) = $c.map(normalize) {\n                    precision!(v).to_css(dest)?;\n                    dest.write_str(\"deg\")?;\n                } else {\n                    write!(dest, \"none\")?;\n                }\n            }};\n        }\n\n        match self.color_space {\n            ColorSpace::Srgb => {\n                write!(dest, \"rgb(\")?;\n                unit_percentage!(self.c0());\n                dest.write_char(' ')?;\n                unit_percentage!(self.c1());\n                dest.write_char(' ')?;\n                unit_percentage!(self.c2());\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n            ColorSpace::Hsl | ColorSpace::Hwb => {\n                dest.write_str(if self.color_space == ColorSpace::Hsl {\n                    \"hsl(\"\n                } else {\n                    \"hwb(\"\n                })?;\n                angle!(self.c0());\n                dest.write_char(' ')?;\n                percentage!(self.c1());\n                dest.write_char(' ')?;\n                percentage!(self.c2());\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n            ColorSpace::Lab | ColorSpace::Oklab => {\n                if self.color_space == ColorSpace::Oklab {\n                    dest.write_str(\"ok\")?;\n                }\n                dest.write_str(\"lab(\")?;\n                if self.color_space == ColorSpace::Lab {\n                    percentage!(self.c0())\n                } else {\n                    unit_percentage!(self.c0())\n                }\n                dest.write_char(' ')?;\n                number!(self.c1());\n                dest.write_char(' ')?;\n                number!(self.c2());\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n            ColorSpace::Lch | ColorSpace::Oklch => {\n                if self.color_space == ColorSpace::Oklch {\n                    dest.write_str(\"ok\")?;\n                }\n                dest.write_str(\"lch(\")?;\n                number!(self.c0());\n                dest.write_char(' ')?;\n                number!(self.c1());\n                dest.write_char(' ')?;\n                angle!(self.c2());\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n            ColorSpace::SrgbLinear\n            | ColorSpace::DisplayP3\n            | ColorSpace::DisplayP3Linear\n            | ColorSpace::A98Rgb\n            | ColorSpace::ProphotoRgb\n            | ColorSpace::Rec2020\n            | ColorSpace::XyzD50\n            | ColorSpace::XyzD65 => {\n                dest.write_str(\"color(\")?;\n                self.color_space.to_css(dest)?;\n                dest.write_char(' ')?;\n                number!(self.c0());\n                dest.write_char(' ')?;\n                number!(self.c1());\n                dest.write_char(' ')?;\n                number!(self.c2());\n                serialize_color_alpha(dest, self.alpha(), false)?;\n                dest.write_char(')')\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "style/context.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The context within which style is calculated.\n\n#[cfg(feature = \"servo\")]\nuse crate::animation::DocumentAnimationSet;\nuse crate::bloom::StyleBloom;\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::data::{EagerPseudoStyles, ElementData};\nuse crate::derives::*;\nuse crate::dom::{SendElement, TElement};\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs;\nuse crate::parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};\nuse crate::properties::ComputedValues;\n#[cfg(feature = \"servo\")]\nuse crate::properties::PropertyId;\nuse crate::rule_cache::RuleCache;\nuse crate::rule_tree::{RuleCascadeFlags, StrongRuleNode};\nuse crate::selector_parser::{SnapshotMap, EAGER_PSEUDO_COUNT};\nuse crate::shared_lock::StylesheetGuards;\nuse crate::sharing::StyleSharingCache;\nuse crate::stylist::Stylist;\nuse crate::thread_state::{self, ThreadState};\nuse crate::traversal::DomTraversal;\nuse crate::traversal_flags::TraversalFlags;\nuse app_units::Au;\nuse euclid::default::Size2D;\nuse euclid::Scale;\n#[cfg(feature = \"servo\")]\nuse rustc_hash::FxHashMap;\nuse selectors::context::SelectorCaches;\n#[cfg(feature = \"gecko\")]\nuse servo_arc::Arc;\nuse std::fmt;\nuse std::ops;\nuse std::time::{Duration, Instant};\nuse style_traits::CSSPixel;\nuse style_traits::DevicePixel;\n#[cfg(feature = \"servo\")]\nuse style_traits::SpeculativePainter;\n#[cfg(feature = \"servo\")]\nuse stylo_atoms::Atom;\n\npub use selectors::matching::QuirksMode;\n\n/// A global options structure for the style system. We use this instead of\n/// opts to abstract across Gecko and Servo.\n#[derive(Clone)]\npub struct StyleSystemOptions {\n    /// Whether the style sharing cache is disabled.\n    pub disable_style_sharing_cache: bool,\n    /// Whether we should dump statistics about the style system.\n    pub dump_style_statistics: bool,\n    /// The minimum number of elements that must be traversed to trigger a dump\n    /// of style statistics.\n    pub style_statistics_threshold: usize,\n}\n\n#[cfg(feature = \"gecko\")]\nfn get_env_bool(name: &str) -> bool {\n    use std::env;\n    match env::var(name) {\n        Ok(s) => !s.is_empty(),\n        Err(_) => false,\n    }\n}\n\nconst DEFAULT_STATISTICS_THRESHOLD: usize = 50;\n\n#[cfg(feature = \"gecko\")]\nfn get_env_usize(name: &str) -> Option<usize> {\n    use std::env;\n    env::var(name).ok().map(|s| {\n        s.parse::<usize>()\n            .expect(\"Couldn't parse environmental variable as usize\")\n    })\n}\n\n/// A global variable holding the state of\n/// `StyleSystemOptions::default().disable_style_sharing_cache`.\n/// See [#22854](https://github.com/servo/servo/issues/22854).\n#[cfg(feature = \"servo\")]\npub static DEFAULT_DISABLE_STYLE_SHARING_CACHE: std::sync::atomic::AtomicBool =\n    std::sync::atomic::AtomicBool::new(false);\n\n/// A global variable holding the state of\n/// `StyleSystemOptions::default().dump_style_statistics`.\n/// See [#22854](https://github.com/servo/servo/issues/22854).\n#[cfg(feature = \"servo\")]\npub static DEFAULT_DUMP_STYLE_STATISTICS: std::sync::atomic::AtomicBool =\n    std::sync::atomic::AtomicBool::new(false);\n\nimpl Default for StyleSystemOptions {\n    #[cfg(feature = \"servo\")]\n    fn default() -> Self {\n        use std::sync::atomic::Ordering;\n\n        StyleSystemOptions {\n            disable_style_sharing_cache: DEFAULT_DISABLE_STYLE_SHARING_CACHE\n                .load(Ordering::Relaxed),\n            dump_style_statistics: DEFAULT_DUMP_STYLE_STATISTICS.load(Ordering::Relaxed),\n            style_statistics_threshold: DEFAULT_STATISTICS_THRESHOLD,\n        }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn default() -> Self {\n        StyleSystemOptions {\n            disable_style_sharing_cache: get_env_bool(\"DISABLE_STYLE_SHARING_CACHE\"),\n            dump_style_statistics: get_env_bool(\"DUMP_STYLE_STATISTICS\"),\n            style_statistics_threshold: get_env_usize(\"STYLE_STATISTICS_THRESHOLD\")\n                .unwrap_or(DEFAULT_STATISTICS_THRESHOLD),\n        }\n    }\n}\n\n/// A shared style context.\n///\n/// There's exactly one of these during a given restyle traversal, and it's\n/// shared among the worker threads.\npub struct SharedStyleContext<'a> {\n    /// The CSS selector stylist.\n    pub stylist: &'a Stylist,\n\n    /// Whether visited styles are enabled.\n    ///\n    /// They may be disabled when Gecko's pref layout.css.visited_links_enabled\n    /// is false, or when in private browsing mode.\n    pub visited_styles_enabled: bool,\n\n    /// Configuration options.\n    pub options: StyleSystemOptions,\n\n    /// Guards for pre-acquired locks\n    pub guards: StylesheetGuards<'a>,\n\n    /// The current time for transitions and animations. This is needed to ensure\n    /// a consistent sampling time and also to adjust the time for testing.\n    pub current_time_for_animations: f64,\n\n    /// Flags controlling how we traverse the tree.\n    pub traversal_flags: TraversalFlags,\n\n    /// A map with our snapshots in order to handle restyle hints.\n    pub snapshot_map: &'a SnapshotMap,\n\n    /// The state of all animations for our styled elements.\n    #[cfg(feature = \"servo\")]\n    pub animations: DocumentAnimationSet,\n\n    /// Paint worklets\n    #[cfg(feature = \"servo\")]\n    pub registered_speculative_painters: &'a dyn RegisteredSpeculativePainters,\n}\n\nimpl<'a> SharedStyleContext<'a> {\n    /// Return a suitable viewport size in order to be used for viewport units.\n    pub fn viewport_size(&self) -> Size2D<Au> {\n        self.stylist.device().au_viewport_size()\n    }\n\n    /// The device pixel ratio\n    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {\n        self.stylist.device().device_pixel_ratio()\n    }\n\n    /// The quirks mode of the document.\n    pub fn quirks_mode(&self) -> QuirksMode {\n        self.stylist.quirks_mode()\n    }\n}\n\n/// The structure holds various intermediate inputs that are eventually used by\n/// by the cascade.\n///\n/// The matching and cascading process stores them in this format temporarily\n/// within the `CurrentElementInfo`. At the end of the cascade, they are folded\n/// down into the main `ComputedValues` to reduce memory usage per element while\n/// still remaining accessible.\n#[derive(Clone, Debug, Default)]\npub struct CascadeInputs {\n    /// The rule node representing the ordered list of rules matched for this\n    /// node.\n    pub rules: Option<StrongRuleNode>,\n\n    /// The rule node representing the ordered list of rules matched for this\n    /// node if visited, only computed if there's a relevant link for this\n    /// element. A element's \"relevant link\" is the element being matched if it\n    /// is a link or the nearest ancestor link.\n    pub visited_rules: Option<StrongRuleNode>,\n\n    /// The set of flags from container queries that we need for invalidation.\n    pub flags: ComputedValueFlags,\n\n    /// The set of RuleCascadeFlags to include in the cascade.\n    pub included_cascade_flags: RuleCascadeFlags,\n}\n\nimpl CascadeInputs {\n    /// Construct inputs from previous cascade results, if any.\n    pub fn new_from_style(style: &ComputedValues) -> Self {\n        Self {\n            rules: style.rules.clone(),\n            visited_rules: style.visited_style().and_then(|v| v.rules.clone()),\n            flags: style.flags.for_cascade_inputs(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n        }\n    }\n}\n\n/// A list of cascade inputs for eagerly-cascaded pseudo-elements.\n/// The list is stored inline.\n#[derive(Debug)]\npub struct EagerPseudoCascadeInputs(Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]>);\n\n// Manually implement `Clone` here because the derived impl of `Clone` for\n// array types assumes the value inside is `Copy`.\nimpl Clone for EagerPseudoCascadeInputs {\n    fn clone(&self) -> Self {\n        if self.0.is_none() {\n            return EagerPseudoCascadeInputs(None);\n        }\n        let self_inputs = self.0.as_ref().unwrap();\n        let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();\n        for i in 0..EAGER_PSEUDO_COUNT {\n            inputs[i] = self_inputs[i].clone();\n        }\n        EagerPseudoCascadeInputs(Some(inputs))\n    }\n}\n\nimpl EagerPseudoCascadeInputs {\n    /// Construct inputs from previous cascade results, if any.\n    fn new_from_style(styles: &EagerPseudoStyles) -> Self {\n        EagerPseudoCascadeInputs(styles.as_optional_array().map(|styles| {\n            let mut inputs: [Option<CascadeInputs>; EAGER_PSEUDO_COUNT] = Default::default();\n            for i in 0..EAGER_PSEUDO_COUNT {\n                inputs[i] = styles[i].as_ref().map(|s| CascadeInputs::new_from_style(s));\n            }\n            inputs\n        }))\n    }\n\n    /// Returns the list of rules, if they exist.\n    pub fn into_array(self) -> Option<[Option<CascadeInputs>; EAGER_PSEUDO_COUNT]> {\n        self.0\n    }\n}\n\n/// The cascade inputs associated with a node, including those for any\n/// pseudo-elements.\n///\n/// The matching and cascading process stores them in this format temporarily\n/// within the `CurrentElementInfo`. At the end of the cascade, they are folded\n/// down into the main `ComputedValues` to reduce memory usage per element while\n/// still remaining accessible.\n#[derive(Clone, Debug)]\npub struct ElementCascadeInputs {\n    /// The element's cascade inputs.\n    pub primary: CascadeInputs,\n    /// A list of the inputs for the element's eagerly-cascaded pseudo-elements.\n    pub pseudos: EagerPseudoCascadeInputs,\n}\n\nimpl ElementCascadeInputs {\n    /// Construct inputs from previous cascade results, if any.\n    #[inline]\n    pub fn new_from_element_data(data: &ElementData) -> Self {\n        debug_assert!(data.has_styles());\n        ElementCascadeInputs {\n            primary: CascadeInputs::new_from_style(data.styles.primary()),\n            pseudos: EagerPseudoCascadeInputs::new_from_style(&data.styles.pseudos),\n        }\n    }\n}\n\n/// Statistics gathered during the traversal. We gather statistics on each\n/// thread and then combine them after the threads join via the Add\n/// implementation below.\n#[derive(AddAssign, Clone, Default)]\npub struct PerThreadTraversalStatistics {\n    /// The total number of elements traversed.\n    pub elements_traversed: u32,\n    /// The number of elements where has_styles() went from false to true.\n    pub elements_styled: u32,\n    /// The number of elements for which we performed selector matching.\n    pub elements_matched: u32,\n    /// The number of cache hits from the StyleSharingCache.\n    pub styles_shared: u32,\n    /// The number of styles reused via rule node comparison from the\n    /// StyleSharingCache.\n    pub styles_reused: u32,\n}\n\n/// Statistics gathered during the traversal plus some information from\n/// other sources including stylist.\n#[derive(Default)]\npub struct TraversalStatistics {\n    /// Aggregated statistics gathered during the traversal.\n    pub aggregated: PerThreadTraversalStatistics,\n    /// The number of selectors in the stylist.\n    pub selectors: u32,\n    /// The number of revalidation selectors.\n    pub revalidation_selectors: u32,\n    /// The number of state/attr dependencies in the dependency set.\n    pub dependency_selectors: u32,\n    /// The number of declarations in the stylist.\n    pub declarations: u32,\n    /// The number of times the stylist was rebuilt.\n    pub stylist_rebuilds: u32,\n    /// Time spent in the traversal, in milliseconds.\n    pub traversal_time: Duration,\n    /// Whether this was a parallel traversal.\n    pub is_parallel: bool,\n    /// Whether this is a \"large\" traversal.\n    pub is_large: bool,\n}\n\n/// Format the statistics in a way that the performance test harness understands.\n/// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331856#c2\nimpl fmt::Display for TraversalStatistics {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"[PERF] perf block start\")?;\n        writeln!(\n            f,\n            \"[PERF],traversal,{}\",\n            if self.is_parallel {\n                \"parallel\"\n            } else {\n                \"sequential\"\n            }\n        )?;\n        writeln!(\n            f,\n            \"[PERF],elements_traversed,{}\",\n            self.aggregated.elements_traversed\n        )?;\n        writeln!(\n            f,\n            \"[PERF],elements_styled,{}\",\n            self.aggregated.elements_styled\n        )?;\n        writeln!(\n            f,\n            \"[PERF],elements_matched,{}\",\n            self.aggregated.elements_matched\n        )?;\n        writeln!(f, \"[PERF],styles_shared,{}\", self.aggregated.styles_shared)?;\n        writeln!(f, \"[PERF],styles_reused,{}\", self.aggregated.styles_reused)?;\n        writeln!(f, \"[PERF],selectors,{}\", self.selectors)?;\n        writeln!(\n            f,\n            \"[PERF],revalidation_selectors,{}\",\n            self.revalidation_selectors\n        )?;\n        writeln!(\n            f,\n            \"[PERF],dependency_selectors,{}\",\n            self.dependency_selectors\n        )?;\n        writeln!(f, \"[PERF],declarations,{}\", self.declarations)?;\n        writeln!(f, \"[PERF],stylist_rebuilds,{}\", self.stylist_rebuilds)?;\n        writeln!(\n            f,\n            \"[PERF],traversal_time_ms,{}\",\n            self.traversal_time.as_secs_f64() * 1000.\n        )?;\n        writeln!(f, \"[PERF] perf block end\")\n    }\n}\n\nimpl TraversalStatistics {\n    /// Generate complete traversal statistics.\n    ///\n    /// The traversal time is computed given the start time in seconds.\n    pub fn new<E, D>(\n        aggregated: PerThreadTraversalStatistics,\n        traversal: &D,\n        parallel: bool,\n        start: Instant,\n    ) -> TraversalStatistics\n    where\n        E: TElement,\n        D: DomTraversal<E>,\n    {\n        let threshold = traversal\n            .shared_context()\n            .options\n            .style_statistics_threshold;\n        let stylist = traversal.shared_context().stylist;\n        let is_large = aggregated.elements_traversed as usize >= threshold;\n        TraversalStatistics {\n            aggregated,\n            selectors: stylist.num_selectors() as u32,\n            revalidation_selectors: stylist.num_revalidation_selectors() as u32,\n            dependency_selectors: stylist.num_invalidations() as u32,\n            declarations: stylist.num_declarations() as u32,\n            stylist_rebuilds: stylist.num_rebuilds() as u32,\n            traversal_time: Instant::now() - start,\n            is_parallel: parallel,\n            is_large,\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nbitflags! {\n    /// Represents which tasks are performed in a SequentialTask of\n    /// UpdateAnimations which is a result of normal restyle.\n    pub struct UpdateAnimationsTasks: u8 {\n        /// Update CSS Animations.\n        const CSS_ANIMATIONS = structs::UpdateAnimationsTasks_CSSAnimations;\n        /// Update CSS Transitions.\n        const CSS_TRANSITIONS = structs::UpdateAnimationsTasks_CSSTransitions;\n        /// Update effect properties.\n        const EFFECT_PROPERTIES = structs::UpdateAnimationsTasks_EffectProperties;\n        /// Update animation cacade results for animations running on the compositor.\n        const CASCADE_RESULTS = structs::UpdateAnimationsTasks_CascadeResults;\n        /// Display property was changed from none.\n        /// Script animations keep alive on display:none elements, so we need to trigger\n        /// the second animation restyles for the script animations in the case where\n        /// the display property was changed from 'none' to others.\n        const DISPLAY_CHANGED_FROM_NONE = structs::UpdateAnimationsTasks_DisplayChangedFromNone;\n        /// Update CSS named scroll progress timelines.\n        const SCROLL_TIMELINES = structs::UpdateAnimationsTasks_ScrollTimelines;\n        /// Update CSS named view progress timelines.\n        const VIEW_TIMELINES = structs::UpdateAnimationsTasks_ViewTimelines;\n        /// Update CSS timeline scopes, which affect visibility of both scroll and view timelines.\n        const TIMELINE_SCOPES = structs::UpdateAnimationsTasks_TimelineScopes;\n    }\n}\n\n/// A task to be run in sequential mode on the parent (non-worker) thread. This\n/// is used by the style system to queue up work which is not safe to do during\n/// the parallel traversal.\npub enum SequentialTask<E: TElement> {\n    /// Entry to avoid an unused type parameter error on servo.\n    Unused(SendElement<E>),\n\n    /// Performs one of a number of possible tasks related to updating\n    /// animations based on the |tasks| field. These include updating CSS\n    /// animations/transitions that changed as part of the non-animation style\n    /// traversal, and updating the computed effect properties.\n    #[cfg(feature = \"gecko\")]\n    UpdateAnimations {\n        /// The target element or pseudo-element.\n        el: SendElement<E>,\n        /// The before-change style for transitions. We use before-change style\n        /// as the initial value of its Keyframe. Required if |tasks| includes\n        /// CSSTransitions.\n        before_change_style: Option<Arc<ComputedValues>>,\n        /// The tasks which are performed in this SequentialTask.\n        tasks: UpdateAnimationsTasks,\n    },\n}\n\nimpl<E: TElement> SequentialTask<E> {\n    /// Executes this task.\n    pub fn execute(self) {\n        use self::SequentialTask::*;\n        debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));\n        match self {\n            Unused(_) => unreachable!(),\n            #[cfg(feature = \"gecko\")]\n            UpdateAnimations {\n                el,\n                before_change_style,\n                tasks,\n            } => {\n                el.update_animations(before_change_style, tasks);\n            },\n        }\n    }\n\n    /// Creates a task to update various animation-related state on a given\n    /// (pseudo-)element.\n    #[cfg(feature = \"gecko\")]\n    pub fn update_animations(\n        el: E,\n        before_change_style: Option<Arc<ComputedValues>>,\n        tasks: UpdateAnimationsTasks,\n    ) -> Self {\n        use self::SequentialTask::*;\n        UpdateAnimations {\n            el: unsafe { SendElement::new(el) },\n            before_change_style,\n            tasks,\n        }\n    }\n}\n\n/// A list of SequentialTasks that get executed on Drop.\npub struct SequentialTaskList<E>(Vec<SequentialTask<E>>)\nwhere\n    E: TElement;\n\nimpl<E> ops::Deref for SequentialTaskList<E>\nwhere\n    E: TElement,\n{\n    type Target = Vec<SequentialTask<E>>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl<E> ops::DerefMut for SequentialTaskList<E>\nwhere\n    E: TElement,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\nimpl<E> Drop for SequentialTaskList<E>\nwhere\n    E: TElement,\n{\n    fn drop(&mut self) {\n        debug_assert!(thread_state::get().contains(ThreadState::LAYOUT));\n        for task in self.0.drain(..) {\n            task.execute()\n        }\n    }\n}\n\n/// A helper type for stack limit checking.  This assumes that stacks grow\n/// down, which is true for all non-ancient CPU architectures.\npub struct StackLimitChecker {\n    lower_limit: usize,\n}\n\nimpl StackLimitChecker {\n    /// Create a new limit checker, for this thread, allowing further use\n    /// of up to |stack_size| bytes beyond (below) the current stack pointer.\n    #[inline(never)]\n    pub fn new(stack_size_limit: usize) -> Self {\n        StackLimitChecker {\n            lower_limit: StackLimitChecker::get_sp() - stack_size_limit,\n        }\n    }\n\n    /// Checks whether the previously stored stack limit has now been exceeded.\n    #[inline(never)]\n    pub fn limit_exceeded(&self) -> bool {\n        let curr_sp = StackLimitChecker::get_sp();\n\n        // Do some sanity-checking to ensure that our invariants hold, even in\n        // the case where we've exceeded the soft limit.\n        //\n        // The correctness of depends on the assumption that no stack wraps\n        // around the end of the address space.\n        if cfg!(debug_assertions) {\n            // Compute the actual bottom of the stack by subtracting our safety\n            // margin from our soft limit. Note that this will be slightly below\n            // the actual bottom of the stack, because there are a few initial\n            // frames on the stack before we do the measurement that computes\n            // the limit.\n            let stack_bottom = self.lower_limit - STACK_SAFETY_MARGIN_KB * 1024;\n\n            // The bottom of the stack should be below the current sp. If it\n            // isn't, that means we've either waited too long to check the limit\n            // and burned through our safety margin (in which case we probably\n            // would have segfaulted by now), or we're using a limit computed for\n            // a different thread.\n            debug_assert!(stack_bottom < curr_sp);\n\n            // Compute the distance between the current sp and the bottom of\n            // the stack, and compare it against the current stack. It should be\n            // no further from us than the total stack size. We allow some slop\n            // to handle the fact that stack_bottom is a bit further than the\n            // bottom of the stack, as discussed above.\n            let distance_to_stack_bottom = curr_sp - stack_bottom;\n            let max_allowable_distance = (STYLE_THREAD_STACK_SIZE_KB + 10) * 1024;\n            debug_assert!(distance_to_stack_bottom <= max_allowable_distance);\n        }\n\n        // The actual bounds check.\n        curr_sp <= self.lower_limit\n    }\n\n    // Technically, rustc can optimize this away, but shouldn't for now.\n    // We should fix this once black_box is stable.\n    #[inline(always)]\n    fn get_sp() -> usize {\n        let mut foo: usize = 42;\n        (&mut foo as *mut usize) as usize\n    }\n}\n\n/// A thread-local style context.\n///\n/// This context contains data that needs to be used during restyling, but is\n/// not required to be unique among worker threads, so we create one per worker\n/// thread in order to be able to mutate it without locking.\npub struct ThreadLocalStyleContext<E: TElement> {\n    /// A cache to share style among siblings.\n    pub sharing_cache: StyleSharingCache<E>,\n    /// A cache from matched properties to elements that match those.\n    pub rule_cache: RuleCache,\n    /// The bloom filter used to fast-reject selector-matching.\n    pub bloom_filter: StyleBloom<E>,\n    /// A set of tasks to be run (on the parent thread) in sequential mode after\n    /// the rest of the styling is complete. This is useful for\n    /// infrequently-needed non-threadsafe operations.\n    ///\n    /// It's important that goes after the style sharing cache and the bloom\n    /// filter, to ensure they're dropped before we execute the tasks, which\n    /// could create another ThreadLocalStyleContext for style computation.\n    pub tasks: SequentialTaskList<E>,\n    /// Statistics about the traversal.\n    pub statistics: PerThreadTraversalStatistics,\n    /// A checker used to ensure that parallel.rs does not recurse indefinitely\n    /// even on arbitrarily deep trees.  See Gecko bug 1376883.\n    pub stack_limit_checker: StackLimitChecker,\n    /// Collection of caches (And cache-likes) for speeding up expensive selector matches.\n    pub selector_caches: SelectorCaches,\n}\n\nimpl<E: TElement> ThreadLocalStyleContext<E> {\n    /// Creates a new `ThreadLocalStyleContext`\n    pub fn new() -> Self {\n        ThreadLocalStyleContext {\n            sharing_cache: StyleSharingCache::new(),\n            rule_cache: RuleCache::new(),\n            bloom_filter: StyleBloom::new(),\n            tasks: SequentialTaskList(Vec::new()),\n            statistics: PerThreadTraversalStatistics::default(),\n            stack_limit_checker: StackLimitChecker::new(\n                (STYLE_THREAD_STACK_SIZE_KB - STACK_SAFETY_MARGIN_KB) * 1024,\n            ),\n            selector_caches: SelectorCaches::default(),\n        }\n    }\n}\n\n/// A `StyleContext` is just a simple container for a immutable reference to a\n/// shared style context, and a mutable reference to a local one.\npub struct StyleContext<'a, E: TElement + 'a> {\n    /// The shared style context reference.\n    pub shared: &'a SharedStyleContext<'a>,\n    /// The thread-local style context (mutable) reference.\n    pub thread_local: &'a mut ThreadLocalStyleContext<E>,\n}\n\n/// A registered painter\n#[cfg(feature = \"servo\")]\npub trait RegisteredSpeculativePainter: SpeculativePainter {\n    /// The name it was registered with\n    fn name(&self) -> Atom;\n    /// The properties it was registered with\n    fn properties(&self) -> &FxHashMap<Atom, PropertyId>;\n}\n\n/// A set of registered painters\n#[cfg(feature = \"servo\")]\npub trait RegisteredSpeculativePainters: Sync {\n    /// Look up a speculative painter\n    fn get(&self, name: &Atom) -> Option<&dyn RegisteredSpeculativePainter>;\n}\n"
  },
  {
    "path": "style/counter_style/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@counter-style`][counter-style] at-rule.\n//!\n//! [counter-style]: https://drafts.csswg.org/css-counter-styles/\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::values::specified::Integer;\nuse crate::values::{AtomString, CustomIdent};\nuse crate::Atom;\nuse cssparser::{\n    ascii_case_insensitive_phf_map, match_ignore_ascii_case, CowRcStr, Parser, RuleBodyParser,\n    SourceLocation, Token,\n};\nuse std::fmt::{self, Write};\nuse std::mem;\nuse std::num::Wrapping;\nuse style_traits::{\n    Comma, CssStringWriter, CssWriter, KeywordsCollectFn, OneOrMoreSeparated, ParseError,\n    SpecifiedValueInfo, StyleParseErrorKind, ToCss,\n};\n\npub use crate::properties::counter_style::{DescriptorId, DescriptorParser, Descriptors};\n\n/// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum SymbolsType {\n    Cyclic,\n    Numeric,\n    Alphabetic,\n    Symbolic,\n    Fixed,\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>\n///\n/// Note that 'none' is not a valid name, but we include this (along with String) for space\n/// efficiency when storing list-style-type.\n#[derive(\n    Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,\n)]\n#[repr(u8)]\npub enum CounterStyle {\n    /// The 'none' value.\n    None,\n    /// `<counter-style-name>`\n    Name(CustomIdent),\n    /// `symbols()`\n    #[css(function)]\n    Symbols {\n        /// The <symbols-type>, or symbolic if not specified.\n        #[css(skip_if = \"is_symbolic\")]\n        ty: SymbolsType,\n        /// The actual symbols.\n        symbols: Symbols,\n    },\n    /// A single string value, useful for `<list-style-type>`.\n    String(AtomString),\n}\n\n#[inline]\nfn is_symbolic(symbols_type: &SymbolsType) -> bool {\n    *symbols_type == SymbolsType::Symbolic\n}\n\nimpl CounterStyle {\n    /// disc value\n    pub fn disc() -> Self {\n        CounterStyle::Name(CustomIdent(atom!(\"disc\")))\n    }\n\n    /// decimal value\n    pub fn decimal() -> Self {\n        CounterStyle::Name(CustomIdent(atom!(\"decimal\")))\n    }\n\n    /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)\n    #[inline]\n    pub fn is_bullet(&self) -> bool {\n        match self {\n            CounterStyle::Name(CustomIdent(ref name)) => {\n                name == &atom!(\"disc\")\n                    || name == &atom!(\"circle\")\n                    || name == &atom!(\"square\")\n                    || name == &atom!(\"disclosure-closed\")\n                    || name == &atom!(\"disclosure-open\")\n            },\n            _ => false,\n        }\n    }\n}\n\nbitflags! {\n    #[derive(Clone, Copy)]\n    /// Flags to control parsing of counter styles.\n    pub struct CounterStyleParsingFlags: u8 {\n        /// Whether `none` is allowed.\n        const ALLOW_NONE = 1 << 0;\n        /// Whether a bare string is allowed.\n        const ALLOW_STRING = 1 << 1;\n    }\n}\n\nimpl CounterStyle {\n    /// Parse a counter style, and optionally none|string (for list-style-type).\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        flags: CounterStyleParsingFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        use self::CounterStyleParsingFlags as Flags;\n        let location = input.current_source_location();\n        match input.next()? {\n            Token::QuotedString(ref string) if flags.intersects(Flags::ALLOW_STRING) => {\n                Ok(Self::String(AtomString::from(string.as_ref())))\n            },\n            Token::Ident(ref ident) => {\n                if flags.intersects(Flags::ALLOW_NONE) && ident.eq_ignore_ascii_case(\"none\") {\n                    return Ok(Self::None);\n                }\n                Ok(Self::Name(counter_style_name_from_ident(ident, location)?))\n            },\n            Token::Function(ref name) if name.eq_ignore_ascii_case(\"symbols\") => {\n                input.parse_nested_block(|input| {\n                    let symbols_type = input\n                        .try_parse(SymbolsType::parse)\n                        .unwrap_or(SymbolsType::Symbolic);\n                    let symbols = Symbols::parse(context, input)?;\n                    // There must be at least two symbols for alphabetic or\n                    // numeric system.\n                    if (symbols_type == SymbolsType::Alphabetic\n                        || symbols_type == SymbolsType::Numeric)\n                        && symbols.0.len() < 2\n                    {\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                    // Identifier is not allowed in symbols() function.\n                    if symbols.0.iter().any(|sym| !sym.is_allowed_in_symbols()) {\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                    Ok(Self::Symbols {\n                        ty: symbols_type,\n                        symbols,\n                    })\n                })\n            },\n            t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n}\n\nimpl SpecifiedValueInfo for CounterStyle {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        // XXX The best approach for implementing this is probably\n        // having a CounterStyleName type wrapping CustomIdent, and\n        // put the predefined list for that type in counter_style mod.\n        // But that's a non-trivial change itself, so we use a simpler\n        // approach here.\n        macro_rules! predefined {\n            ($($name:expr,)+) => {\n                f(&[\"symbols\", \"none\", $($name,)+])\n            }\n        }\n        include!(\"predefined.rs\");\n    }\n}\n\nfn parse_counter_style_name<'i>(input: &mut Parser<'i, '_>) -> Result<CustomIdent, ParseError<'i>> {\n    let location = input.current_source_location();\n    let ident = input.expect_ident()?;\n    counter_style_name_from_ident(ident, location)\n}\n\n/// This allows the reserved counter style names \"decimal\" and \"disc\".\nfn counter_style_name_from_ident<'i>(\n    ident: &CowRcStr<'i>,\n    location: SourceLocation,\n) -> Result<CustomIdent, ParseError<'i>> {\n    macro_rules! predefined {\n        ($($name: tt,)+) => {{\n            ascii_case_insensitive_phf_map! {\n                predefined -> Atom = {\n                    $(\n                        $name => atom!($name),\n                    )+\n                }\n            }\n\n            // This effectively performs case normalization only on predefined names.\n            if let Some(lower_case) = predefined::get(&ident) {\n                Ok(CustomIdent(lower_case.clone()))\n            } else {\n                // none is always an invalid <counter-style> value.\n                CustomIdent::from_ident(location, ident, &[\"none\"])\n            }\n        }}\n    }\n    include!(\"predefined.rs\")\n}\n\nfn is_valid_name_definition(ident: &CustomIdent) -> bool {\n    ident.0 != atom!(\"decimal\")\n        && ident.0 != atom!(\"disc\")\n        && ident.0 != atom!(\"circle\")\n        && ident.0 != atom!(\"square\")\n        && ident.0 != atom!(\"disclosure-closed\")\n        && ident.0 != atom!(\"disclosure-open\")\n}\n\n/// Parse the prelude of an @counter-style rule\npub fn parse_counter_style_name_definition<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<CustomIdent, ParseError<'i>> {\n    parse_counter_style_name(input).and_then(|ident| {\n        if !is_valid_name_definition(&ident) {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        } else {\n            Ok(ident)\n        }\n    })\n}\n\n/// A @counter-style rule\n#[derive(Clone, Debug, ToShmem)]\npub struct CounterStyleRule {\n    name: CustomIdent,\n    generation: Wrapping<u32>,\n    descriptors: Descriptors,\n    /// The parser location of the @counter-style rule.\n    pub source_location: SourceLocation,\n}\n\n/// Parse the body (inside `{}`) of an @counter-style rule\npub fn parse_counter_style_body<'i, 't>(\n    name: CustomIdent,\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    location: SourceLocation,\n) -> Result<CounterStyleRule, ParseError<'i>> {\n    let start = input.current_source_location();\n    let mut rule = CounterStyleRule::empty(name, location);\n    {\n        let mut parser = DescriptorParser {\n            context,\n            descriptors: &mut rule.descriptors,\n        };\n        let mut iter = RuleBodyParser::new(input, &mut parser);\n        while let Some(declaration) = iter.next() {\n            if let Err((error, slice)) = declaration {\n                let location = error.location;\n                let error = ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(\n                    slice, error,\n                );\n                context.log_css_error(location, error)\n            }\n        }\n    }\n    let error = match *rule.resolved_system() {\n        ref system @ System::Cyclic\n        | ref system @ System::Fixed { .. }\n        | ref system @ System::Symbolic\n        | ref system @ System::Alphabetic\n        | ref system @ System::Numeric\n            if rule.descriptors.symbols.is_none() =>\n        {\n            let system = system.to_css_string();\n            Some(ContextualParseError::InvalidCounterStyleWithoutSymbols(\n                system,\n            ))\n        },\n        ref system @ System::Alphabetic | ref system @ System::Numeric\n            if rule.descriptors.symbols.as_ref().unwrap().0.len() < 2 =>\n        {\n            let system = system.to_css_string();\n            Some(ContextualParseError::InvalidCounterStyleNotEnoughSymbols(\n                system,\n            ))\n        },\n        System::Additive if rule.descriptors.additive_symbols.is_none() => {\n            Some(ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols)\n        },\n        System::Extends(_) if rule.descriptors.symbols.is_some() => {\n            Some(ContextualParseError::InvalidCounterStyleExtendsWithSymbols)\n        },\n        System::Extends(_) if rule.descriptors.additive_symbols.is_some() => {\n            Some(ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols)\n        },\n        _ => None,\n    };\n    if let Some(error) = error {\n        context.log_css_error(start, error);\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    } else {\n        Ok(rule)\n    }\n}\n\nimpl ToCssWithGuard for CounterStyleRule {\n    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@counter-style \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" { \")?;\n        self.descriptors.to_css(&mut CssWriter::new(dest))?;\n        dest.write_char('}')\n    }\n}\n\n// Implements the special checkers for some setters.\n// See <https://drafts.csswg.org/css-counter-styles/#the-csscounterstylerule-interface>\nimpl CounterStyleRule {\n    fn empty(name: CustomIdent, source_location: SourceLocation) -> Self {\n        Self {\n            name: name,\n            generation: Wrapping(0),\n            descriptors: Descriptors::default(),\n            source_location,\n        }\n    }\n\n    /// Expose the descriptors as read-only, since we rely on updating our generation when they\n    /// change, and some setters need extra validation.\n    pub fn descriptors(&self) -> &Descriptors {\n        &self.descriptors\n    }\n\n    /// Set a descriptor, with the relevant validation. returns whether the descriptor changed.\n    pub fn set_descriptor<'i>(\n        &mut self,\n        id: DescriptorId,\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<bool, ParseError<'i>> {\n        // Some descriptors need a couple extra checks, deal with them specially.\n        // TODO(emilio): Remove these, see https://github.com/w3c/csswg-drafts/issues/5717\n        if id == DescriptorId::AdditiveSymbols\n            && matches!(*self.resolved_system(), System::Extends(..))\n        {\n            // No additive symbols should be set for extends system.\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let changed = match id {\n            DescriptorId::System => {\n                let system = input.parse_entirely(|i| System::parse(context, i))?;\n                if !self.check_system(&system) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                let new = Some(system);\n                if self.descriptors.system == new {\n                    return Ok(false);\n                }\n                self.descriptors.system = new;\n                true\n            },\n            DescriptorId::Symbols => {\n                let symbols = input.parse_entirely(|i| Symbols::parse(context, i))?;\n                if !self.check_symbols(&symbols) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                let new = Some(symbols);\n                if self.descriptors.symbols == new {\n                    return Ok(false);\n                }\n                self.descriptors.symbols = new;\n                true\n            },\n            _ => self.descriptors.set(id, context, input)?,\n        };\n        if changed {\n            self.generation += Wrapping(1);\n        }\n        Ok(changed)\n    }\n\n    /// Check that the system is effectively not changed. Only params of system descriptor is\n    /// changeable.\n    fn check_system(&self, value: &System) -> bool {\n        mem::discriminant(self.resolved_system()) == mem::discriminant(value)\n    }\n\n    fn check_symbols(&self, value: &Symbols) -> bool {\n        match *self.resolved_system() {\n            // These two systems require at least two symbols.\n            System::Numeric | System::Alphabetic => value.0.len() >= 2,\n            // No symbols should be set for extends system.\n            System::Extends(_) => false,\n            _ => true,\n        }\n    }\n\n    /// Get the name of the counter style rule.\n    pub fn name(&self) -> &CustomIdent {\n        &self.name\n    }\n\n    /// Set the name of the counter style rule. Caller must ensure that\n    /// the name is valid.\n    pub fn set_name(&mut self, name: CustomIdent) {\n        debug_assert!(is_valid_name_definition(&name));\n        self.name = name;\n    }\n\n    /// Get the current generation of the counter style rule.\n    pub fn generation(&self) -> u32 {\n        self.generation.0\n    }\n\n    /// Get the system of this counter style rule, default to\n    /// `symbolic` if not specified.\n    pub fn resolved_system(&self) -> &System {\n        match self.descriptors.system {\n            Some(ref system) => system,\n            None => &System::Symbolic,\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-system>\n#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]\npub enum System {\n    /// 'cyclic'\n    Cyclic,\n    /// 'numeric'\n    Numeric,\n    /// 'alphabetic'\n    Alphabetic,\n    /// 'symbolic'\n    Symbolic,\n    /// 'additive'\n    Additive,\n    /// 'fixed <integer>?'\n    Fixed {\n        /// '<integer>?'\n        first_symbol_value: Option<Integer>,\n    },\n    /// 'extends <counter-style-name>'\n    Extends(CustomIdent),\n}\n\nimpl Parse for System {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        try_match_ident_ignore_ascii_case! { input,\n            \"cyclic\" => Ok(System::Cyclic),\n            \"numeric\" => Ok(System::Numeric),\n            \"alphabetic\" => Ok(System::Alphabetic),\n            \"symbolic\" => Ok(System::Symbolic),\n            \"additive\" => Ok(System::Additive),\n            \"fixed\" => {\n                let first_symbol_value = input.try_parse(|i| Integer::parse(context, i)).ok();\n                Ok(System::Fixed { first_symbol_value })\n            },\n            \"extends\" => {\n                let other = parse_counter_style_name(input)?;\n                Ok(System::Extends(other))\n            },\n        }\n    }\n}\n\nimpl ToCss for System {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            System::Cyclic => dest.write_str(\"cyclic\"),\n            System::Numeric => dest.write_str(\"numeric\"),\n            System::Alphabetic => dest.write_str(\"alphabetic\"),\n            System::Symbolic => dest.write_str(\"symbolic\"),\n            System::Additive => dest.write_str(\"additive\"),\n            System::Fixed { first_symbol_value } => {\n                if let Some(value) = first_symbol_value {\n                    dest.write_str(\"fixed \")?;\n                    value.to_css(dest)\n                } else {\n                    dest.write_str(\"fixed\")\n                }\n            },\n            System::Extends(ref other) => {\n                dest.write_str(\"extends \")?;\n                other.to_css(dest)\n            },\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>\n#[derive(\n    Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,\n)]\n#[repr(u8)]\npub enum Symbol {\n    /// <string>\n    String(crate::OwnedStr),\n    /// <custom-ident>\n    Ident(CustomIdent),\n    // Not implemented:\n    // /// <image>\n    // Image(Image),\n}\n\nimpl Parse for Symbol {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::QuotedString(ref s) => Ok(Symbol::String(s.as_ref().to_owned().into())),\n            Token::Ident(ref s) => Ok(Symbol::Ident(CustomIdent::from_ident(location, s, &[])?)),\n            ref t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n}\n\nimpl Symbol {\n    /// Returns whether this symbol is allowed in symbols() function.\n    pub fn is_allowed_in_symbols(&self) -> bool {\n        match self {\n            // Identifier is not allowed.\n            &Symbol::Ident(_) => false,\n            _ => true,\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-negative>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub struct Negative(pub Symbol, pub Option<Symbol>);\n\nimpl Parse for Negative {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Negative(\n            Symbol::parse(context, input)?,\n            input.try_parse(|input| Symbol::parse(context, input)).ok(),\n        ))\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub struct CounterRange {\n    /// The start of the range.\n    pub start: CounterBound,\n    /// The end of the range.\n    pub end: CounterBound,\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-range>\n///\n/// Empty represents 'auto'\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\n#[css(comma)]\npub struct CounterRanges(#[css(iterable, if_empty = \"auto\")] pub crate::OwnedSlice<CounterRange>);\n\n/// A bound found in `CounterRanges`.\n#[derive(Clone, Copy, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub enum CounterBound {\n    /// An integer bound.\n    Integer(Integer),\n    /// The infinite bound.\n    Infinite,\n}\n\nimpl Parse for CounterRanges {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"auto\"))\n            .is_ok()\n        {\n            return Ok(CounterRanges(Default::default()));\n        }\n\n        let ranges = input.parse_comma_separated(|input| {\n            let start = parse_bound(context, input)?;\n            let end = parse_bound(context, input)?;\n            if let (CounterBound::Integer(start), CounterBound::Integer(end)) = (start, end) {\n                if start > end {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n            }\n            Ok(CounterRange { start, end })\n        })?;\n\n        Ok(CounterRanges(ranges.into()))\n    }\n}\n\nfn parse_bound<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<CounterBound, ParseError<'i>> {\n    if let Ok(integer) = input.try_parse(|input| Integer::parse(context, input)) {\n        return Ok(CounterBound::Integer(integer));\n    }\n    input.expect_ident_matching(\"infinite\")?;\n    Ok(CounterBound::Infinite)\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-pad>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub struct Pad(pub Integer, pub Symbol);\n\nimpl Parse for Pad {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let pad_with = input.try_parse(|input| Symbol::parse(context, input));\n        let min_length = Integer::parse_non_negative(context, input)?;\n        let pad_with = pad_with.or_else(|_| Symbol::parse(context, input))?;\n        Ok(Pad(min_length, pad_with))\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-fallback>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub struct Fallback(pub CustomIdent);\n\nimpl Parse for Fallback {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Fallback(parse_counter_style_name(input)?))\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>\n#[derive(\n    Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToCss, ToShmem,\n)]\n#[repr(C)]\npub struct Symbols(\n    #[css(iterable)]\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub crate::ArcSlice<Symbol>,\n);\n\nimpl Parse for Symbols {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut symbols = smallvec::SmallVec::<[_; 5]>::new();\n        while let Ok(s) = input.try_parse(|input| Symbol::parse(context, input)) {\n            symbols.push(s);\n        }\n        if symbols.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(Symbols(crate::ArcSlice::from_iter(symbols.drain(..))))\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\n#[css(comma)]\npub struct AdditiveSymbols(#[css(iterable)] pub crate::OwnedSlice<AdditiveTuple>);\n\nimpl Parse for AdditiveSymbols {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let tuples = Vec::<AdditiveTuple>::parse(context, input)?;\n        // FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220\n        if tuples\n            .windows(2)\n            .any(|window| window[0].weight <= window[1].weight)\n        {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(AdditiveSymbols(tuples.into()))\n    }\n}\n\n/// <integer> && <symbol>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToShmem, PartialEq)]\npub struct AdditiveTuple {\n    /// <integer>\n    pub weight: Integer,\n    /// <symbol>\n    pub symbol: Symbol,\n}\n\nimpl OneOrMoreSeparated for AdditiveTuple {\n    type S = Comma;\n}\n\nimpl Parse for AdditiveTuple {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let symbol = input.try_parse(|input| Symbol::parse(context, input));\n        let weight = Integer::parse_non_negative(context, input)?;\n        let symbol = symbol.or_else(|_| Symbol::parse(context, input))?;\n        Ok(Self { weight, symbol })\n    }\n}\n\n/// <https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as>\n#[derive(Clone, Debug, MallocSizeOf, ToCss, PartialEq, ToShmem)]\npub enum SpeakAs {\n    /// auto\n    Auto,\n    /// bullets\n    Bullets,\n    /// numbers\n    Numbers,\n    /// words\n    Words,\n    // /// spell-out, not supported, see bug 1024178\n    // SpellOut,\n    /// <counter-style-name>\n    Other(CustomIdent),\n}\n\nimpl Parse for SpeakAs {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut is_spell_out = false;\n        let result = input.try_parse(|input| {\n            let ident = input.expect_ident().map_err(|_| ())?;\n            match_ignore_ascii_case! { &*ident,\n                \"auto\" => Ok(SpeakAs::Auto),\n                \"bullets\" => Ok(SpeakAs::Bullets),\n                \"numbers\" => Ok(SpeakAs::Numbers),\n                \"words\" => Ok(SpeakAs::Words),\n                \"spell-out\" => {\n                    is_spell_out = true;\n                    Err(())\n                },\n                _ => Err(()),\n            }\n        });\n        if is_spell_out {\n            // spell-out is not supported, but don’t parse it as a <counter-style-name>.\n            // See bug 1024178.\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        result.or_else(|_| Ok(SpeakAs::Other(parse_counter_style_name(input)?)))\n    }\n}\n"
  },
  {
    "path": "style/counter_style/predefined.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\npredefined! {\n    \"decimal\",\n    \"decimal-leading-zero\",\n    \"arabic-indic\",\n    \"armenian\",\n    \"upper-armenian\",\n    \"lower-armenian\",\n    \"bengali\",\n    \"cambodian\",\n    \"khmer\",\n    \"cjk-decimal\",\n    \"devanagari\",\n    \"georgian\",\n    \"gujarati\",\n    \"gurmukhi\",\n    \"hebrew\",\n    \"kannada\",\n    \"lao\",\n    \"malayalam\",\n    \"mongolian\",\n    \"myanmar\",\n    \"oriya\",\n    \"persian\",\n    \"lower-roman\",\n    \"upper-roman\",\n    \"tamil\",\n    \"telugu\",\n    \"thai\",\n    \"tibetan\",\n    \"lower-alpha\",\n    \"lower-latin\",\n    \"upper-alpha\",\n    \"upper-latin\",\n    \"cjk-earthly-branch\",\n    \"cjk-heavenly-stem\",\n    \"lower-greek\",\n    \"hiragana\",\n    \"hiragana-iroha\",\n    \"katakana\",\n    \"katakana-iroha\",\n    \"disc\",\n    \"circle\",\n    \"square\",\n    \"disclosure-open\",\n    \"disclosure-closed\",\n    \"japanese-informal\",\n    \"japanese-formal\",\n    \"korean-hangul-formal\",\n    \"korean-hanja-informal\",\n    \"korean-hanja-formal\",\n    \"simp-chinese-informal\",\n    \"simp-chinese-formal\",\n    \"trad-chinese-informal\",\n    \"trad-chinese-formal\",\n    \"cjk-ideographic\",\n    \"ethiopic-numeric\",\n}\n"
  },
  {
    "path": "style/counter_style/update_predefined.py",
    "content": "#!/usr/bin/env python\n\n# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nfrom os.path import join, dirname\nimport re\nfrom urllib.request import urlopen\n\n\ndef main(filename):\n    names = [\n        re.search('>([^>]+)(</dfn>|<a class=\"self-link\")', line.decode()).group(1)\n        for line in urlopen(\"https://drafts.csswg.org/css-counter-styles/\")\n        if b'data-dfn-for=\"<counter-style-name>\"' in line\n        or b'data-dfn-for=\"<counter-style>\"' in line\n    ]\n    with open(filename, \"w\") as f:\n        f.write(\n            \"\"\"\\\n/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\npredefined! {\n\"\"\"\n        )\n        for name in names:\n            f.write('    \"%s\",\\n' % name)\n        f.write(\"}\\n\")\n\n\nif __name__ == \"__main__\":\n    main(join(dirname(__file__), \"predefined.rs\"))\n"
  },
  {
    "path": "style/custom_properties.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Support for [custom properties for cascading variables][custom].\n//!\n//! [custom]: https://drafts.csswg.org/css-variables/\n\nuse crate::applicable_declarations::{CascadePriority, RevertKind};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::custom_properties_map::{AllSubstitutionFunctions, CustomPropertiesMap, OwnMap};\nuse crate::device::Device;\nuse crate::dom::AttributeTracker;\nuse crate::properties::{\n    CSSWideKeyword, CustomDeclaration, CustomDeclarationValue, LonghandId, LonghandIdSet,\n    PropertyDeclaration,\n};\nuse crate::properties_and_values::{\n    rule::Descriptors as PropertyDescriptors,\n    syntax::{data_type::DependentDataTypes, Descriptor as SyntaxDescriptor},\n    value::{\n        AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,\n        SpecifiedValue as SpecifiedRegisteredValue,\n    },\n};\nuse crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};\nuse crate::stylesheets::UrlExtraData;\nuse crate::stylist::Stylist;\nuse crate::values::computed::{self, ToComputedValue};\nuse crate::values::generics::calc::SortKey as AttrUnit;\nuse crate::values::specified::FontRelativeLength;\nuse crate::values::specified::ParsedNamespace;\nuse crate::{derives::*, Namespace, Prefix};\nuse crate::{Atom, LocalName};\nuse cssparser::{\n    CowRcStr, Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType,\n};\nuse rustc_hash::FxHashMap;\nuse selectors::parser::SelectorParseErrorKind;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::borrow::Cow;\nuse std::collections::hash_map::Entry;\nuse std::fmt::{self, Write};\nuse std::ops::{Index, IndexMut};\nuse std::{cmp, num};\nuse style_traits::{\n    CssString, CssWriter, ParseError, StyleParseErrorKind, ToCss, ToTyped, TypedValue,\n    UnparsedSegment, UnparsedValue, VariableReferenceValue,\n};\nuse thin_vec::ThinVec;\n\n/// The environment from which to get `env` function values.\n///\n/// TODO(emilio): If this becomes a bit more complex we should probably move it\n/// to the `media_queries` module, or something.\n#[derive(Debug, MallocSizeOf)]\npub struct CssEnvironment;\n\ntype EnvironmentEvaluator = fn(device: &Device, url_data: &UrlExtraData) -> VariableValue;\n\nstruct EnvironmentVariable {\n    name: Atom,\n    evaluator: EnvironmentEvaluator,\n}\n\nmacro_rules! make_variable {\n    ($name:expr, $evaluator:expr) => {{\n        EnvironmentVariable {\n            name: $name,\n            evaluator: $evaluator,\n        }\n    }};\n}\n\nfn get_safearea_inset_top(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(device.safe_area_insets().top, url_data)\n}\n\nfn get_safearea_inset_bottom(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(device.safe_area_insets().bottom, url_data)\n}\n\nfn get_safearea_inset_left(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(device.safe_area_insets().left, url_data)\n}\n\nfn get_safearea_inset_right(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(device.safe_area_insets().right, url_data)\n}\n\n#[cfg(feature = \"gecko\")]\nfn get_content_preferred_color_scheme(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    use crate::queries::values::PrefersColorScheme;\n    let prefers_color_scheme = unsafe {\n        crate::gecko_bindings::bindings::Gecko_MediaFeatures_PrefersColorScheme(\n            device.document(),\n            /* use_content = */ true,\n        )\n    };\n    VariableValue::ident(\n        match prefers_color_scheme {\n            PrefersColorScheme::Light => \"light\",\n            PrefersColorScheme::Dark => \"dark\",\n        },\n        url_data,\n    )\n}\n\n#[cfg(feature = \"servo\")]\nfn get_content_preferred_color_scheme(_device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    // TODO: Add an implementation for Servo.\n    VariableValue::ident(\"light\", url_data)\n}\n\nfn get_scrollbar_inline_size(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(device.scrollbar_inline_size().px(), url_data)\n}\n\nfn get_hairline(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    VariableValue::pixels(\n        app_units::Au(device.app_units_per_device_pixel()).to_f32_px(),\n        url_data,\n    )\n}\n\nstatic ENVIRONMENT_VARIABLES: [EnvironmentVariable; 4] = [\n    make_variable!(atom!(\"safe-area-inset-top\"), get_safearea_inset_top),\n    make_variable!(atom!(\"safe-area-inset-bottom\"), get_safearea_inset_bottom),\n    make_variable!(atom!(\"safe-area-inset-left\"), get_safearea_inset_left),\n    make_variable!(atom!(\"safe-area-inset-right\"), get_safearea_inset_right),\n];\n\n#[cfg(feature = \"gecko\")]\nmacro_rules! lnf_int {\n    ($id:ident) => {\n        unsafe {\n            crate::gecko_bindings::bindings::Gecko_GetLookAndFeelInt(\n                crate::gecko_bindings::bindings::LookAndFeel_IntID::$id as i32,\n            )\n        }\n    };\n}\n\n#[cfg(feature = \"servo\")]\nmacro_rules! lnf_int {\n    ($id:ident) => {\n        // TODO: Add an implementation for Servo.\n        0\n    };\n}\n\nmacro_rules! lnf_int_variable {\n    ($atom:expr, $id:ident, $ctor:ident) => {{\n        fn __eval(_: &Device, url_data: &UrlExtraData) -> VariableValue {\n            VariableValue::$ctor(lnf_int!($id), url_data)\n        }\n        make_variable!($atom, __eval)\n    }};\n}\n\nfn eval_gtk_csd_titlebar_radius(device: &Device, url_data: &UrlExtraData) -> VariableValue {\n    let int_pixels = lnf_int!(TitlebarRadius);\n    let unzoomed_scale =\n        device.device_pixel_ratio_ignoring_full_zoom().get() / device.device_pixel_ratio().get();\n    VariableValue::pixels(int_pixels as f32 * unzoomed_scale, url_data)\n}\n\nstatic CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 9] = [\n    make_variable!(\n        atom!(\"-moz-gtk-csd-titlebar-radius\"),\n        eval_gtk_csd_titlebar_radius\n    ),\n    lnf_int_variable!(\n        atom!(\"-moz-gtk-csd-tooltip-radius\"),\n        TooltipRadius,\n        int_pixels\n    ),\n    lnf_int_variable!(\n        atom!(\"-moz-gtk-csd-close-button-position\"),\n        GTKCSDCloseButtonPosition,\n        integer\n    ),\n    lnf_int_variable!(\n        atom!(\"-moz-gtk-csd-minimize-button-position\"),\n        GTKCSDMinimizeButtonPosition,\n        integer\n    ),\n    lnf_int_variable!(\n        atom!(\"-moz-gtk-csd-maximize-button-position\"),\n        GTKCSDMaximizeButtonPosition,\n        integer\n    ),\n    lnf_int_variable!(\n        atom!(\"-moz-overlay-scrollbar-fade-duration\"),\n        ScrollbarFadeDuration,\n        int_ms\n    ),\n    make_variable!(\n        atom!(\"-moz-content-preferred-color-scheme\"),\n        get_content_preferred_color_scheme\n    ),\n    make_variable!(atom!(\"scrollbar-inline-size\"), get_scrollbar_inline_size),\n    make_variable!(atom!(\"hairline\"), get_hairline),\n];\n\nimpl CssEnvironment {\n    #[inline]\n    fn get(&self, name: &Atom, device: &Device, url_data: &UrlExtraData) -> Option<VariableValue> {\n        if let Some(var) = ENVIRONMENT_VARIABLES.iter().find(|var| var.name == *name) {\n            return Some((var.evaluator)(device, url_data));\n        }\n        if !url_data.chrome_rules_enabled() {\n            return None;\n        }\n        let var = CHROME_ENVIRONMENT_VARIABLES\n            .iter()\n            .find(|var| var.name == *name)?;\n        Some((var.evaluator)(device, url_data))\n    }\n}\n\n/// A custom property name is just an `Atom`.\n///\n/// Note that this does not include the `--` prefix\npub type Name = Atom;\n\n/// Parse a custom property name.\n///\n/// <https://drafts.csswg.org/css-variables/#typedef-custom-property-name>\npub fn parse_name(s: &str) -> Result<&str, ()> {\n    if s.starts_with(\"--\") && s.len() > 2 {\n        Ok(&s[2..])\n    } else {\n        Err(())\n    }\n}\n\n/// A value for a custom property is just a set of tokens.\n///\n/// We preserve the original CSS for serialization, and also the variable\n/// references to other custom property names.\n#[derive(Clone, Debug, MallocSizeOf, ToShmem)]\npub struct VariableValue {\n    /// The raw CSS string.\n    pub css: String,\n\n    /// The url data of the stylesheet where this value came from.\n    pub url_data: UrlExtraData,\n\n    first_token_type: TokenSerializationType,\n    last_token_type: TokenSerializationType,\n\n    /// var(), env(), attr() or non-custom property (e.g. through `em`) references.\n    references: References,\n}\n\ntrivial_to_computed_value!(VariableValue);\n\n/// Given a potentially registered variable value turn it into a computed custom property value.\npub fn compute_variable_value(\n    value: &Arc<VariableValue>,\n    registration: &PropertyDescriptors,\n    computed_context: &computed::Context,\n) -> Option<ComputedRegisteredValue> {\n    if registration.is_universal() {\n        return Some(ComputedRegisteredValue::universal(Arc::clone(value)));\n    }\n    compute_value(\n        &value.css,\n        &value.url_data,\n        registration,\n        computed_context,\n        AttrTaint::default(),\n    )\n    .ok()\n}\n\n// For all purposes, we want values to be considered equal if their css text is equal.\nimpl PartialEq for VariableValue {\n    fn eq(&self, other: &Self) -> bool {\n        self.css == other.css\n    }\n}\n\nimpl Eq for VariableValue {}\n\nimpl ToCss for SpecifiedValue {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(&self.css)\n    }\n}\n\nimpl ToTyped for SpecifiedValue {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        let unparsed_value = reify_variable_value(self)?;\n        dest.push(TypedValue::Unparsed(unparsed_value));\n        Ok(())\n    }\n}\n\nfn reify_variable_value(value: &VariableValue) -> Result<UnparsedValue, ()> {\n    let mut reference_index = 0;\n    reify_variable_value_range(\n        &value.css,\n        &value.references.refs,\n        &mut reference_index,\n        0,\n        value.css.len(),\n    )\n}\n\n/// Reify a slice of the CSS string into UnparsedSegment entries.\n///\n/// References are stored in source order, with outer substitution functions\n/// inserted before references in their fallback. The shared `reference_index`\n/// relies on this ordering to recurse into fallbacks without reprocessing\n/// nested referecences.\nfn reify_variable_value_range(\n    css: &str,\n    references: &[SubstitutionFunctionReference],\n    reference_index: &mut usize,\n    start: usize,\n    end: usize,\n) -> Result<UnparsedValue, ()> {\n    debug_assert!(start <= end);\n    debug_assert!(end <= css.len());\n\n    let mut values = ThinVec::new();\n    let mut cur_pos = start;\n\n    while *reference_index < references.len() {\n        let reference = &references[*reference_index];\n\n        if reference.start >= end {\n            break;\n        }\n\n        debug_assert!(reference.start >= cur_pos);\n        debug_assert!(reference.start <= reference.end);\n        debug_assert!(reference.end <= css.len());\n\n        if cur_pos < reference.start {\n            values.push(UnparsedSegment::String(CssString::from(\n                &css[cur_pos..reference.start],\n            )));\n        }\n\n        *reference_index += 1;\n\n        if reference.substitution_kind != SubstitutionFunctionKind::Var {\n            return Err(());\n        }\n\n        let (fallback, has_fallback) = if let Some(fallback) = &reference.fallback {\n            debug_assert!(fallback.start.get() <= reference.end - 1);\n\n            (\n                reify_variable_value_range(\n                    css,\n                    references,\n                    reference_index,\n                    fallback.start.get(),\n                    reference.end - 1, // Skip the closing ')'.\n                )?,\n                true,\n            )\n        } else {\n            (ThinVec::new(), false)\n        };\n\n        values.push(UnparsedSegment::VariableReference(VariableReferenceValue {\n            variable: CssString::from(format!(\"--{}\", reference.name)),\n            fallback,\n            has_fallback,\n        }));\n\n        cur_pos = reference.end;\n    }\n\n    if cur_pos < end {\n        values.push(UnparsedSegment::String(CssString::from(&css[cur_pos..end])));\n    }\n\n    Ok(values)\n}\n\n/// A pair of separate CustomPropertiesMaps, split between custom properties\n/// that have the inherit flag set and those with the flag unset.\n#[repr(C)]\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct ComputedCustomProperties {\n    /// Map for custom properties with inherit flag set, including non-registered\n    /// ones.\n    pub inherited: CustomPropertiesMap,\n    /// Map for custom properties with inherit flag unset.\n    pub non_inherited: CustomPropertiesMap,\n}\n\nimpl ComputedCustomProperties {\n    /// Return whether the inherited and non_inherited maps are none.\n    pub fn is_empty(&self) -> bool {\n        self.inherited.is_empty() && self.non_inherited.is_empty()\n    }\n\n    /// Return the name and value of the property at specified index, if any.\n    pub fn property_at(&self, index: usize) -> Option<(&Name, &Option<ComputedRegisteredValue>)> {\n        // Just expose the custom property items from custom_properties.inherited, followed\n        // by custom property items from custom_properties.non_inherited.\n        self.inherited\n            .get_index(index)\n            .or_else(|| self.non_inherited.get_index(index - self.inherited.len()))\n    }\n\n    /// Insert a custom property in the corresponding inherited/non_inherited\n    /// map, depending on whether the inherit flag is set or unset.\n    pub(crate) fn insert(\n        &mut self,\n        registration: &PropertyDescriptors,\n        name: &Name,\n        value: ComputedRegisteredValue,\n    ) {\n        self.map_mut(registration).insert(name, value)\n    }\n\n    /// Remove a custom property from the corresponding inherited/non_inherited\n    /// map, depending on whether the inherit flag is set or unset.\n    pub(crate) fn remove(&mut self, registration: &PropertyDescriptors, name: &Name) {\n        self.map_mut(registration).remove(name);\n    }\n\n    /// Shrink the capacity of the inherited maps as much as possible.\n    fn shrink_to_fit(&mut self) {\n        self.inherited.shrink_to_fit();\n        self.non_inherited.shrink_to_fit();\n    }\n\n    fn map_mut(&mut self, registration: &PropertyDescriptors) -> &mut CustomPropertiesMap {\n        if registration.inherits() {\n            &mut self.inherited\n        } else {\n            &mut self.non_inherited\n        }\n    }\n\n    /// Returns the relevant custom property value given a registration.\n    pub fn get(\n        &self,\n        registration: &PropertyDescriptors,\n        name: &Name,\n    ) -> Option<&ComputedRegisteredValue> {\n        if registration.inherits() {\n            self.inherited.get(name)\n        } else {\n            self.non_inherited.get(name)\n        }\n    }\n}\n\n/// Both specified and computed values are VariableValues, the difference is\n/// whether var() functions are expanded.\npub type SpecifiedValue = VariableValue;\n/// Both specified and computed values are VariableValues, the difference is\n/// whether var() functions are expanded.\npub type ComputedValue = VariableValue;\n\n/// Set of flags to non-custom references this custom property makes.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, MallocSizeOf, ToShmem)]\nstruct NonCustomReferences(u8);\n\nbitflags! {\n    impl NonCustomReferences: u8 {\n        /// At least one custom property depends on font-relative units.\n        const FONT_UNITS = 1 << 0;\n        /// At least one custom property depends on root element's font-relative units.\n        const ROOT_FONT_UNITS = 1 << 1;\n        /// At least one custom property depends on line height units.\n        const LH_UNITS = 1 << 2;\n        /// At least one custom property depends on root element's line height units.\n        const ROOT_LH_UNITS = 1 << 3;\n        /// All dependencies not depending on the root element.\n        const NON_ROOT_DEPENDENCIES = Self::FONT_UNITS.0 | Self::LH_UNITS.0;\n        /// All dependencies depending on the root element.\n        const ROOT_DEPENDENCIES = Self::ROOT_FONT_UNITS.0 | Self::ROOT_LH_UNITS.0;\n    }\n}\n\nimpl NonCustomReferences {\n    fn for_each<F>(&self, mut f: F)\n    where\n        F: FnMut(SingleNonCustomReference),\n    {\n        for (_, r) in self.iter_names() {\n            let single = match r {\n                Self::FONT_UNITS => SingleNonCustomReference::FontUnits,\n                Self::ROOT_FONT_UNITS => SingleNonCustomReference::RootFontUnits,\n                Self::LH_UNITS => SingleNonCustomReference::LhUnits,\n                Self::ROOT_LH_UNITS => SingleNonCustomReference::RootLhUnits,\n                _ => unreachable!(\"Unexpected single bit value\"),\n            };\n            f(single);\n        }\n    }\n\n    fn from_unit(value: &CowRcStr) -> Self {\n        // For registered properties, any reference to font-relative dimensions\n        // make it dependent on font-related properties.\n        // TODO(dshin): When we unit algebra gets implemented and handled -\n        // Is it valid to say that `calc(1em / 2em * 3px)` triggers this?\n        if value.eq_ignore_ascii_case(FontRelativeLength::LH) {\n            return Self::FONT_UNITS | Self::LH_UNITS;\n        }\n        if value.eq_ignore_ascii_case(FontRelativeLength::EM)\n            || value.eq_ignore_ascii_case(FontRelativeLength::EX)\n            || value.eq_ignore_ascii_case(FontRelativeLength::CAP)\n            || value.eq_ignore_ascii_case(FontRelativeLength::CH)\n            || value.eq_ignore_ascii_case(FontRelativeLength::IC)\n        {\n            return Self::FONT_UNITS;\n        }\n        if value.eq_ignore_ascii_case(FontRelativeLength::RLH) {\n            return Self::ROOT_FONT_UNITS | Self::ROOT_LH_UNITS;\n        }\n        if value.eq_ignore_ascii_case(FontRelativeLength::REM)\n            || value.eq_ignore_ascii_case(FontRelativeLength::REX)\n            || value.eq_ignore_ascii_case(FontRelativeLength::RCH)\n            || value.eq_ignore_ascii_case(FontRelativeLength::RCAP)\n            || value.eq_ignore_ascii_case(FontRelativeLength::RIC)\n        {\n            return Self::ROOT_FONT_UNITS;\n        }\n        Self::empty()\n    }\n}\n\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum SingleNonCustomReference {\n    FontUnits = 0,\n    RootFontUnits,\n    LhUnits,\n    RootLhUnits,\n}\n\nstruct NonCustomReferenceMap<T>([Option<T>; 4]);\n\nimpl<T> Default for NonCustomReferenceMap<T> {\n    fn default() -> Self {\n        NonCustomReferenceMap(Default::default())\n    }\n}\n\nimpl<T> Index<SingleNonCustomReference> for NonCustomReferenceMap<T> {\n    type Output = Option<T>;\n\n    fn index(&self, reference: SingleNonCustomReference) -> &Self::Output {\n        &self.0[reference as usize]\n    }\n}\n\nimpl<T> IndexMut<SingleNonCustomReference> for NonCustomReferenceMap<T> {\n    fn index_mut(&mut self, reference: SingleNonCustomReference) -> &mut Self::Output {\n        &mut self.0[reference as usize]\n    }\n}\n\n/// Whether to defer resolving custom properties referencing font relative units.\n#[derive(Clone, Copy, PartialEq, Eq)]\n#[allow(missing_docs)]\npub enum DeferFontRelativeCustomPropertyResolution {\n    Yes,\n    No,\n}\n\n/// Substitution function source: var, env, attr.\n#[derive(Copy, Clone, Debug, MallocSizeOf, Hash, Eq, PartialEq, ToShmem, Parse)]\npub enum SubstitutionFunctionKind {\n    /// CSS variable / custom property\n    Var,\n    /// Environment variable\n    Env,\n    /// DOM attribute\n    Attr,\n}\n\n/// A wrapper map that encapsulates both the custom properties and attributes\n/// for a given element.\n#[repr(C)]\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct ComputedSubstitutionFunctions {\n    /// The applicable custom properties (includes inherited and non-inherited).\n    pub custom_properties: ComputedCustomProperties,\n    /// The applicable DOM attributes.\n    pub attributes: OwnMap,\n}\n\nimpl ComputedSubstitutionFunctions {\n    /// Creates a substitution function map from optional custom properties\n    /// and DOM attributes.\n    #[inline(always)]\n    pub fn new(\n        custom_properties: Option<ComputedCustomProperties>,\n        attributes: Option<OwnMap>,\n    ) -> Self {\n        Self {\n            custom_properties: custom_properties.unwrap_or_default(),\n            attributes: attributes.unwrap_or_default(),\n        }\n    }\n\n    #[inline(always)]\n    fn insert_var(\n        &mut self,\n        registration: &PropertyDescriptors,\n        name: &Name,\n        value: ComputedRegisteredValue,\n    ) {\n        self.custom_properties.insert(registration, name, value);\n    }\n\n    #[inline(always)]\n    fn insert_attr(&mut self, name: &Name, value: ComputedRegisteredValue) {\n        self.attributes.insert(name.clone(), Some(value));\n    }\n\n    #[inline(always)]\n    fn remove_var(&mut self, registration: &PropertyDescriptors, name: &Name) {\n        self.custom_properties.remove(registration, name);\n    }\n\n    #[inline(always)]\n    fn remove_attr(&mut self, name: &Name) {\n        self.attributes.insert(name.clone(), None);\n    }\n\n    #[inline(always)]\n    fn get_var(\n        &self,\n        registration: &PropertyDescriptors,\n        name: &Name,\n    ) -> Option<&ComputedRegisteredValue> {\n        self.custom_properties.get(registration, name)\n    }\n\n    #[inline(always)]\n    fn get_attr(&self, name: &Name) -> Option<&ComputedRegisteredValue> {\n        self.attributes.get(name).and_then(|p| p.as_ref())\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, Parse)]\nenum AttributeType {\n    None,\n    RawString,\n    Type(SyntaxDescriptor),\n    Unit(AttrUnit),\n}\n\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\nstruct AttributeData {\n    kind: AttributeType,\n    namespace: ParsedNamespace,\n}\n\n/// For a CSS string, the range, counted in bytes, that is attr()-tainted.\n#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem, ToComputedValue)]\npub struct AttrTaintedRange {\n    /// Start of the range, counted in bytes. Inclusive.\n    start: usize,\n    /// End of the range, counted in bytes. Exclusive.\n    end: usize,\n}\n\nimpl AttrTaintedRange {\n    /// Creates a range within a CSS string that is tainted by attr().\n    #[inline(always)]\n    pub fn new(start: usize, end: usize) -> Self {\n        debug_assert!(start <= end);\n        Self { start, end }\n    }\n}\n\n/// In CSS Values and Units, values produced by `attr()` are considered attr()-tainted, as are\n/// functions that contain an attr()-tainted value. Using an attr()-tainted value as or in a <url>\n/// makes a declaration invalid at computed-value time.\n/// https://drafts.csswg.org/css-values-5/#attr-security\n#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]\npub struct AttrTaint(SmallVec<[AttrTaintedRange; 1]>);\n\nimpl AttrTaint {\n    /// For a CSS string, determine whether any `<url>` overlapping this `range`\n    /// is disallowed due to attr()-tainting.\n    #[inline(always)]\n    pub fn should_disallow_urls_in_range(&self, range: &AttrTaintedRange) -> bool {\n        self.0\n            .iter()\n            .any(|r| r.start <= range.end && r.end >= range.start)\n    }\n\n    /// Returns true if the attr()-tainted range contains no elements.\n    #[inline(always)]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    #[inline(always)]\n    fn new_fully_tainted(end: usize) -> Self {\n        let mut taint = Self::default();\n        taint.push(0, end);\n        taint\n    }\n\n    #[inline(always)]\n    fn push(&mut self, start: usize, end: usize) {\n        self.0.push(AttrTaintedRange::new(start, end));\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\nstruct VariableFallback {\n    // NOTE(emilio): We don't track fallback end, because we rely on the missing closing\n    // parenthesis, if any, to be inserted, which means that we can rely on our end being\n    // reference.end - 1.\n    start: num::NonZeroUsize,\n    first_token_type: TokenSerializationType,\n    last_token_type: TokenSerializationType,\n}\n\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\nstruct SubstitutionFunctionReference {\n    name: Name,\n    start: usize,\n    end: usize,\n    fallback: Option<VariableFallback>,\n    attribute_data: AttributeData,\n    prev_token_type: TokenSerializationType,\n    next_token_type: TokenSerializationType,\n    substitution_kind: SubstitutionFunctionKind,\n}\n\n/// A struct holding information about the external references to that a custom\n/// property value may have.\n#[derive(Clone, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]\nstruct References {\n    refs: Vec<SubstitutionFunctionReference>,\n    non_custom_references: NonCustomReferences,\n    any_env: bool,\n    any_var: bool,\n    any_attr: bool,\n}\n\nimpl References {\n    fn has_references(&self) -> bool {\n        !self.refs.is_empty()\n    }\n\n    fn non_custom_references(&self, is_root_element: bool) -> NonCustomReferences {\n        let mut mask = NonCustomReferences::NON_ROOT_DEPENDENCIES;\n        if is_root_element {\n            mask |= NonCustomReferences::ROOT_DEPENDENCIES\n        }\n        self.non_custom_references & mask\n    }\n}\n\nimpl VariableValue {\n    fn empty(url_data: &UrlExtraData) -> Self {\n        Self {\n            css: String::new(),\n            last_token_type: Default::default(),\n            first_token_type: Default::default(),\n            url_data: url_data.clone(),\n            references: Default::default(),\n        }\n    }\n\n    /// Create a new custom property without parsing if the CSS is known to be valid and contain no\n    /// references.\n    pub fn new(\n        css: String,\n        url_data: &UrlExtraData,\n        first_token_type: TokenSerializationType,\n        last_token_type: TokenSerializationType,\n    ) -> Self {\n        Self {\n            css,\n            url_data: url_data.clone(),\n            first_token_type,\n            last_token_type,\n            references: References::default(),\n        }\n    }\n\n    fn push<'i>(\n        &mut self,\n        css: &str,\n        css_first_token_type: TokenSerializationType,\n        css_last_token_type: TokenSerializationType,\n        attr_taint: Option<&mut AttrTaint>,\n    ) -> Result<(), ()> {\n        /// Prevent values from getting terribly big since you can use custom\n        /// properties exponentially.\n        ///\n        /// This number (2MB) is somewhat arbitrary, but silly enough that no\n        /// reasonable page should hit it. We could limit by number of total\n        /// substitutions, but that was very easy to work around in practice\n        /// (just choose a larger initial value and boom).\n        const MAX_VALUE_LENGTH_IN_BYTES: usize = 2 * 1024 * 1024;\n\n        if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES {\n            return Err(());\n        }\n\n        // This happens e.g. between two subsequent var() functions:\n        // `var(--a)var(--b)`.\n        //\n        // In that case, css_*_token_type is nonsensical.\n        if css.is_empty() {\n            return Ok(());\n        }\n\n        self.first_token_type.set_if_nothing(css_first_token_type);\n        // If self.first_token_type was nothing,\n        // self.last_token_type is also nothing and this will be false:\n        if self\n            .last_token_type\n            .needs_separator_when_before(css_first_token_type)\n        {\n            self.css.push_str(\"/**/\")\n        }\n        let start = self.css.len();\n        self.css.push_str(css);\n        let end = self.css.len();\n        if let Some(taint) = attr_taint {\n            taint.push(start, end);\n        }\n        self.last_token_type = css_last_token_type;\n        Ok(())\n    }\n\n    /// Parse a custom property value.\n    pub fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        namespaces: Option<&FxHashMap<Prefix, Namespace>>,\n        url_data: &UrlExtraData,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut references = References::default();\n        let mut missing_closing_characters = String::new();\n        let start_position = input.position();\n        let (first_token_type, last_token_type) = parse_declaration_value(\n            input,\n            start_position,\n            namespaces,\n            &mut references,\n            &mut missing_closing_characters,\n        )?;\n        let mut css = input\n            .slice_from(start_position)\n            .trim_ascii_start()\n            .to_owned();\n        if !missing_closing_characters.is_empty() {\n            // Unescaped backslash at EOF in a quoted string is ignored.\n            if css.ends_with(\"\\\\\")\n                && matches!(missing_closing_characters.as_bytes()[0], b'\"' | b'\\'')\n            {\n                css.pop();\n            }\n            css.push_str(&missing_closing_characters);\n        }\n\n        css.truncate(css.trim_ascii_end().len());\n        css.shrink_to_fit();\n        references.refs.shrink_to_fit();\n\n        Ok(Self {\n            css,\n            url_data: url_data.clone(),\n            first_token_type,\n            last_token_type,\n            references,\n        })\n    }\n\n    /// Returns whether this value is tainted by `attr()`.\n    pub fn is_attr_tainted(&self) -> bool {\n        self.references.any_attr\n    }\n\n    /// Create VariableValue from an int.\n    fn integer(number: i32, url_data: &UrlExtraData) -> Self {\n        Self::from_token(\n            Token::Number {\n                has_sign: false,\n                value: number as f32,\n                int_value: Some(number),\n            },\n            url_data,\n        )\n    }\n\n    /// Create VariableValue from an int.\n    fn ident(ident: &'static str, url_data: &UrlExtraData) -> Self {\n        Self::from_token(Token::Ident(ident.into()), url_data)\n    }\n\n    /// Create VariableValue from a float amount of CSS pixels.\n    fn pixels(number: f32, url_data: &UrlExtraData) -> Self {\n        // FIXME (https://github.com/servo/rust-cssparser/issues/266):\n        // No way to get TokenSerializationType::Dimension without creating\n        // Token object.\n        Self::from_token(\n            Token::Dimension {\n                has_sign: false,\n                value: number,\n                int_value: None,\n                unit: CowRcStr::from(\"px\"),\n            },\n            url_data,\n        )\n    }\n\n    /// Create VariableValue from an integer amount of milliseconds.\n    fn int_ms(number: i32, url_data: &UrlExtraData) -> Self {\n        Self::from_token(\n            Token::Dimension {\n                has_sign: false,\n                value: number as f32,\n                int_value: Some(number),\n                unit: CowRcStr::from(\"ms\"),\n            },\n            url_data,\n        )\n    }\n\n    /// Create VariableValue from an integer amount of CSS pixels.\n    fn int_pixels(number: i32, url_data: &UrlExtraData) -> Self {\n        Self::from_token(\n            Token::Dimension {\n                has_sign: false,\n                value: number as f32,\n                int_value: Some(number),\n                unit: CowRcStr::from(\"px\"),\n            },\n            url_data,\n        )\n    }\n\n    fn from_token(token: Token, url_data: &UrlExtraData) -> Self {\n        let token_type = token.serialization_type();\n        let mut css = token.to_css_string();\n        css.shrink_to_fit();\n\n        VariableValue {\n            css,\n            url_data: url_data.clone(),\n            first_token_type: token_type,\n            last_token_type: token_type,\n            references: Default::default(),\n        }\n    }\n\n    /// Returns the raw CSS text from this VariableValue\n    pub fn css_text(&self) -> &str {\n        &self.css\n    }\n\n    /// Returns whether this variable value has any reference to the environment or other\n    /// variables.\n    pub fn has_references(&self) -> bool {\n        self.references.has_references()\n    }\n}\n\n/// <https://drafts.csswg.org/css-syntax-3/#typedef-declaration-value>\nfn parse_declaration_value<'i, 't>(\n    input: &mut Parser<'i, 't>,\n    input_start: SourcePosition,\n    namespaces: Option<&FxHashMap<Prefix, Namespace>>,\n    references: &mut References,\n    missing_closing_characters: &mut String,\n) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {\n    input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| {\n        parse_declaration_value_block(\n            input,\n            input_start,\n            namespaces,\n            references,\n            missing_closing_characters,\n        )\n    })\n}\n\n/// Like parse_declaration_value, but accept `!` and `;` since they are only invalid at the top level.\nfn parse_declaration_value_block<'i, 't>(\n    input: &mut Parser<'i, 't>,\n    input_start: SourcePosition,\n    namespaces: Option<&FxHashMap<Prefix, Namespace>>,\n    references: &mut References,\n    missing_closing_characters: &mut String,\n) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {\n    let mut is_first = true;\n    let mut first_token_type = TokenSerializationType::Nothing;\n    let mut last_token_type = TokenSerializationType::Nothing;\n    let mut prev_reference_index: Option<usize> = None;\n    loop {\n        let token_start = input.position();\n        let Ok(token) = input.next_including_whitespace_and_comments() else {\n            break;\n        };\n\n        let prev_token_type = last_token_type;\n        let serialization_type = token.serialization_type();\n        last_token_type = serialization_type;\n        if is_first {\n            first_token_type = last_token_type;\n            is_first = false;\n        }\n\n        macro_rules! nested {\n            ($closing:expr) => {{\n                let mut inner_end_position = None;\n                let result = input.parse_nested_block(|input| {\n                    let result = parse_declaration_value_block(\n                        input,\n                        input_start,\n                        namespaces,\n                        references,\n                        missing_closing_characters,\n                    )?;\n                    inner_end_position = Some(input.position());\n                    Ok(result)\n                })?;\n                if inner_end_position.unwrap() == input.position() {\n                    missing_closing_characters.push_str($closing);\n                }\n                result\n            }};\n        }\n        if let Some(index) = prev_reference_index.take() {\n            references.refs[index].next_token_type = serialization_type;\n        }\n        match *token {\n            Token::Comment(_) => {\n                let token_slice = input.slice_from(token_start);\n                if !token_slice.ends_with(\"*/\") {\n                    missing_closing_characters.push_str(if token_slice.ends_with('*') {\n                        \"/\"\n                    } else {\n                        \"*/\"\n                    })\n                }\n            },\n            Token::BadUrl(ref u) => {\n                let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone());\n                return Err(input.new_custom_error(e));\n            },\n            Token::BadString(ref s) => {\n                let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone());\n                return Err(input.new_custom_error(e));\n            },\n            Token::CloseParenthesis => {\n                let e = StyleParseErrorKind::UnbalancedCloseParenthesisInDeclarationValueBlock;\n                return Err(input.new_custom_error(e));\n            },\n            Token::CloseSquareBracket => {\n                let e = StyleParseErrorKind::UnbalancedCloseSquareBracketInDeclarationValueBlock;\n                return Err(input.new_custom_error(e));\n            },\n            Token::CloseCurlyBracket => {\n                let e = StyleParseErrorKind::UnbalancedCloseCurlyBracketInDeclarationValueBlock;\n                return Err(input.new_custom_error(e));\n            },\n            Token::Function(ref name) => {\n                let substitution_kind = match SubstitutionFunctionKind::from_ident(name).ok() {\n                    Some(SubstitutionFunctionKind::Attr) => {\n                        if static_prefs::pref!(\"layout.css.attr.enabled\") {\n                            Some(SubstitutionFunctionKind::Attr)\n                        } else {\n                            None\n                        }\n                    },\n                    kind => kind,\n                };\n                if let Some(substitution_kind) = substitution_kind {\n                    let our_ref_index = references.refs.len();\n                    let mut input_end_position = None;\n                    let fallback = input.parse_nested_block(|input| {\n                        let mut namespace = ParsedNamespace::Known(Namespace::default());\n                        if substitution_kind == SubstitutionFunctionKind::Attr {\n                            if let Some(namespaces) = namespaces {\n                                if let Ok(ns) = input\n                                    .try_parse(|input| ParsedNamespace::parse(namespaces, input))\n                                {\n                                    namespace = ns;\n                                }\n                            }\n                        }\n                        // TODO(emilio): For env() this should be <custom-ident> per spec, but no other browser does\n                        // that, see https://github.com/w3c/csswg-drafts/issues/3262.\n                        let name = input.expect_ident()?;\n                        let name =\n                            Atom::from(if substitution_kind == SubstitutionFunctionKind::Var {\n                                match parse_name(name.as_ref()) {\n                                    Ok(name) => name,\n                                    Err(()) => {\n                                        let name = name.clone();\n                                        return Err(input.new_custom_error(\n                                            SelectorParseErrorKind::UnexpectedIdent(name),\n                                        ));\n                                    },\n                                }\n                            } else {\n                                name.as_ref()\n                            });\n\n                        let attribute_kind = if substitution_kind == SubstitutionFunctionKind::Attr\n                        {\n                            parse_attr_type(input)\n                        } else {\n                            AttributeType::None\n                        };\n\n                        // We want the order of the references to match source order. So we need to reserve our slot\n                        // now, _before_ parsing our fallback. Note that we don't care if parsing fails after all, since\n                        // if this fails we discard the whole result anyways.\n                        let start = token_start.byte_index() - input_start.byte_index();\n                        references.refs.push(SubstitutionFunctionReference {\n                            name,\n                            start,\n                            // To be fixed up after parsing fallback and auto-closing via our_ref_index.\n                            end: start,\n                            prev_token_type,\n                            // To be fixed up (if needed) on the next loop iteration via prev_reference_index.\n                            next_token_type: TokenSerializationType::Nothing,\n                            // To be fixed up after parsing fallback.\n                            fallback: None,\n                            attribute_data: AttributeData {\n                                kind: attribute_kind,\n                                namespace,\n                            },\n                            substitution_kind: substitution_kind.clone(),\n                        });\n\n                        let mut fallback = None;\n                        if input.try_parse(|input| input.expect_comma()).is_ok() {\n                            input.skip_whitespace();\n                            let fallback_start = num::NonZeroUsize::new(\n                                input.position().byte_index() - input_start.byte_index(),\n                            )\n                            .unwrap();\n                            // NOTE(emilio): Intentionally using parse_declaration_value rather than\n                            // parse_declaration_value_block, since that's what parse_fallback used to do.\n                            let (first, last) = parse_declaration_value(\n                                input,\n                                input_start,\n                                namespaces,\n                                references,\n                                missing_closing_characters,\n                            )?;\n                            fallback = Some(VariableFallback {\n                                start: fallback_start,\n                                first_token_type: first,\n                                last_token_type: last,\n                            });\n                            input_end_position = Some(input.position());\n                        } else {\n                            let state = input.state();\n                            // We still need to consume the rest of the potentially-unclosed\n                            // tokens, but make sure to not consume tokens that would otherwise be\n                            // invalid, by calling reset().\n                            parse_declaration_value_block(\n                                input,\n                                input_start,\n                                namespaces,\n                                references,\n                                missing_closing_characters,\n                            )?;\n                            input_end_position = Some(input.position());\n                            input.reset(&state);\n                        }\n                        Ok(fallback)\n                    })?;\n                    if input_end_position.unwrap() == input.position() {\n                        missing_closing_characters.push_str(\")\");\n                    }\n                    prev_reference_index = Some(our_ref_index);\n                    let reference = &mut references.refs[our_ref_index];\n                    reference.end = input.position().byte_index() - input_start.byte_index()\n                        + missing_closing_characters.len();\n                    reference.fallback = fallback;\n                    match substitution_kind {\n                        SubstitutionFunctionKind::Var => references.any_var = true,\n                        SubstitutionFunctionKind::Env => references.any_env = true,\n                        SubstitutionFunctionKind::Attr => references.any_attr = true,\n                    };\n                } else {\n                    nested!(\")\");\n                }\n            },\n            Token::ParenthesisBlock => {\n                nested!(\")\");\n            },\n            Token::CurlyBracketBlock => {\n                nested!(\"}\");\n            },\n            Token::SquareBracketBlock => {\n                nested!(\"]\");\n            },\n            Token::QuotedString(_) => {\n                let token_slice = input.slice_from(token_start);\n                let quote = &token_slice[..1];\n                debug_assert!(matches!(quote, \"\\\"\" | \"'\"));\n                if !(token_slice.ends_with(quote) && token_slice.len() > 1) {\n                    missing_closing_characters.push_str(quote)\n                }\n            },\n            Token::Ident(ref value)\n            | Token::AtKeyword(ref value)\n            | Token::Hash(ref value)\n            | Token::IDHash(ref value)\n            | Token::UnquotedUrl(ref value)\n            | Token::Dimension {\n                unit: ref value, ..\n            } => {\n                references\n                    .non_custom_references\n                    .insert(NonCustomReferences::from_unit(value));\n                let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));\n                if value.ends_with(\"�\") && input.slice_from(token_start).ends_with(\"\\\\\") {\n                    // Unescaped backslash at EOF in these contexts is interpreted as U+FFFD\n                    // Check the value in case the final backslash was itself escaped.\n                    // Serialize as escaped U+FFFD, which is also interpreted as U+FFFD.\n                    // (Unescaped U+FFFD would also work, but removing the backslash is annoying.)\n                    missing_closing_characters.push_str(\"�\")\n                }\n                if is_unquoted_url && !input.slice_from(token_start).ends_with(\")\") {\n                    missing_closing_characters.push_str(\")\");\n                }\n            },\n            _ => {},\n        };\n    }\n    Ok((first_token_type, last_token_type))\n}\n\n/// Parse <attr-type> = type( <syntax> ) | raw-string | number | <attr-unit>.\n/// https://drafts.csswg.org/css-values-5/#attr-notation\nfn parse_attr_type<'i, 't>(input: &mut Parser<'i, 't>) -> AttributeType {\n    input\n        .try_parse(|input| {\n            Ok(match input.next()? {\n                Token::Function(ref name) if name.eq_ignore_ascii_case(\"type\") => {\n                    AttributeType::Type(\n                        input.parse_nested_block(SyntaxDescriptor::from_css_parser)?,\n                    )\n                },\n                Token::Ident(ref ident) => {\n                    if ident.eq_ignore_ascii_case(\"raw-string\") {\n                        AttributeType::RawString\n                    } else {\n                        let unit = AttrUnit::from_ident(ident).map_err(|_| {\n                            input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                        })?;\n                        AttributeType::Unit(unit)\n                    }\n                },\n                Token::Delim('%') => AttributeType::Unit(AttrUnit::Percentage),\n                _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n            })\n        })\n        .unwrap_or(AttributeType::None)\n}\n\n/// Attribute values may reference other substitution functions we may need to process.\n/// See step 6: https://drafts.csswg.org/css-values-5/#attr-substitution\nfn parse_attribute_value(\n    name: &Atom,\n    attribute_data: &AttributeData,\n    url_data: &UrlExtraData,\n    attribute_tracker: &mut AttributeTracker,\n) -> Result<ComputedRegisteredValue, ()> {\n    #[cfg(feature = \"gecko\")]\n    let local_name = LocalName::cast(name);\n    #[cfg(feature = \"servo\")]\n    let local_name = &LocalName::from(name.as_ref());\n    let namespace = match attribute_data.namespace {\n        ParsedNamespace::Known(ref ns) => ns,\n        ParsedNamespace::Unknown => return Err(()),\n    };\n    let attr = attribute_tracker.query(local_name, namespace).ok_or(())?;\n    let mut input = ParserInput::new(&attr);\n    let mut parser = Parser::new(&mut input);\n    // TODO(Bug 2021110): Support namespaced attributes in chained references.\n    let value = VariableValue::parse(&mut parser, None, &url_data).map_err(|_| ())?;\n    Ok(ComputedRegisteredValue::universal(Arc::new(value)))\n}\n\n#[derive(Default)]\nstruct SeenSubstitutionFunctions<'a> {\n    var: PrecomputedHashSet<&'a Name>,\n    attr: PrecomputedHashSet<&'a Name>,\n}\n\n/// A struct that takes care of encapsulating the cascade process for custom properties.\npub struct CustomPropertiesBuilder<'a, 'b: 'a> {\n    seen: SeenSubstitutionFunctions<'a>,\n    may_have_cycles: bool,\n    has_color_scheme: bool,\n    substitution_functions: ComputedSubstitutionFunctions,\n    reverted: PrecomputedHashMap<&'a Name, (CascadePriority, RevertKind)>,\n    stylist: &'a Stylist,\n    computed_context: &'a mut computed::Context<'b>,\n    references_from_non_custom_properties: NonCustomReferenceMap<Vec<Name>>,\n}\n\nfn find_non_custom_references(\n    registration: &PropertyDescriptors,\n    value: &VariableValue,\n    may_have_color_scheme: bool,\n    is_root_element: bool,\n    include_universal: bool,\n) -> Option<NonCustomReferences> {\n    let syntax = registration.syntax.as_ref()?;\n    let dependent_types = syntax.dependent_types();\n    let may_reference_length = dependent_types.intersects(DependentDataTypes::LENGTH)\n        || (include_universal && syntax.is_universal());\n    if may_reference_length {\n        let value_dependencies = value.references.non_custom_references(is_root_element);\n        if !value_dependencies.is_empty() {\n            return Some(value_dependencies);\n        }\n    }\n    if dependent_types.intersects(DependentDataTypes::COLOR) && may_have_color_scheme {\n        // NOTE(emilio): We might want to add a NonCustomReferences::COLOR_SCHEME or something but\n        // it's not really needed for correctness, so for now we use an Option for that to signal\n        // that there might be a dependencies.\n        return Some(NonCustomReferences::empty());\n    }\n    None\n}\n\nimpl<'a, 'b: 'a> CustomPropertiesBuilder<'a, 'b> {\n    /// Create a new builder, inheriting from a given custom properties map.\n    ///\n    /// We expose this publicly mostly for @keyframe blocks.\n    pub fn new_with_properties(\n        stylist: &'a Stylist,\n        custom_properties: ComputedCustomProperties,\n        computed_context: &'a mut computed::Context<'b>,\n    ) -> Self {\n        Self {\n            seen: SeenSubstitutionFunctions::default(),\n            reverted: Default::default(),\n            may_have_cycles: false,\n            has_color_scheme: false,\n            substitution_functions: ComputedSubstitutionFunctions::new(\n                Some(custom_properties),\n                None,\n            ),\n            stylist,\n            computed_context,\n            references_from_non_custom_properties: NonCustomReferenceMap::default(),\n        }\n    }\n\n    /// Create a new builder, inheriting from the right style given context.\n    pub fn new(stylist: &'a Stylist, context: &'a mut computed::Context<'b>) -> Self {\n        let is_root_element = context.is_root_element();\n\n        let inherited = context.inherited_custom_properties();\n        let initial_values = stylist.get_custom_property_initial_values();\n        let properties = ComputedCustomProperties {\n            inherited: if is_root_element {\n                debug_assert!(inherited.is_empty());\n                initial_values.inherited.clone()\n            } else {\n                inherited.inherited.clone()\n            },\n            non_inherited: initial_values.non_inherited.clone(),\n        };\n\n        // Reuse flags from computing registered custom properties initial values, such as\n        // whether they depend on viewport units.\n        context\n            .style()\n            .add_flags(stylist.get_custom_property_initial_values_flags());\n        Self::new_with_properties(stylist, properties, context)\n    }\n\n    /// Cascade a given custom property declaration.\n    pub fn cascade(\n        &mut self,\n        declaration: &'a CustomDeclaration,\n        priority: CascadePriority,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        let CustomDeclaration {\n            ref name,\n            ref value,\n        } = *declaration;\n\n        if let Some(&(reverted_priority, revert_kind)) = self.reverted.get(&name) {\n            if !reverted_priority.allows_when_reverted(&priority, revert_kind) {\n                return;\n            }\n        }\n\n        if !(priority.flags() - self.computed_context.included_cascade_flags).is_empty() {\n            return;\n        }\n\n        let was_already_present = !self.seen.var.insert(name);\n        if was_already_present {\n            return;\n        }\n\n        if !self.value_may_affect_style(name, value) {\n            return;\n        }\n\n        let kind = SubstitutionFunctionKind::Var;\n        let map = &mut self.substitution_functions;\n        let registration = self.stylist.get_custom_property_registration(&name);\n        match value {\n            CustomDeclarationValue::Unparsed(unparsed_value) => {\n                // At this point of the cascade we're not guaranteed to have seen the color-scheme\n                // declaration, so need to assume the worst. We could track all system color\n                // keyword tokens + the light-dark() function, but that seems non-trivial /\n                // probably overkill.\n                let may_have_color_scheme = true;\n                // Non-custom dependency is really relevant for registered custom properties\n                // that require computed value of such dependencies.\n                let has_dependency = unparsed_value.references.any_var\n                    || unparsed_value.references.any_attr\n                    || find_non_custom_references(\n                        registration,\n                        unparsed_value,\n                        may_have_color_scheme,\n                        self.computed_context.is_root_element(),\n                        /* include_unregistered = */ false,\n                    )\n                    .is_some();\n                // If the variable value has no references to other properties, perform\n                // substitution here instead of forcing a full traversal in `substitute_all`\n                // afterwards.\n                if !has_dependency {\n                    return substitute_references_if_needed_and_apply(\n                        name,\n                        kind,\n                        unparsed_value,\n                        &mut self.substitution_functions,\n                        self.stylist,\n                        self.computed_context,\n                        attribute_tracker,\n                    );\n                }\n                self.may_have_cycles = true;\n                let value = ComputedRegisteredValue::universal(Arc::clone(unparsed_value));\n                map.insert_var(registration, name, value);\n            },\n            CustomDeclarationValue::Parsed(parsed_value) => {\n                let value = parsed_value.to_computed_value(&self.computed_context);\n                map.insert_var(registration, name, value);\n            },\n            CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword.revert_kind() {\n                Some(revert_kind) => {\n                    self.seen.var.remove(name);\n                    self.reverted.insert(name, (priority, revert_kind));\n                },\n                None => match keyword {\n                    CSSWideKeyword::Initial => {\n                        // For non-inherited custom properties, 'initial' was handled in value_may_affect_style.\n                        debug_assert!(registration.inherits(), \"Should've been handled earlier\");\n                        remove_and_insert_initial_value(name, registration, map);\n                    },\n                    CSSWideKeyword::Inherit => {\n                        // For inherited custom properties, 'inherit' was handled in value_may_affect_style.\n                        debug_assert!(!registration.inherits(), \"Should've been handled earlier\");\n                        self.computed_context\n                            .style()\n                            .add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);\n                        if let Some(inherited_value) = self\n                            .computed_context\n                            .inherited_custom_properties()\n                            .non_inherited\n                            .get(name)\n                        {\n                            map.insert_var(registration, name, inherited_value.clone());\n                        }\n                    },\n                    // handled in value_may_affect_style or in the revert_kind branch above.\n                    CSSWideKeyword::Revert\n                    | CSSWideKeyword::RevertLayer\n                    | CSSWideKeyword::RevertRule\n                    | CSSWideKeyword::Unset => unreachable!(),\n                },\n            },\n        }\n    }\n\n    /// Fast check to avoid calling maybe_note_non_custom_dependency in ~all cases.\n    #[inline]\n    pub fn might_have_non_custom_or_attr_dependency(\n        id: LonghandId,\n        decl: &PropertyDeclaration,\n    ) -> bool {\n        if id == LonghandId::ColorScheme {\n            return true;\n        }\n        if let PropertyDeclaration::WithVariables(v) = decl {\n            return matches!(id, LonghandId::LineHeight | LonghandId::FontSize)\n                || v.value.variable_value.references.any_attr;\n        }\n        false\n    }\n\n    /// Note a non-custom property with variable reference that may in turn depend on that property.\n    /// e.g. `font-size` depending on a custom property that may be a registered property using `em`.\n    pub fn maybe_note_non_custom_dependency(\n        &mut self,\n        id: LonghandId,\n        decl: &'a PropertyDeclaration,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        debug_assert!(Self::might_have_non_custom_or_attr_dependency(id, decl));\n        if id == LonghandId::ColorScheme {\n            // If we might change the color-scheme, we need to defer computation of colors.\n            self.has_color_scheme = true;\n            return;\n        }\n\n        let PropertyDeclaration::WithVariables(v) = decl else {\n            return;\n        };\n        let value = &v.value.variable_value;\n        let refs = &value.references;\n\n        if !refs.any_var && !refs.any_attr {\n            return;\n        }\n\n        // Attributes in non-custom properties may reference `var()` or `attr()` in their\n        // values, which we need to track to support chained references and detect cycles.\n        // Further processing occurs during `CustomPropertiesBuilder::build()`.\n        if refs.any_attr {\n            self.update_attributes_map(value, attribute_tracker);\n            if !refs.any_var {\n                return;\n            }\n        }\n\n        // With unit algebra in `calc()`, references aren't limited to `font-size`.\n        // For example, `--foo: 100ex; font-weight: calc(var(--foo) / 1ex);`,\n        // or `--foo: 1em; zoom: calc(var(--foo) * 30px / 2em);`\n        let references = match id {\n            LonghandId::FontSize => {\n                if self.computed_context.is_root_element() {\n                    NonCustomReferences::ROOT_FONT_UNITS\n                } else {\n                    NonCustomReferences::FONT_UNITS\n                }\n            },\n            LonghandId::LineHeight => {\n                if self.computed_context.is_root_element() {\n                    NonCustomReferences::ROOT_LH_UNITS | NonCustomReferences::ROOT_FONT_UNITS\n                } else {\n                    NonCustomReferences::LH_UNITS | NonCustomReferences::FONT_UNITS\n                }\n            },\n            _ => return,\n        };\n\n        let variables: Vec<Atom> = refs\n            .refs\n            .iter()\n            .filter_map(|reference| {\n                if reference.substitution_kind != SubstitutionFunctionKind::Var {\n                    return None;\n                }\n                let registration = self\n                    .stylist\n                    .get_custom_property_registration(&reference.name);\n                if !registration\n                    .syntax\n                    .as_ref()?\n                    .dependent_types()\n                    .intersects(DependentDataTypes::LENGTH)\n                {\n                    return None;\n                }\n                Some(reference.name.clone())\n            })\n            .collect();\n        references.for_each(|idx| {\n            let entry = &mut self.references_from_non_custom_properties[idx];\n            let was_none = entry.is_none();\n            let v = entry.get_or_insert_with(|| variables.clone());\n            if was_none {\n                return;\n            }\n            v.extend(variables.iter().cloned());\n        });\n    }\n\n    fn value_may_affect_style(&self, name: &Name, value: &CustomDeclarationValue) -> bool {\n        let registration = self.stylist.get_custom_property_registration(&name);\n        match *value {\n            CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Inherit) => {\n                // For inherited custom properties, explicit 'inherit' means we\n                // can just use any existing value in the inherited\n                // CustomPropertiesMap.\n                if registration.inherits() {\n                    return false;\n                }\n            },\n            CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial) => {\n                // For non-inherited custom properties, explicit 'initial' means\n                // we can just use any initial value in the registration.\n                if !registration.inherits() {\n                    return false;\n                }\n            },\n            CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Unset) => {\n                // Explicit 'unset' means we can either just use any existing\n                // value in the inherited CustomPropertiesMap or the initial\n                // value in the registration.\n                return false;\n            },\n            _ => {},\n        }\n\n        let existing_value = self.substitution_functions.get_var(registration, &name);\n        let existing_value = match existing_value {\n            None => {\n                if matches!(\n                    value,\n                    CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial)\n                ) {\n                    debug_assert!(registration.inherits(), \"Should've been handled earlier\");\n                    // The initial value of a custom property without a\n                    // guaranteed-invalid initial value is the same as it\n                    // not existing in the map.\n                    if registration.initial_value.is_none() {\n                        return false;\n                    }\n                }\n                return true;\n            },\n            Some(v) => v,\n        };\n        let computed_value = match value {\n            CustomDeclarationValue::Unparsed(value) => {\n                // Don't bother overwriting an existing value with the same\n                // specified value.\n                if let Some(existing_value) = existing_value.as_universal() {\n                    return existing_value != value;\n                }\n                if !registration.is_universal() {\n                    compute_value(\n                        &value.css,\n                        &value.url_data,\n                        registration,\n                        self.computed_context,\n                        AttrTaint::default(),\n                    )\n                    .ok()\n                } else {\n                    None\n                }\n            },\n            CustomDeclarationValue::Parsed(value) => {\n                Some(value.to_computed_value(&self.computed_context))\n            },\n            CustomDeclarationValue::CSSWideKeyword(kw) => {\n                match kw {\n                    CSSWideKeyword::Inherit => {\n                        debug_assert!(!registration.inherits(), \"Should've been handled earlier\");\n                        // existing_value is the registered initial value.\n                        // Don't bother adding it to self.custom_properties.non_inherited\n                        // if the key is also absent from self.inherited.non_inherited.\n                        if self\n                            .computed_context\n                            .inherited_custom_properties()\n                            .non_inherited\n                            .get(name)\n                            .is_none()\n                        {\n                            return false;\n                        }\n                    },\n                    CSSWideKeyword::Initial => {\n                        debug_assert!(registration.inherits(), \"Should've been handled earlier\");\n                        // Don't bother overwriting an existing value with the initial value specified in\n                        // the registration.\n                        if let Some(initial_value) = self\n                            .stylist\n                            .get_custom_property_initial_values()\n                            .get(registration, name)\n                        {\n                            return existing_value != initial_value;\n                        }\n                    },\n                    CSSWideKeyword::Unset => {\n                        debug_assert!(false, \"Should've been handled earlier\");\n                    },\n                    CSSWideKeyword::Revert\n                    | CSSWideKeyword::RevertLayer\n                    | CSSWideKeyword::RevertRule => {},\n                }\n                None\n            },\n        };\n\n        if let Some(value) = computed_value {\n            return existing_value.v != value.v;\n        }\n\n        true\n    }\n\n    /// For a given unparsed variable, update the attributes map with its attr references.\n    pub fn update_attributes_map(\n        &mut self,\n        value: &'a VariableValue,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        let refs = &value.references;\n        if !refs.any_attr {\n            return;\n        }\n        self.may_have_cycles = true;\n\n        for next in &refs.refs {\n            // Skip non-attrs and attributes we've already processed.\n            if next.substitution_kind != SubstitutionFunctionKind::Attr\n                || !self.seen.attr.insert(&next.name)\n            {\n                continue;\n            }\n            if let Ok(v) = parse_attribute_value(\n                &next.name,\n                &next.attribute_data,\n                &value.url_data,\n                attribute_tracker,\n            ) {\n                self.substitution_functions.insert_attr(&next.name, v);\n            }\n        }\n    }\n\n    /// Computes the map of applicable custom properties, as well as\n    /// longhand properties that are now considered invalid-at-compute time.\n    /// The result is saved into the computed context.\n    ///\n    /// If there was any specified property or non-inherited custom property\n    /// with an initial value, we've created a new map and now we\n    /// need to remove any potential cycles (And marking non-custom\n    /// properties), and wrap it in an arc.\n    ///\n    /// Some registered custom properties may require font-related properties\n    /// be resolved to resolve. If these properties are not resolved at this time,\n    /// `defer` should be set to `Yes`, which will leave such custom properties,\n    /// and other properties referencing them, untouched. These properties are\n    /// returned separately, to be resolved by `build_deferred` to fully resolve\n    /// all custom properties after all necessary non-custom properties are resolved.\n    pub fn build(\n        mut self,\n        defer: DeferFontRelativeCustomPropertyResolution,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> Option<AllSubstitutionFunctions> {\n        let mut deferred_substitution_functions = None;\n        if self.may_have_cycles {\n            if defer == DeferFontRelativeCustomPropertyResolution::Yes {\n                deferred_substitution_functions = Some(AllSubstitutionFunctions::default());\n            }\n            let mut invalid_non_custom_properties = LonghandIdSet::default();\n            substitute_all(\n                &mut self.substitution_functions,\n                deferred_substitution_functions.as_mut(),\n                &mut invalid_non_custom_properties,\n                self.has_color_scheme,\n                &self.seen,\n                &self.references_from_non_custom_properties,\n                self.stylist,\n                self.computed_context,\n                attribute_tracker,\n            );\n            self.computed_context.builder.invalid_non_custom_properties =\n                invalid_non_custom_properties;\n        }\n        self.substitution_functions\n            .custom_properties\n            .shrink_to_fit();\n\n        // Some pages apply a lot of redundant custom properties, see e.g.\n        // bug 1758974 comment 5. Try to detect the case where the values\n        // haven't really changed, and save some memory by reusing the inherited\n        // map in that case.\n        let initial_values = self.stylist.get_custom_property_initial_values();\n        let custom_properties = self.substitution_functions.custom_properties;\n        self.computed_context\n            .builder\n            .substitution_functions\n            .custom_properties = ComputedCustomProperties {\n            inherited: if self\n                .computed_context\n                .inherited_custom_properties()\n                .inherited\n                == custom_properties.inherited\n            {\n                self.computed_context\n                    .inherited_custom_properties()\n                    .inherited\n                    .clone()\n            } else {\n                custom_properties.inherited\n            },\n            non_inherited: if initial_values.non_inherited == custom_properties.non_inherited {\n                initial_values.non_inherited.clone()\n            } else {\n                custom_properties.non_inherited\n            },\n        };\n        self.computed_context\n            .builder\n            .substitution_functions\n            .attributes = self.substitution_functions.attributes;\n\n        deferred_substitution_functions\n    }\n\n    /// Fully resolve all deferred custom properties and attributes, assuming that the\n    /// incoming context has necessary properties resolved.\n    pub fn build_deferred(\n        deferred: AllSubstitutionFunctions,\n        stylist: &Stylist,\n        computed_context: &mut computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        if deferred.is_empty() {\n            return;\n        }\n        let mut map = std::mem::take(&mut computed_context.builder.substitution_functions);\n        // Since `CustomPropertiesMap` preserves insertion order, we shouldn't have to worry about\n        // resolving in a wrong order.\n        for (name, kind, v) in deferred.iter() {\n            let Some(v) = v.as_universal() else {\n                unreachable!(\"Computing should have been deferred!\")\n            };\n            substitute_references_if_needed_and_apply(\n                name,\n                kind,\n                v,\n                &mut map,\n                stylist,\n                computed_context,\n                attribute_tracker,\n            );\n        }\n        computed_context.builder.substitution_functions = map;\n    }\n}\n\n/// Resolve all custom properties to either substituted, invalid, or unset\n/// (meaning we should use the inherited value).\n///\n/// It does cycle dependencies removal at the same time as substitution.\nfn substitute_all(\n    substitution_function_map: &mut ComputedSubstitutionFunctions,\n    mut deferred_substituted_functions_map: Option<&mut AllSubstitutionFunctions>,\n    invalid_non_custom_properties: &mut LonghandIdSet,\n    has_color_scheme: bool,\n    seen: &SeenSubstitutionFunctions,\n    references_from_non_custom_properties: &NonCustomReferenceMap<Vec<Name>>,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    attr_tracker: &mut AttributeTracker,\n) {\n    // The cycle dependencies removal in this function is a variant\n    // of Tarjan's algorithm. It is mostly based on the pseudo-code\n    // listed in\n    // https://en.wikipedia.org/w/index.php?\n    // title=Tarjan%27s_strongly_connected_components_algorithm&oldid=801728495\n\n    #[derive(Clone, Eq, PartialEq, Debug)]\n    enum VarType {\n        Attr(Name),\n        Custom(Name),\n        NonCustom(SingleNonCustomReference),\n    }\n\n    /// Struct recording necessary information for each variable.\n    #[derive(Debug)]\n    struct VarInfo {\n        /// The name of the variable. It will be taken to save addref\n        /// when the corresponding variable is popped from the stack.\n        /// This also serves as a mark for whether the variable is\n        /// currently in the stack below.\n        var: Option<VarType>,\n        /// If the variable is in a dependency cycle, lowlink represents\n        /// a smaller index which corresponds to a variable in the same\n        /// strong connected component, which is known to be accessible\n        /// from this variable. It is not necessarily the root, though.\n        lowlink: usize,\n    }\n    /// Context struct for traversing the variable graph, so that we can\n    /// avoid referencing all the fields multiple times.\n    struct Context<'a, 'b: 'a> {\n        /// Number of variables visited. This is used as the order index\n        /// when we visit a new unresolved variable.\n        count: usize,\n        /// The map from custom property name to its order index.\n        index_map: PrecomputedHashMap<Name, usize>,\n        /// Mapping from a non-custom dependency to its order index.\n        non_custom_index_map: NonCustomReferenceMap<usize>,\n        /// Information of each variable indexed by the order index.\n        var_info: SmallVec<[VarInfo; 5]>,\n        /// The stack of order index of visited variables. It contains\n        /// all unfinished strong connected components.\n        stack: SmallVec<[usize; 5]>,\n        /// References to non-custom properties in this strongly connected component.\n        non_custom_references: NonCustomReferences,\n        /// Whether the builder has seen a non-custom color-scheme reference.\n        has_color_scheme: bool,\n        /// Whether this strongly connected component contains any custom properties involving\n        /// value computation.\n        contains_computed_custom_property: bool,\n        map: &'a mut ComputedSubstitutionFunctions,\n        /// The stylist is used to get registered properties, and to resolve the environment to\n        /// substitute `env()` variables.\n        stylist: &'a Stylist,\n        /// The computed context is used to get inherited custom\n        /// properties  and compute registered custom properties.\n        computed_context: &'a computed::Context<'b>,\n        /// Longhand IDs that became invalid due to dependency cycle(s).\n        invalid_non_custom_properties: &'a mut LonghandIdSet,\n        /// Substitution functions that cannot yet be substituted. We store both custom\n        /// properties (inherited and non-inherited) and attributes in the same map, since\n        /// we need to make sure we iterate through them in the right order.\n        deferred_substitution_functions: Option<&'a mut AllSubstitutionFunctions>,\n    }\n\n    /// This function combines the traversal for cycle removal and value\n    /// substitution. It returns either a signal None if this variable\n    /// has been fully resolved (to either having no reference or being\n    /// marked invalid), or the order index for the given name.\n    ///\n    /// When it returns, the variable corresponds to the name would be\n    /// in one of the following states:\n    /// * It is still in context.stack, which means it is part of an\n    ///   potentially incomplete dependency circle.\n    /// * It has been removed from the map.  It can be either that the\n    ///   substitution failed, or it is inside a dependency circle.\n    ///   When this function removes a variable from the map because\n    ///   of dependency circle, it would put all variables in the same\n    ///   strong connected component to the set together.\n    /// * It doesn't have any reference, because either this variable\n    ///   doesn't have reference at all in specified value, or it has\n    ///   been completely resolved.\n    /// * There is no such variable at all.\n    fn traverse<'a, 'b>(\n        var: VarType,\n        non_custom_references: &NonCustomReferenceMap<Vec<Name>>,\n        context: &mut Context<'a, 'b>,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> Option<usize> {\n        let kind = if matches!(var, VarType::Custom(_)) {\n            SubstitutionFunctionKind::Var\n        } else {\n            SubstitutionFunctionKind::Attr\n        };\n        // Some shortcut checks.\n        let value = match var {\n            VarType::Custom(ref name) | VarType::Attr(ref name) => {\n                let registration;\n                let value;\n                match kind {\n                    SubstitutionFunctionKind::Var => {\n                        registration = context.stylist.get_custom_property_registration(name);\n                        value = context.map.get_var(registration, name)?.as_universal()?;\n                    },\n                    SubstitutionFunctionKind::Attr => {\n                        // FIXME(bug1997338): registration does not make much sense for attrs.\n                        //     Rework find_non_custom_references to take Descriptor instead?\n                        registration = PropertyDescriptors::unregistered();\n                        value = context.map.get_attr(name)?.as_universal()?;\n                    },\n                    _ => unreachable!(\"Substitution kind must be var or attr for VarType::Custom.\"),\n                }\n                let is_var = kind == SubstitutionFunctionKind::Var;\n                let is_attr = kind == SubstitutionFunctionKind::Attr;\n                let is_root = context.computed_context.is_root_element();\n                // We need to keep track of potential non-custom-references even on unregistered\n                // properties for cycle-detection purposes.\n                let non_custom_refs = find_non_custom_references(\n                    registration,\n                    value,\n                    context.has_color_scheme,\n                    is_root,\n                    /* include_unregistered = */ true,\n                );\n                context.non_custom_references |= non_custom_refs.unwrap_or_default();\n                let has_dependency = value.references.any_var\n                    || value.references.any_attr\n                    || non_custom_refs.is_some();\n                // Nothing to resolve.\n                if !has_dependency {\n                    debug_assert!(!value.references.any_env, \"Should've been handled earlier\");\n                    if is_attr || !registration.is_universal() {\n                        // We might still need to compute the value if this is not an universal\n                        // registration if we thought this had a dependency before but turned out\n                        // not to be (due to has_color_scheme, for example). Note that if this was\n                        // already computed we would've bailed out in the as_universal() check.\n                        if is_var {\n                            debug_assert!(\n                                registration\n                                    .syntax\n                                    .as_ref()\n                                    .unwrap()\n                                    .dependent_types()\n                                    .intersects(DependentDataTypes::COLOR),\n                                \"How did an unresolved value get here otherwise?\",\n                            );\n                        }\n                        let value = value.clone();\n                        substitute_references_if_needed_and_apply(\n                            name,\n                            kind,\n                            &value,\n                            &mut context.map,\n                            context.stylist,\n                            context.computed_context,\n                            attribute_tracker,\n                        );\n                    }\n                    return None;\n                }\n\n                // Has this variable been visited?\n                // FIXME(bug1997338): a name conflict between between y and --y is possible\n                //     because they refer to the same atom. E.g. `attr(y type(*))` where\n                //     `y=\"var(--y)\"` and `--y: var(--baz)`.\n                match context.index_map.entry(name.clone()) {\n                    Entry::Occupied(entry) => {\n                        return Some(*entry.get());\n                    },\n                    Entry::Vacant(entry) => {\n                        entry.insert(context.count);\n                    },\n                }\n                context.contains_computed_custom_property |= is_var && !registration.is_universal();\n\n                // Hold a strong reference to the value so that we don't\n                // need to keep reference to context.map.\n                Some(value.clone())\n            },\n            VarType::NonCustom(ref non_custom) => {\n                let entry = &mut context.non_custom_index_map[*non_custom];\n                if let Some(v) = entry {\n                    return Some(*v);\n                }\n                *entry = Some(context.count);\n                None\n            },\n        };\n\n        // Add new entry to the information table.\n        let index = context.count;\n        context.count += 1;\n        debug_assert_eq!(index, context.var_info.len());\n        context.var_info.push(VarInfo {\n            var: Some(var.clone()),\n            lowlink: index,\n        });\n        context.stack.push(index);\n\n        let mut self_ref = false;\n        let mut lowlink = index;\n        let visit_link = |var: VarType,\n                          context: &mut Context,\n                          lowlink: &mut usize,\n                          self_ref: &mut bool,\n                          attr_tracker: &mut AttributeTracker| {\n            let next_index = match traverse(var, non_custom_references, context, attr_tracker) {\n                Some(index) => index,\n                // There is nothing to do if the next variable has been\n                // fully resolved at this point.\n                None => {\n                    return;\n                },\n            };\n            let next_info = &context.var_info[next_index];\n            if next_index > index {\n                // The next variable has a larger index than us, so it\n                // must be inserted in the recursive call above. We want\n                // to get its lowlink.\n                *lowlink = cmp::min(*lowlink, next_info.lowlink);\n            } else if next_index == index {\n                *self_ref = true;\n            } else if next_info.var.is_some() {\n                // The next variable has a smaller order index and it is\n                // in the stack, so we are at the same component.\n                *lowlink = cmp::min(*lowlink, next_index);\n            }\n        };\n        if let Some(ref v) = value.as_ref() {\n            debug_assert!(\n                matches!(var, VarType::Custom(_) | VarType::Attr(_)),\n                \"Non-custom property has references?\"\n            );\n\n            // Visit other custom properties...\n            // FIXME: Maybe avoid visiting the same var twice if not needed?\n            for next in &v.references.refs {\n                if next.substitution_kind == SubstitutionFunctionKind::Env {\n                    continue;\n                }\n\n                let next_var = if next.substitution_kind == SubstitutionFunctionKind::Attr {\n                    if context.map.get_attr(&next.name).is_none() {\n                        let Ok(val) = parse_attribute_value(\n                            &next.name,\n                            &next.attribute_data,\n                            &v.url_data,\n                            attribute_tracker,\n                        ) else {\n                            continue;\n                        };\n                        context.map.insert_attr(&next.name, val);\n                    }\n                    VarType::Attr(next.name.clone())\n                } else {\n                    VarType::Custom(next.name.clone())\n                };\n\n                visit_link(\n                    next_var,\n                    context,\n                    &mut lowlink,\n                    &mut self_ref,\n                    attribute_tracker,\n                );\n            }\n\n            // ... Then non-custom properties.\n            v.references.non_custom_references.for_each(|r| {\n                visit_link(\n                    VarType::NonCustom(r),\n                    context,\n                    &mut lowlink,\n                    &mut self_ref,\n                    attribute_tracker,\n                );\n            });\n        } else if let VarType::NonCustom(non_custom) = var {\n            let entry = &non_custom_references[non_custom];\n            if let Some(deps) = entry.as_ref() {\n                for d in deps {\n                    // Visit any reference from this non-custom property to custom properties.\n                    // TODO(bug1997338): non-custom properties can reference attrs.\n                    visit_link(\n                        VarType::Custom(d.clone()),\n                        context,\n                        &mut lowlink,\n                        &mut self_ref,\n                        attribute_tracker,\n                    );\n                }\n            }\n        }\n\n        context.var_info[index].lowlink = lowlink;\n        if lowlink != index {\n            // This variable is in a loop, but it is not the root of\n            // this strong connected component. We simply return for\n            // now, and the root would remove it from the map.\n            //\n            // This cannot be removed from the map here, because\n            // otherwise the shortcut check at the beginning of this\n            // function would return the wrong value.\n            return Some(index);\n        }\n\n        // This is the root of a strong-connected component.\n        let mut in_loop = self_ref;\n        let name;\n\n        let handle_variable_in_loop =\n            |name: &Name, context: &mut Context<'a, 'b>, kind: SubstitutionFunctionKind| {\n                if context.contains_computed_custom_property {\n                    // These non-custom properties can't become invalid-at-compute-time from\n                    // cyclic dependencies purely consisting of non-registered properties.\n                    if context.non_custom_references.intersects(\n                        NonCustomReferences::FONT_UNITS | NonCustomReferences::ROOT_FONT_UNITS,\n                    ) {\n                        context\n                            .invalid_non_custom_properties\n                            .insert(LonghandId::FontSize);\n                    }\n                    if context.non_custom_references.intersects(\n                        NonCustomReferences::LH_UNITS | NonCustomReferences::ROOT_LH_UNITS,\n                    ) {\n                        context\n                            .invalid_non_custom_properties\n                            .insert(LonghandId::LineHeight);\n                    }\n                }\n                // This variable is in loop. Resolve to invalid.\n                handle_invalid_at_computed_value_time(\n                    name,\n                    kind,\n                    &mut context.map,\n                    context.computed_context,\n                );\n            };\n        loop {\n            let var_index = context\n                .stack\n                .pop()\n                .expect(\"The current variable should still be in stack\");\n            let var_info = &mut context.var_info[var_index];\n            // We should never visit the variable again, so it's safe\n            // to take the name away, so that we don't do additional\n            // reference count.\n            let var_name = var_info\n                .var\n                .take()\n                .expect(\"Variable should not be poped from stack twice\");\n            if var_index == index {\n                name = match var_name {\n                    VarType::Custom(name) | VarType::Attr(name) => name,\n                    // At the root of this component, and it's a non-custom\n                    // reference - we have nothing to substitute, so\n                    // it's effectively resolved.\n                    VarType::NonCustom(..) => return None,\n                };\n                break;\n            }\n            if let VarType::Custom(name) | VarType::Attr(name) = var_name {\n                // Anything here is in a loop which can traverse to the\n                // variable we are handling, so it's invalid at\n                // computed-value time.\n                handle_variable_in_loop(&name, context, kind);\n            }\n            in_loop = true;\n        }\n        // We've gotten to the root of this strongly connected component, so clear\n        // whether or not it involved non-custom references.\n        // It's fine to track it like this, because non-custom properties currently\n        // being tracked can only participate in any loop only once.\n        if in_loop {\n            handle_variable_in_loop(&name, context, kind);\n            context.non_custom_references = NonCustomReferences::default();\n            return None;\n        }\n\n        if let Some(ref v) = value {\n            let registration = context.stylist.get_custom_property_registration(&name);\n\n            let mut defer = false;\n            if let Some(ref mut deferred) = context.deferred_substitution_functions {\n                // We need to defer this property if it has a non-custom property dependency, or\n                // any variable that it references is already deferred.\n                defer = find_non_custom_references(\n                    registration,\n                    v,\n                    context.has_color_scheme,\n                    context.computed_context.is_root_element(),\n                    /* include_unregistered = */ false,\n                )\n                .is_some()\n                    || v.references.refs.iter().any(|reference| {\n                        (reference.substitution_kind == SubstitutionFunctionKind::Var\n                            && deferred\n                                .get(&reference.name, SubstitutionFunctionKind::Var)\n                                .is_some())\n                            || reference.substitution_kind == SubstitutionFunctionKind::Attr\n                    });\n                if defer {\n                    let value = ComputedRegisteredValue::universal(Arc::clone(v));\n                    deferred.insert(&name, kind, value);\n                    if kind == SubstitutionFunctionKind::Var {\n                        context.map.remove_var(registration, &name);\n                    } else {\n                        context.map.remove_attr(&name);\n                    }\n                }\n            }\n\n            // If there are no var or attr references we should already be computed and substituted by now.\n            if !defer && (v.references.any_var || v.references.any_attr) {\n                substitute_references_if_needed_and_apply(\n                    &name,\n                    kind,\n                    v,\n                    &mut context.map,\n                    context.stylist,\n                    context.computed_context,\n                    attribute_tracker,\n                );\n            }\n        }\n        context.non_custom_references = NonCustomReferences::default();\n\n        // All resolved, so return the signal value.\n        None\n    }\n\n    let mut run = |make_var: fn(Name) -> VarType, seen: &PrecomputedHashSet<&Name>| {\n        for name in seen {\n            let mut context = Context {\n                count: 0,\n                index_map: PrecomputedHashMap::default(),\n                non_custom_index_map: NonCustomReferenceMap::default(),\n                stack: SmallVec::new(),\n                var_info: SmallVec::new(),\n                map: substitution_function_map,\n                non_custom_references: NonCustomReferences::default(),\n                has_color_scheme,\n                stylist,\n                computed_context,\n                invalid_non_custom_properties,\n                deferred_substitution_functions: deferred_substituted_functions_map.as_deref_mut(),\n                contains_computed_custom_property: false,\n            };\n\n            traverse(\n                make_var((*name).clone()),\n                references_from_non_custom_properties,\n                &mut context,\n                attr_tracker,\n            );\n        }\n    };\n\n    // Note that `seen` doesn't contain names inherited from our parent, but\n    // those can't have variable references (since we inherit the computed\n    // variables) so we don't want to spend cycles traversing them anyway.\n    run(VarType::Custom, &seen.var);\n    // Traverse potentially untraversed chained references from `attr(type())`\n    // in non-custom properties.\n    run(VarType::Attr, &seen.attr);\n}\n\n// See https://drafts.csswg.org/css-variables-2/#invalid-at-computed-value-time\nfn handle_invalid_at_computed_value_time(\n    name: &Name,\n    kind: SubstitutionFunctionKind,\n    substitution_functions: &mut ComputedSubstitutionFunctions,\n    computed_context: &computed::Context,\n) {\n    if kind == SubstitutionFunctionKind::Attr {\n        // Early return: `attr()` is always treated as unregistered.\n        substitution_functions.remove_attr(name);\n        return;\n    }\n\n    let stylist = computed_context.style().stylist.unwrap();\n    let registration = stylist.get_custom_property_registration(&name);\n    if !registration.is_universal() {\n        // For the root element, inherited maps are empty. We should just\n        // use the initial value if any, rather than removing the name.\n        if registration.inherits() && !computed_context.is_root_element() {\n            let inherited = computed_context.inherited_custom_properties();\n            if let Some(value) = inherited.get(registration, name) {\n                substitution_functions.insert_var(registration, name, value.clone());\n                return;\n            }\n        } else if let Some(ref initial_value) = registration.initial_value {\n            if let Ok(initial_value) = compute_value(\n                &initial_value.css,\n                &initial_value.url_data,\n                registration,\n                computed_context,\n                AttrTaint::default(),\n            ) {\n                substitution_functions.insert_var(registration, name, initial_value);\n                return;\n            }\n        }\n    }\n    substitution_functions.remove_var(registration, name);\n}\n\n/// Replace `var()`, `env()`, and `attr()` functions in a pre-existing variable value.\nfn substitute_references_if_needed_and_apply(\n    name: &Name,\n    kind: SubstitutionFunctionKind,\n    value: &Arc<VariableValue>,\n    substitution_functions: &mut ComputedSubstitutionFunctions,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    attribute_tracker: &mut AttributeTracker,\n) {\n    debug_assert_ne!(kind, SubstitutionFunctionKind::Env);\n    let is_var = kind == SubstitutionFunctionKind::Var;\n    let registration = stylist.get_custom_property_registration(&name);\n    if is_var && !value.has_references() && registration.is_universal() {\n        // Trivial path: no references and no need to compute the value, just apply it directly.\n        let computed_value = ComputedRegisteredValue::universal(Arc::clone(value));\n        substitution_functions.insert_var(registration, name, computed_value);\n        return;\n    }\n\n    let inherited = computed_context.inherited_custom_properties();\n    let url_data = &value.url_data;\n    let substitution = match substitute_internal(\n        value,\n        substitution_functions,\n        stylist,\n        computed_context,\n        attribute_tracker,\n        None,\n    ) {\n        Ok(v) => v,\n        Err(..) => {\n            handle_invalid_at_computed_value_time(\n                name,\n                kind,\n                substitution_functions,\n                computed_context,\n            );\n            return;\n        },\n    };\n\n    // TODO(bug1997338): rework with attr.\n    // If variable fallback results in a wide keyword, deal with it now.\n    {\n        let css = &substitution.css;\n        let css_wide_kw = {\n            let mut input = ParserInput::new(&css);\n            let mut input = Parser::new(&mut input);\n            input.try_parse(CSSWideKeyword::parse)\n        };\n\n        if let Ok(kw) = css_wide_kw {\n            // TODO: It's unclear what this should do for revert / revert-layer, see\n            // https://github.com/w3c/csswg-drafts/issues/9131. For now treating as unset\n            // seems fine?\n            match (\n                kw,\n                registration.inherits(),\n                computed_context.is_root_element(),\n            ) {\n                (CSSWideKeyword::Initial, _, _)\n                | (CSSWideKeyword::Revert, false, _)\n                | (CSSWideKeyword::RevertLayer, false, _)\n                | (CSSWideKeyword::RevertRule, false, _)\n                | (CSSWideKeyword::Unset, false, _)\n                | (CSSWideKeyword::Revert, true, true)\n                | (CSSWideKeyword::RevertLayer, true, true)\n                | (CSSWideKeyword::RevertRule, true, true)\n                | (CSSWideKeyword::Unset, true, true)\n                | (CSSWideKeyword::Inherit, _, true) => {\n                    remove_and_insert_initial_value(name, registration, substitution_functions);\n                },\n                (CSSWideKeyword::Revert, true, false)\n                | (CSSWideKeyword::RevertLayer, true, false)\n                | (CSSWideKeyword::RevertRule, true, false)\n                | (CSSWideKeyword::Inherit, _, false)\n                | (CSSWideKeyword::Unset, true, false) => {\n                    match inherited.get(registration, name) {\n                        Some(value) => {\n                            substitution_functions.insert_var(registration, name, value.clone());\n                        },\n                        None => {\n                            substitution_functions.remove_var(registration, name);\n                        },\n                    };\n                },\n            }\n            return;\n        }\n    }\n\n    match kind {\n        SubstitutionFunctionKind::Var => {\n            let value = match substitution.into_value(url_data, registration, computed_context) {\n                Ok(v) => v,\n                Err(()) => {\n                    handle_invalid_at_computed_value_time(\n                        name,\n                        kind,\n                        substitution_functions,\n                        computed_context,\n                    );\n                    return;\n                },\n            };\n            substitution_functions.insert_var(registration, name, value);\n        },\n        SubstitutionFunctionKind::Attr => {\n            let mut value = ComputedRegisteredValue::universal(Arc::new(VariableValue::new(\n                substitution.css.into_owned(),\n                url_data,\n                substitution.first_token_type,\n                substitution.last_token_type,\n            )));\n            value.attr_tainted |= substitution.attr_tainted;\n            substitution_functions.insert_attr(name, value);\n        },\n        SubstitutionFunctionKind::Env => unreachable!(\"Kind cannot be env.\"),\n    }\n}\n\n#[derive(Default, Debug)]\nstruct Substitution<'a> {\n    css: Cow<'a, str>,\n    first_token_type: TokenSerializationType,\n    last_token_type: TokenSerializationType,\n    attr_tainted: bool,\n}\n\nimpl<'a> Substitution<'a> {\n    fn from_value(v: VariableValue, attr_tainted: bool) -> Self {\n        Substitution {\n            css: v.css.into(),\n            first_token_type: v.first_token_type,\n            last_token_type: v.last_token_type,\n            attr_tainted,\n        }\n    }\n\n    fn into_value(\n        self,\n        url_data: &UrlExtraData,\n        registration: &PropertyDescriptors,\n        computed_context: &computed::Context,\n    ) -> Result<ComputedRegisteredValue, ()> {\n        if registration.is_universal() {\n            let mut value = ComputedRegisteredValue::universal(Arc::new(VariableValue::new(\n                self.css.into_owned(),\n                url_data,\n                self.first_token_type,\n                self.last_token_type,\n            )));\n            value.attr_tainted |= self.attr_tainted;\n            return Ok(value);\n        }\n        let taint = if self.attr_tainted {\n            // Per spec: substitution value of an arbitrary substitution function is\n            // attr()-tainted as a whole if any attr()-tainted values were involved\n            // in creating that substitution value.\n            // https://drafts.csswg.org/css-values-5/#attr-security\n            AttrTaint::new_fully_tainted(self.css.len())\n        } else {\n            AttrTaint::default()\n        };\n        let mut v = compute_value(&self.css, url_data, registration, computed_context, taint)?;\n        v.attr_tainted |= self.attr_tainted;\n        Ok(v)\n    }\n\n    fn new(\n        css: Cow<'a, str>,\n        first_token_type: TokenSerializationType,\n        last_token_type: TokenSerializationType,\n        attr_tainted: bool,\n    ) -> Self {\n        Self {\n            css,\n            first_token_type,\n            last_token_type,\n            attr_tainted,\n        }\n    }\n}\n\n/// Result of var(), env(), and attr() substitution.\n#[derive(Debug)]\npub struct SubstitutionResult<'a> {\n    /// The resolved CSS string after substitution.\n    pub css: Cow<'a, str>,\n    /// Regions in the `css` string that are attr()-tainted, if any.\n    pub attr_taint: AttrTaint,\n}\n\nfn compute_value(\n    css: &str,\n    url_data: &UrlExtraData,\n    registration: &PropertyDescriptors,\n    computed_context: &computed::Context,\n    attr_taint: AttrTaint,\n) -> Result<ComputedRegisteredValue, ()> {\n    debug_assert!(!registration.is_universal());\n\n    let mut input = ParserInput::new(&css);\n    let mut input = Parser::new(&mut input);\n\n    SpecifiedRegisteredValue::compute(\n        &mut input,\n        registration,\n        None,\n        url_data,\n        computed_context,\n        AllowComputationallyDependent::Yes,\n        attr_taint,\n    )\n}\n\n/// Removes the named registered custom property and inserts its uncomputed initial value.\nfn remove_and_insert_initial_value(\n    name: &Name,\n    registration: &PropertyDescriptors,\n    substitution_functions: &mut ComputedSubstitutionFunctions,\n) {\n    substitution_functions.remove_var(registration, name);\n    if let Some(ref initial_value) = registration.initial_value {\n        let value = ComputedRegisteredValue::universal(Arc::clone(initial_value));\n        substitution_functions.insert_var(registration, name, value);\n    }\n}\n\nfn do_substitute_chunk<'a>(\n    css: &'a str,\n    start: usize,\n    end: usize,\n    first_token_type: TokenSerializationType,\n    last_token_type: TokenSerializationType,\n    url_data: &UrlExtraData,\n    substitution_functions: &'a ComputedSubstitutionFunctions,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    references: &mut std::iter::Peekable<std::slice::Iter<SubstitutionFunctionReference>>,\n    attribute_tracker: &mut AttributeTracker,\n    mut attr_taint: Option<&mut AttrTaint>,\n) -> Result<Substitution<'a>, ()> {\n    if start == end {\n        // Empty string. Easy.\n        return Ok(Substitution::default());\n    }\n    // Easy case: no references involved.\n    if references\n        .peek()\n        .map_or(true, |reference| reference.end > end)\n    {\n        let result = &css[start..end];\n        return Ok(Substitution::new(\n            Cow::Borrowed(result),\n            first_token_type,\n            last_token_type,\n            Default::default(),\n        ));\n    }\n\n    let mut substituted = ComputedValue::empty(url_data);\n    let mut next_token_type = first_token_type;\n    let mut cur_pos = start;\n    let mut attr_tainted = false;\n    while let Some(reference) = references.next_if(|reference| reference.end <= end) {\n        if reference.start != cur_pos {\n            substituted.push(\n                &css[cur_pos..reference.start],\n                next_token_type,\n                reference.prev_token_type,\n                /* attr_taint */ None,\n            )?;\n        }\n\n        let substitution = substitute_one_reference(\n            css,\n            url_data,\n            substitution_functions,\n            reference,\n            stylist,\n            computed_context,\n            references,\n            attribute_tracker,\n        )?;\n\n        // Optimize the property: var(--...) case to avoid allocating at all.\n        if reference.start == start && reference.end == end {\n            if let Some(taint) = attr_taint.filter(|_| substitution.attr_tainted) {\n                taint.push(start, end);\n            }\n            return Ok(substitution);\n        }\n\n        substituted.push(\n            &substitution.css,\n            substitution.first_token_type,\n            substitution.last_token_type,\n            attr_taint\n                .as_deref_mut()\n                .filter(|_| substitution.attr_tainted),\n        )?;\n        attr_tainted |= substitution.attr_tainted;\n        next_token_type = reference.next_token_type;\n        cur_pos = reference.end;\n    }\n    // Push the rest of the value if needed.\n    if cur_pos != end {\n        substituted.push(\n            &css[cur_pos..end],\n            next_token_type,\n            last_token_type,\n            /* attr_taint */ None,\n        )?;\n    }\n    Ok(Substitution::from_value(substituted, attr_tainted))\n}\n\nfn quoted_css_string(src: &str) -> String {\n    let mut dest = String::with_capacity(src.len() + 2);\n    cssparser::serialize_string(src, &mut dest).unwrap();\n    dest\n}\n\nfn substitute_one_reference<'a>(\n    css: &'a str,\n    url_data: &UrlExtraData,\n    substitution_functions: &'a ComputedSubstitutionFunctions,\n    reference: &SubstitutionFunctionReference,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    references: &mut std::iter::Peekable<std::slice::Iter<SubstitutionFunctionReference>>,\n    attribute_tracker: &mut AttributeTracker,\n) -> Result<Substitution<'a>, ()> {\n    let simple_attr_subst = |s: &str| {\n        Some(Substitution::new(\n            Cow::Owned(quoted_css_string(s)),\n            TokenSerializationType::Nothing,\n            TokenSerializationType::Nothing,\n            /* attr_tainted */ true,\n        ))\n    };\n    let substitution: Option<_> = match reference.substitution_kind {\n        SubstitutionFunctionKind::Var => {\n            let registration = stylist.get_custom_property_registration(&reference.name);\n            substitution_functions\n                .get_var(registration, &reference.name)\n                .map(|v| Substitution::from_value(v.to_variable_value(), v.attr_tainted))\n        },\n        SubstitutionFunctionKind::Env => {\n            let device = stylist.device();\n            device\n                .environment()\n                .get(&reference.name, device, url_data)\n                .map(|v| Substitution::from_value(v, /* attr_tainted */ false))\n        },\n        // https://drafts.csswg.org/css-values-5/#attr-substitution\n        SubstitutionFunctionKind::Attr => {\n            #[cfg(feature = \"gecko\")]\n            let local_name = LocalName::cast(&reference.name);\n            #[cfg(feature = \"servo\")]\n            let local_name = LocalName::from(reference.name.as_ref());\n            let namespace = match reference.attribute_data.namespace {\n                ParsedNamespace::Known(ref ns) => Some(ns),\n                ParsedNamespace::Unknown => None,\n            };\n            namespace\n                .and_then(|namespace| attribute_tracker.query(&local_name, namespace))\n                .map_or_else(\n                    || {\n                        // Special case when fallback and <attr-type> are omitted.\n                        // See FAILURE: https://drafts.csswg.org/css-values-5/#attr-substitution\n                        if reference.fallback.is_none()\n                            && reference.attribute_data.kind == AttributeType::None\n                        {\n                            simple_attr_subst(\"\")\n                        } else {\n                            None\n                        }\n                    },\n                    |attr| {\n                        let attr = if let AttributeType::Type(_) = &reference.attribute_data.kind {\n                            // If we're evaluating a container query, we haven't run the cascade\n                            // and populated substitution_functions.attributes, so we can't do the\n                            // get_attr() lookup here.\n                            // TODO: This means chained attr() references will not work reliably in\n                            // container style queries:\n                            // https://bugzilla.mozilla.org/show_bug.cgi?id=2028861\n                            if computed_context.in_container_query {\n                                attr\n                            } else {\n                                substitution_functions\n                                    .get_attr(&reference.name)\n                                    .map(|v| v.to_variable_value())?\n                                    .css\n                            }\n                        } else {\n                            attr\n                        };\n                        let mut input = ParserInput::new(&attr);\n                        let mut parser = Parser::new(&mut input);\n                        match &reference.attribute_data.kind {\n                            AttributeType::Unit(unit) => {\n                                let css = {\n                                    // Verify that attribute data is a <number-token>.\n                                    parser.expect_number().ok()?;\n                                    let mut s = attr.clone();\n                                    s.push_str(unit.as_ref());\n                                    s\n                                };\n                                let serialization = match unit {\n                                    AttrUnit::Number => TokenSerializationType::Number,\n                                    AttrUnit::Percentage => TokenSerializationType::Percentage,\n                                    _ => TokenSerializationType::Dimension,\n                                };\n                                let value =\n                                    ComputedValue::new(css, url_data, serialization, serialization);\n                                Some(Substitution::from_value(\n                                    value, /* attr_tainted */ true,\n                                ))\n                            },\n                            AttributeType::Type(syntax) => {\n                                let value = SpecifiedRegisteredValue::parse(\n                                    &mut parser,\n                                    &syntax,\n                                    url_data,\n                                    None,\n                                    AllowComputationallyDependent::Yes,\n                                    AttrTaint::default(),\n                                )\n                                .ok()?;\n                                let value = value.to_variable_value();\n                                Some(Substitution::from_value(\n                                    value, /* attr_tainted */ true,\n                                ))\n                            },\n                            AttributeType::RawString | AttributeType::None => {\n                                simple_attr_subst(&attr)\n                            },\n                        }\n                    },\n                )\n        },\n    };\n\n    if let Some(s) = substitution {\n        // Skip references that are inside the outer variable (in fallback for example).\n        while references\n            .next_if(|next_ref| next_ref.end <= reference.end)\n            .is_some()\n        {}\n        return Ok(s);\n    }\n\n    let Some(ref fallback) = reference.fallback else {\n        return Err(());\n    };\n\n    do_substitute_chunk(\n        css,\n        fallback.start.get(),\n        reference.end - 1, // Skip the closing parenthesis of the reference value.\n        fallback.first_token_type,\n        fallback.last_token_type,\n        url_data,\n        substitution_functions,\n        stylist,\n        computed_context,\n        references,\n        attribute_tracker,\n        /* attr_taint */ None,\n    )\n}\n\n/// Replace `var()`, `env()`, and `attr()` functions. Return `Err(..)` for invalid at computed time.\nfn substitute_internal<'a>(\n    variable_value: &'a VariableValue,\n    substitution_functions: &'a ComputedSubstitutionFunctions,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    attribute_tracker: &mut AttributeTracker,\n    mut attr_taint: Option<&mut AttrTaint>,\n) -> Result<Substitution<'a>, ()> {\n    let mut refs = variable_value.references.refs.iter().peekable();\n    do_substitute_chunk(\n        &variable_value.css,\n        /* start = */ 0,\n        /* end = */ variable_value.css.len(),\n        variable_value.first_token_type,\n        variable_value.last_token_type,\n        &variable_value.url_data,\n        substitution_functions,\n        stylist,\n        computed_context,\n        &mut refs,\n        attribute_tracker,\n        attr_taint.as_deref_mut(),\n    )\n}\n\n/// Replace var(), env(), and attr() functions, returning the resulting CSS string.\npub fn substitute<'a>(\n    variable_value: &'a VariableValue,\n    substitution_functions: &'a ComputedSubstitutionFunctions,\n    stylist: &Stylist,\n    computed_context: &computed::Context,\n    attribute_tracker: &mut AttributeTracker,\n) -> Result<SubstitutionResult<'a>, ()> {\n    debug_assert!(variable_value.has_references());\n    let mut attr_taint = AttrTaint::default();\n    let v = substitute_internal(\n        variable_value,\n        substitution_functions,\n        stylist,\n        computed_context,\n        attribute_tracker,\n        Some(&mut attr_taint),\n    )?;\n    Ok(SubstitutionResult {\n        css: v.css,\n        attr_taint,\n    })\n}\n"
  },
  {
    "path": "style/custom_properties_map.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The structure that contains the custom properties of a given element.\n\nuse crate::custom_properties::{Name, SubstitutionFunctionKind};\nuse crate::properties_and_values::value::ComputedValue as ComputedRegisteredValue;\nuse crate::selector_map::PrecomputedHasher;\nuse indexmap::{Equivalent, IndexMap};\nuse rustc_hash::FxBuildHasher;\nuse servo_arc::Arc;\nuse std::hash::{BuildHasherDefault, Hash};\nuse std::sync::LazyLock;\n\n/// A map for a set of custom properties, which implements copy-on-write behavior on insertion with\n/// cheap copying.\n#[derive(Clone, Debug, PartialEq)]\npub struct CustomPropertiesMap(Arc<Inner>);\n\nimpl Default for CustomPropertiesMap {\n    fn default() -> Self {\n        Self(EMPTY.clone())\n    }\n}\n\n/// We use None in the value to represent a removed entry.\npub type OwnMap =\n    IndexMap<Name, Option<ComputedRegisteredValue>, BuildHasherDefault<PrecomputedHasher>>;\n\nstatic EMPTY: LazyLock<Arc<Inner>> = LazyLock::new(|| {\n    Arc::new_leaked(Inner {\n        own_properties: Default::default(),\n        parent: None,\n        len: 0,\n        ancestor_count: 0,\n    })\n});\n\n#[derive(Debug, Clone)]\nstruct Inner {\n    own_properties: OwnMap,\n    parent: Option<Arc<Inner>>,\n    /// The number of custom properties we store. Note that this is different from the sum of our\n    /// own and our parent's length, since we might store duplicate entries.\n    len: usize,\n    /// The number of ancestors we have.\n    ancestor_count: u8,\n}\n\n/// A not-too-large, not too small ancestor limit, to prevent creating too-big chains.\nconst ANCESTOR_COUNT_LIMIT: usize = 4;\n\n/// An iterator over the custom properties.\npub struct Iter<'a> {\n    current: &'a Inner,\n    current_iter: indexmap::map::Iter<'a, Name, Option<ComputedRegisteredValue>>,\n    descendants: smallvec::SmallVec<[&'a Inner; ANCESTOR_COUNT_LIMIT]>,\n}\n\nimpl<'a> Iterator for Iter<'a> {\n    type Item = (&'a Name, &'a Option<ComputedRegisteredValue>);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            let (name, value) = match self.current_iter.next() {\n                Some(v) => v,\n                None => {\n                    let parent = self.current.parent.as_deref()?;\n                    self.descendants.push(self.current);\n                    self.current = parent;\n                    self.current_iter = parent.own_properties.iter();\n                    continue;\n                },\n            };\n            // If the property is overridden by a descendant we've already visited it.\n            for descendant in &self.descendants {\n                if descendant.own_properties.contains_key(name) {\n                    continue;\n                }\n            }\n            return Some((name, value));\n        }\n    }\n}\n\nimpl PartialEq for Inner {\n    fn eq(&self, other: &Self) -> bool {\n        if self.len != other.len {\n            return false;\n        }\n        // NOTE(emilio): In order to speed up custom property comparison when tons of custom\n        // properties are involved, we return false in some cases where the ordering might be\n        // different, but the computed values end up being the same.\n        //\n        // This is a performance trade-off, on the assumption that if the ordering is different,\n        // there's likely a different value as well, but might over-invalidate.\n        //\n        // Doing the slow thing (checking all the keys) shows up a lot in profiles, see\n        // bug 1926423.\n        //\n        // Note that self.own_properties != other.own_properties is not the same, as by default\n        // IndexMap comparison is not order-aware.\n        if self.own_properties.as_slice() != other.own_properties.as_slice() {\n            return false;\n        }\n        self.parent == other.parent\n    }\n}\n\nimpl Inner {\n    fn iter(&self) -> Iter<'_> {\n        Iter {\n            current: self,\n            current_iter: self.own_properties.iter(),\n            descendants: Default::default(),\n        }\n    }\n\n    fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n\n    fn len(&self) -> usize {\n        self.len\n    }\n\n    fn get(&self, name: &Name) -> Option<&ComputedRegisteredValue> {\n        if let Some(p) = self.own_properties.get(name) {\n            return p.as_ref();\n        }\n        self.parent.as_ref()?.get(name)\n    }\n\n    fn insert(&mut self, name: &Name, value: Option<ComputedRegisteredValue>) {\n        let new = self.own_properties.insert(name.clone(), value).is_none();\n        if new && self.parent.as_ref().map_or(true, |p| p.get(name).is_none()) {\n            self.len += 1;\n        }\n    }\n\n    /// Whether we should expand the chain, or just copy-on-write.\n    fn should_expand_chain(&self) -> bool {\n        const SMALL_THRESHOLD: usize = 8;\n        if self.own_properties.len() <= SMALL_THRESHOLD {\n            return false; // Just copy, to avoid very long chains.\n        }\n        self.ancestor_count < ANCESTOR_COUNT_LIMIT as u8\n    }\n}\n\nimpl CustomPropertiesMap {\n    /// Returns whether the map has no properties in it.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Returns the amount of different properties in the map.\n    pub fn len(&self) -> usize {\n        self.0.len()\n    }\n\n    /// Returns the property name and value at a given index.\n    pub fn get_index(&self, index: usize) -> Option<(&Name, &Option<ComputedRegisteredValue>)> {\n        if index >= self.len() {\n            return None;\n        }\n        // FIXME: This is O(n) which is a bit unfortunate.\n        self.0.iter().nth(index)\n    }\n\n    /// Returns a given property value by name.\n    pub fn get(&self, name: &Name) -> Option<&ComputedRegisteredValue> {\n        self.0.get(name)\n    }\n\n    fn do_insert(&mut self, name: &Name, value: Option<ComputedRegisteredValue>) {\n        if let Some(inner) = Arc::get_mut(&mut self.0) {\n            return inner.insert(name, value);\n        }\n        if self.get(name) == value.as_ref() {\n            return;\n        }\n        if !self.0.should_expand_chain() {\n            return Arc::make_mut(&mut self.0).insert(name, value);\n        }\n        let len = self.0.len;\n        let ancestor_count = self.0.ancestor_count + 1;\n        let mut new_inner = Inner {\n            own_properties: Default::default(),\n            // FIXME: Would be nice to avoid this clone.\n            parent: Some(self.0.clone()),\n            len,\n            ancestor_count,\n        };\n        new_inner.insert(name, value);\n        self.0 = Arc::new(new_inner);\n    }\n\n    /// Inserts an element in the map.\n    pub fn insert(&mut self, name: &Name, value: ComputedRegisteredValue) {\n        self.do_insert(name, Some(value))\n    }\n\n    /// Removes an element from the map.\n    pub fn remove(&mut self, name: &Name) {\n        self.do_insert(name, None)\n    }\n\n    /// Shrinks the map as much as possible.\n    pub fn shrink_to_fit(&mut self) {\n        if let Some(inner) = Arc::get_mut(&mut self.0) {\n            inner.own_properties.shrink_to_fit()\n        }\n    }\n\n    /// Return iterator to go through all properties.\n    pub fn iter(&self) -> Iter<'_> {\n        self.0.iter()\n    }\n}\n\n/// An `IndexMap` containing both custom properties and attributes.\n#[derive(Clone, Debug, Default, PartialEq)]\npub struct AllSubstitutionFunctions(IndexMap<Key, ComputedRegisteredValue, FxBuildHasher>);\n\n#[derive(Clone, Debug, Hash, Eq, PartialEq)]\nstruct Key(Name, SubstitutionFunctionKind);\n\n#[derive(Clone, Debug, Hash, Eq, PartialEq)]\nstruct KeyRef<'a>(&'a Name, SubstitutionFunctionKind);\n\nimpl<'a> Equivalent<Key> for KeyRef<'a> {\n    fn equivalent(&self, key: &Key) -> bool {\n        *self.0 == key.0 && self.1 == key.1\n    }\n}\n\nimpl AllSubstitutionFunctions {\n    /// Returns whether the map has zero properties and attributes in it.\n    #[inline(always)]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Returns a custom property or attribute value by name.\n    #[inline(always)]\n    pub fn get(\n        &self,\n        name: &Name,\n        kind: SubstitutionFunctionKind,\n    ) -> Option<&ComputedRegisteredValue> {\n        debug_assert_ne!(kind, SubstitutionFunctionKind::Env);\n        self.0.get(&KeyRef(name, kind))\n    }\n\n    /// Inserts an element into the map.\n    #[inline(always)]\n    pub fn insert(\n        &mut self,\n        name: &Name,\n        kind: SubstitutionFunctionKind,\n        value: ComputedRegisteredValue,\n    ) {\n        debug_assert_ne!(kind, SubstitutionFunctionKind::Env);\n        let k = Key(name.clone(), kind);\n        self.0.insert(k, value);\n    }\n\n    /// Returns iterator to go through all substitution functions in insertion order.\n    #[inline(always)]\n    pub fn iter(\n        &self,\n    ) -> impl Iterator<Item = (&Name, SubstitutionFunctionKind, &ComputedRegisteredValue)> {\n        self.0.iter().map(|(k, v)| (&k.0, k.1, v))\n    }\n}\n"
  },
  {
    "path": "style/data.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Per-node data used in style calculation.\n\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::{SharedStyleContext, StackLimitChecker};\nuse crate::dom::TElement;\nuse crate::invalidation::element::invalidator::InvalidationResult;\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::properties::ComputedValues;\nuse crate::selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT};\nuse crate::style_resolver::{PrimaryStyle, ResolvedElementStyles, ResolvedStyle};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::MallocSizeOfOps;\nuse selectors::matching::SelectorCaches;\nuse servo_arc::Arc;\nuse std::ops::{Deref, DerefMut};\nuse std::{fmt, mem};\n\n#[cfg(debug_assertions)]\nuse atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};\n\nbitflags! {\n    /// Various flags stored on ElementData.\n    #[derive(Debug, Default)]\n    pub struct ElementDataFlags: u8 {\n        /// Whether the styles changed for this restyle.\n        const WAS_RESTYLED = 1 << 0;\n        /// Whether the last traversal of this element did not do\n        /// any style computation. This is not true during the initial\n        /// styling pass, nor is it true when we restyle (in which case\n        /// WAS_RESTYLED is set).\n        ///\n        /// This bit always corresponds to the last time the element was\n        /// traversed, so each traversal simply updates it with the appropriate\n        /// value.\n        const TRAVERSED_WITHOUT_STYLING = 1 << 1;\n\n        /// Whether the primary style of this element data was reused from\n        /// another element via a rule node comparison. This allows us to\n        /// differentiate between elements that shared styles because they met\n        /// all the criteria of the style sharing cache, compared to elements\n        /// that reused style structs via rule node identity.\n        ///\n        /// The former gives us stronger transitive guarantees that allows us to\n        /// apply the style sharing cache to cousins.\n        const PRIMARY_STYLE_REUSED_VIA_RULE_NODE = 1 << 2;\n    }\n}\n\n/// A lazily-allocated list of styles for eagerly-cascaded pseudo-elements.\n///\n/// We use an Arc so that sharing these styles via the style sharing cache does\n/// not require duplicate allocations. We leverage the copy-on-write semantics of\n/// Arc::make_mut(), which is free (i.e. does not require atomic RMU operations)\n/// in servo_arc.\n#[derive(Clone, Debug, Default)]\npub struct EagerPseudoStyles(Option<Arc<EagerPseudoArray>>);\n\n#[derive(Default)]\nstruct EagerPseudoArray(EagerPseudoArrayInner);\ntype EagerPseudoArrayInner = [Option<Arc<ComputedValues>>; EAGER_PSEUDO_COUNT];\n\nimpl Deref for EagerPseudoArray {\n    type Target = EagerPseudoArrayInner;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for EagerPseudoArray {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n// Manually implement `Clone` here because the derived impl of `Clone` for\n// array types assumes the value inside is `Copy`.\nimpl Clone for EagerPseudoArray {\n    fn clone(&self) -> Self {\n        let mut clone = Self::default();\n        for i in 0..EAGER_PSEUDO_COUNT {\n            clone[i] = self.0[i].clone();\n        }\n        clone\n    }\n}\n\n// Override Debug to print which pseudos we have, and substitute the rule node\n// for the much-more-verbose ComputedValues stringification.\nimpl fmt::Debug for EagerPseudoArray {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"EagerPseudoArray {{ \")?;\n        for i in 0..EAGER_PSEUDO_COUNT {\n            if let Some(ref values) = self[i] {\n                write!(\n                    f,\n                    \"{:?}: {:?}, \",\n                    PseudoElement::from_eager_index(i),\n                    &values.rules\n                )?;\n            }\n        }\n        write!(f, \"}}\")\n    }\n}\n\n// Can't use [None; EAGER_PSEUDO_COUNT] here because it complains\n// about Copy not being implemented for our Arc type.\nconst EMPTY_PSEUDO_ARRAY: &'static EagerPseudoArrayInner = &[None, None, None, None];\n\nimpl EagerPseudoStyles {\n    /// Returns whether there are any pseudo styles.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_none()\n    }\n\n    /// Grabs a reference to the list of styles, if they exist.\n    pub fn as_optional_array(&self) -> Option<&EagerPseudoArrayInner> {\n        match self.0 {\n            None => None,\n            Some(ref x) => Some(&x.0),\n        }\n    }\n\n    /// Grabs a reference to the list of styles or a list of None if\n    /// there are no styles to be had.\n    pub fn as_array(&self) -> &EagerPseudoArrayInner {\n        self.as_optional_array().unwrap_or(EMPTY_PSEUDO_ARRAY)\n    }\n\n    /// Returns a reference to the style for a given eager pseudo, if it exists.\n    pub fn get(&self, pseudo: &PseudoElement) -> Option<&Arc<ComputedValues>> {\n        debug_assert!(pseudo.is_eager());\n        self.0\n            .as_ref()\n            .and_then(|p| p[pseudo.eager_index()].as_ref())\n    }\n\n    /// Sets the style for the eager pseudo.\n    pub fn set(&mut self, pseudo: &PseudoElement, value: Arc<ComputedValues>) {\n        if self.0.is_none() {\n            self.0 = Some(Arc::new(Default::default()));\n        }\n        let arr = Arc::make_mut(self.0.as_mut().unwrap());\n        arr[pseudo.eager_index()] = Some(value);\n    }\n}\n\n/// The styles associated with a node, including the styles for any\n/// pseudo-elements.\n#[derive(Clone, Default)]\npub struct ElementStyles {\n    /// The element's style.\n    pub primary: Option<Arc<ComputedValues>>,\n    /// A list of the styles for the element's eagerly-cascaded pseudo-elements.\n    pub pseudos: EagerPseudoStyles,\n}\n\n// There's one of these per rendered elements so it better be small.\nsize_of_test!(ElementStyles, 16);\n\n/// Information on how this element uses viewport units.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]\npub enum ViewportUnitUsage {\n    /// No viewport units are used.\n    None = 0,\n    /// There are viewport units used from regular style rules (which means we\n    /// should re-cascade).\n    FromDeclaration,\n    /// There are viewport units used from container queries (which means we\n    /// need to re-selector-match).\n    FromQuery,\n}\n\nimpl ElementStyles {\n    /// Returns the primary style.\n    pub fn get_primary(&self) -> Option<&Arc<ComputedValues>> {\n        self.primary.as_ref()\n    }\n\n    /// Returns the primary style.  Panic if no style available.\n    pub fn primary(&self) -> &Arc<ComputedValues> {\n        self.primary.as_ref().unwrap()\n    }\n\n    /// Whether this element `display` value is `none`.\n    pub fn is_display_none(&self) -> bool {\n        self.primary().get_box().clone_display().is_none()\n    }\n\n    /// Whether this element uses viewport units.\n    pub fn viewport_unit_usage(&self) -> ViewportUnitUsage {\n        fn usage_from_flags(flags: ComputedValueFlags) -> ViewportUnitUsage {\n            if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES) {\n                return ViewportUnitUsage::FromQuery;\n            }\n            if flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) {\n                return ViewportUnitUsage::FromDeclaration;\n            }\n            ViewportUnitUsage::None\n        }\n\n        let primary = self.primary();\n        let mut usage = usage_from_flags(primary.flags);\n\n        // Check cached lazy pseudos on the primary style.\n        primary.each_cached_lazy_pseudo(|style| {\n            usage = std::cmp::max(usage, usage_from_flags(style.flags));\n        });\n\n        for pseudo_style in self.pseudos.as_array() {\n            if let Some(ref pseudo_style) = pseudo_style {\n                usage = std::cmp::max(usage, usage_from_flags(pseudo_style.flags));\n                // Also check cached lazy pseudos on eager pseudo styles.\n                pseudo_style.each_cached_lazy_pseudo(|style| {\n                    usage = std::cmp::max(usage, usage_from_flags(style.flags));\n                });\n            }\n        }\n\n        usage\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        // As the method name suggests, we don't measures the ComputedValues\n        // here, because they are measured on the C++ side.\n\n        // XXX: measure the EagerPseudoArray itself, but not the ComputedValues\n        // within it.\n\n        0\n    }\n}\n\n// We manually implement Debug for ElementStyles so that we can avoid the\n// verbose stringification of every property in the ComputedValues. We\n// substitute the rule node instead.\nimpl fmt::Debug for ElementStyles {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(\n            f,\n            \"ElementStyles {{ primary: {:?}, pseudos: {:?} }}\",\n            self.primary.as_ref().map(|x| &x.rules),\n            self.pseudos\n        )\n    }\n}\n\n/// Style system data associated with an Element.\n///\n/// In Gecko, this hangs directly off the Element. Servo, this is embedded\n/// inside of layout data, which itself hangs directly off the Element. In\n/// both cases, it is wrapped inside an AtomicRefCell to ensure thread safety.\n#[derive(Debug, Default)]\npub struct ElementData {\n    /// The styles for the element and its pseudo-elements.\n    pub styles: ElementStyles,\n\n    /// The restyle damage, indicating what kind of layout changes are required\n    /// afte restyling.\n    pub damage: RestyleDamage,\n\n    /// The restyle hint, which indicates whether selectors need to be rematched\n    /// for this element, its children, and its descendants.\n    pub hint: RestyleHint,\n\n    /// Flags.\n    pub flags: ElementDataFlags,\n}\n\n/// A struct that wraps ElementData, giving it the ability of doing thread-safety checks.\n#[derive(Debug, Default)]\npub struct ElementDataWrapper {\n    inner: std::cell::UnsafeCell<ElementData>,\n    /// Implements optional (debug_assertions-only) thread-safety checking.\n    #[cfg(debug_assertions)]\n    refcell: AtomicRefCell<()>,\n}\n\n/// A read-only reference to ElementData.\n#[derive(Debug)]\npub struct ElementDataMut<'a> {\n    v: &'a mut ElementData,\n    #[cfg(debug_assertions)]\n    _borrow: AtomicRefMut<'a, ()>,\n}\n\n/// A mutable reference to ElementData.\n#[derive(Debug)]\npub struct ElementDataRef<'a> {\n    v: &'a ElementData,\n    #[cfg(debug_assertions)]\n    _borrow: AtomicRef<'a, ()>,\n}\n\nimpl ElementDataWrapper {\n    /// Gets a non-exclusive reference to this ElementData.\n    #[inline(always)]\n    pub fn borrow(&self) -> ElementDataRef<'_> {\n        #[cfg(debug_assertions)]\n        let borrow = self.refcell.borrow();\n        ElementDataRef {\n            v: unsafe { &*self.inner.get() },\n            #[cfg(debug_assertions)]\n            _borrow: borrow,\n        }\n    }\n\n    /// Gets an exclusive reference to this ElementData.\n    #[inline(always)]\n    pub fn borrow_mut(&self) -> ElementDataMut<'_> {\n        #[cfg(debug_assertions)]\n        let borrow = self.refcell.borrow_mut();\n        ElementDataMut {\n            v: unsafe { &mut *self.inner.get() },\n            #[cfg(debug_assertions)]\n            _borrow: borrow,\n        }\n    }\n}\n\nimpl<'a> Deref for ElementDataRef<'a> {\n    type Target = ElementData;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &*self.v\n    }\n}\n\nimpl<'a> Deref for ElementDataMut<'a> {\n    type Target = ElementData;\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &*self.v\n    }\n}\n\nimpl<'a> DerefMut for ElementDataMut<'a> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut *self.v\n    }\n}\n\n// There's one of these per rendered elements so it better be small.\nsize_of_test!(ElementData, 24);\n\n/// The kind of restyle that a single element should do.\n#[derive(Debug)]\npub enum RestyleKind {\n    /// We need to run selector matching plus re-cascade, that is, a full\n    /// restyle.\n    MatchAndCascade,\n    /// We need to recascade with some replacement rule, such as the style\n    /// attribute, or animation rules.\n    CascadeWithReplacements(RestyleHint),\n    /// We only need to recascade, for example, because only inherited\n    /// properties in the parent changed.\n    CascadeOnly,\n}\n\nfn needs_to_match_self(hint: RestyleHint, style: &ComputedValues) -> bool {\n    if hint.intersects(RestyleHint::RESTYLE_SELF) {\n        return true;\n    }\n    if hint.intersects(RestyleHint::RESTYLE_SELF_IF_PSEUDO) && style.is_pseudo_style() {\n        return true;\n    }\n    if hint.intersects(RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS)\n        && style\n            .flags\n            .contains(ComputedValueFlags::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY)\n    {\n        return true;\n    }\n    hint.intersects(\n        RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES\n            | RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER,\n    ) && style\n        .flags\n        .contains(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY)\n}\n\nimpl ElementData {\n    /// Invalidates style for this element, its descendants, and later siblings,\n    /// based on the snapshot of the element that we took when attributes or\n    /// state changed.\n    pub fn invalidate_style_if_needed<'a, E: TElement>(\n        &mut self,\n        element: E,\n        shared_context: &SharedStyleContext,\n        stack_limit_checker: Option<&StackLimitChecker>,\n        selector_caches: &'a mut SelectorCaches,\n    ) -> InvalidationResult {\n        // In animation-only restyle we shouldn't touch snapshot at all.\n        if shared_context.traversal_flags.for_animation_only() {\n            return InvalidationResult::empty();\n        }\n\n        use crate::invalidation::element::invalidator::TreeStyleInvalidator;\n        use crate::invalidation::element::state_and_attributes::StateAndAttrInvalidationProcessor;\n\n        debug!(\n            \"invalidate_style_if_needed: {:?}, flags: {:?}, has_snapshot: {}, \\\n             handled_snapshot: {}, pseudo: {:?}\",\n            element,\n            shared_context.traversal_flags,\n            element.has_snapshot(),\n            element.handled_snapshot(),\n            element.implemented_pseudo_element()\n        );\n\n        if !element.has_snapshot() || element.handled_snapshot() {\n            return InvalidationResult::empty();\n        }\n\n        let mut processor =\n            StateAndAttrInvalidationProcessor::new(shared_context, element, self, selector_caches);\n\n        let invalidator = TreeStyleInvalidator::new(element, stack_limit_checker, &mut processor);\n\n        let result = invalidator.invalidate();\n\n        unsafe { element.set_handled_snapshot() }\n        debug_assert!(element.handled_snapshot());\n\n        result\n    }\n\n    /// Returns true if this element has styles.\n    #[inline]\n    pub fn has_styles(&self) -> bool {\n        self.styles.primary.is_some()\n    }\n\n    /// Returns this element's styles as resolved styles to use for sharing.\n    pub fn share_styles(&self) -> ResolvedElementStyles {\n        ResolvedElementStyles {\n            primary: self.share_primary_style(),\n            pseudos: self.styles.pseudos.clone(),\n        }\n    }\n\n    /// Returns this element's primary style as a resolved style to use for sharing.\n    pub fn share_primary_style(&self) -> PrimaryStyle {\n        let reused_via_rule_node = self\n            .flags\n            .contains(ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE);\n\n        PrimaryStyle {\n            style: ResolvedStyle(self.styles.primary().clone()),\n            reused_via_rule_node,\n        }\n    }\n\n    /// Return a copy of the element's primary style as a resolved style with the\n    /// given flags.\n    pub fn clone_style_with_flags(&self, flags: ComputedValueFlags) -> ResolvedStyle {\n        let primary_style = self.styles.primary();\n        // We are only using this pseudo to find the correct pseudo type so it\n        // does not matter it technically belongs to a different style.\n        let pseudo = primary_style.pseudo();\n        ResolvedStyle(\n            primary_style\n                .deref()\n                .clone_with_flags(flags, pseudo.as_ref()),\n        )\n    }\n\n    /// Sets a new set of styles, returning the old ones.\n    pub fn set_styles(&mut self, new_styles: ResolvedElementStyles) -> ElementStyles {\n        self.flags.set(\n            ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,\n            new_styles.primary.reused_via_rule_node,\n        );\n        mem::replace(&mut self.styles, new_styles.into())\n    }\n\n    /// Returns the kind of restyling that we're going to need to do on this\n    /// element, based of the stored restyle hint.\n    pub fn restyle_kind(&self, shared_context: &SharedStyleContext) -> Option<RestyleKind> {\n        let style = match self.styles.primary {\n            Some(ref s) => s,\n            None => return Some(RestyleKind::MatchAndCascade),\n        };\n\n        if shared_context.traversal_flags.for_animation_only() {\n            return self.restyle_kind_for_animation(shared_context);\n        }\n\n        let hint = self.hint;\n        if hint.is_empty() {\n            return None;\n        }\n\n        if needs_to_match_self(hint, style) {\n            return Some(RestyleKind::MatchAndCascade);\n        }\n\n        if hint.has_replacements() {\n            debug_assert!(\n                !hint.has_animation_hint(),\n                \"Animation only restyle hint should have already processed\"\n            );\n            return Some(RestyleKind::CascadeWithReplacements(\n                hint & RestyleHint::replacements(),\n            ));\n        }\n\n        let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF)\n            || (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE)\n                && style\n                    .flags\n                    .contains(ComputedValueFlags::INHERITS_RESET_STYLE));\n        if needs_to_recascade_self {\n            return Some(RestyleKind::CascadeOnly);\n        }\n\n        None\n    }\n\n    /// Returns the kind of restyling for animation-only restyle.\n    fn restyle_kind_for_animation(\n        &self,\n        shared_context: &SharedStyleContext,\n    ) -> Option<RestyleKind> {\n        debug_assert!(shared_context.traversal_flags.for_animation_only());\n        debug_assert!(self.has_styles());\n\n        // FIXME: We should ideally restyle here, but it is a hack to work around our weird\n        // animation-only traversal stuff: If we're display: none and the rules we could\n        // match could change, we consider our style up-to-date. This is because re-cascading with\n        // and old style doesn't guarantee returning the correct animation style (that's\n        // bug 1393323). So if our display changed, and it changed from display: none, we would\n        // incorrectly forget about it and wouldn't be able to correctly style our descendants\n        // later.\n        // XXX Figure out if this still makes sense.\n        let hint = self.hint;\n        if self.styles.is_display_none() && hint.intersects(RestyleHint::RESTYLE_SELF) {\n            return None;\n        }\n\n        let style = self.styles.primary();\n        // Return either CascadeWithReplacements or CascadeOnly in case of animation-only restyle.\n        // I.e. animation-only restyle never does selector matching.\n        if hint.has_animation_hint() {\n            return Some(RestyleKind::CascadeWithReplacements(\n                hint & RestyleHint::for_animations(),\n            ));\n        }\n\n        let needs_to_recascade_self = hint.intersects(RestyleHint::RECASCADE_SELF)\n            || (hint.intersects(RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE)\n                && style\n                    .flags\n                    .contains(ComputedValueFlags::INHERITS_RESET_STYLE));\n        if needs_to_recascade_self {\n            return Some(RestyleKind::CascadeOnly);\n        }\n        return None;\n    }\n\n    /// Drops any restyle state from the element.\n    ///\n    /// FIXME(bholley): The only caller of this should probably just assert that the hint is empty\n    /// and call clear_flags_and_damage().\n    #[inline]\n    pub fn clear_restyle_state(&mut self) {\n        self.hint = RestyleHint::empty();\n        self.clear_restyle_flags_and_damage();\n    }\n\n    /// Drops restyle flags and damage from the element.\n    #[inline]\n    pub fn clear_restyle_flags_and_damage(&mut self) {\n        self.damage = RestyleDamage::empty();\n        self.flags.remove(ElementDataFlags::WAS_RESTYLED);\n    }\n\n    /// Mark this element as restyled, which is useful to know whether we need\n    /// to do a post-traversal.\n    pub fn set_restyled(&mut self) {\n        self.flags.insert(ElementDataFlags::WAS_RESTYLED);\n        self.flags\n            .remove(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);\n    }\n\n    /// Returns true if this element was restyled.\n    #[inline]\n    pub fn is_restyle(&self) -> bool {\n        self.flags.contains(ElementDataFlags::WAS_RESTYLED)\n    }\n\n    /// Mark that we traversed this element without computing any style for it.\n    pub fn set_traversed_without_styling(&mut self) {\n        self.flags\n            .insert(ElementDataFlags::TRAVERSED_WITHOUT_STYLING);\n    }\n\n    /// Returns whether this element has been part of a restyle.\n    #[inline]\n    pub fn contains_restyle_data(&self) -> bool {\n        self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()\n    }\n\n    /// Returns whether it is safe to perform cousin sharing based on the ComputedValues\n    /// identity of the primary style in this ElementData. There are a few subtle things\n    /// to check.\n    ///\n    /// First, if a parent element was already styled and we traversed past it without\n    /// restyling it, that may be because our clever invalidation logic was able to prove\n    /// that the styles of that element would remain unchanged despite changes to the id\n    /// or class attributes. However, style sharing relies on the strong guarantee that all\n    /// the classes and ids up the respective parent chains are identical. As such, if we\n    /// skipped styling for one (or both) of the parents on this traversal, we can't share\n    /// styles across cousins. Note that this is a somewhat conservative check. We could\n    /// tighten it by having the invalidation logic explicitly flag elements for which it\n    /// ellided styling.\n    ///\n    /// Second, we want to only consider elements whose ComputedValues match due to a hit\n    /// in the style sharing cache, rather than due to the rule-node-based reuse that\n    /// happens later in the styling pipeline. The former gives us the stronger guarantees\n    /// we need for style sharing, the latter does not.\n    pub fn safe_for_cousin_sharing(&self) -> bool {\n        if self.flags.intersects(\n            ElementDataFlags::TRAVERSED_WITHOUT_STYLING\n                | ElementDataFlags::PRIMARY_STYLE_REUSED_VIA_RULE_NODE,\n        ) {\n            return false;\n        }\n        if !self\n            .styles\n            .primary()\n            .get_box()\n            .clone_container_type()\n            .is_normal()\n        {\n            return false;\n        }\n        true\n    }\n\n    /// Measures memory usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of_excluding_cvs(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let n = self.styles.size_of_excluding_cvs(ops);\n\n        // We may measure more fields in the future if DMD says it's worth it.\n\n        n\n    }\n}\n"
  },
  {
    "path": "style/device/gecko.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko-specific logic for [`Device`].\n\nuse crate::color::AbsoluteColor;\nuse crate::context::QuirksMode;\nuse crate::custom_properties::CssEnvironment;\nuse crate::device::Device;\nuse crate::font_metrics::FontMetrics;\nuse crate::gecko::wrapper::GeckoElement;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs;\nuse crate::logical_geometry::WritingMode;\nuse crate::media_queries::MediaType;\nuse crate::properties::ComputedValues;\nuse crate::string_cache::Atom;\nuse crate::values::computed::font::GenericFontFamily;\nuse crate::values::computed::{ColorScheme, Length, NonNegativeLength};\nuse crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor};\nuse crate::values::specified::font::{\n    QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX,\n    FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX,\n};\nuse crate::values::specified::ViewportVariant;\nuse crate::values::{CustomIdent, KeyframesName};\nuse app_units::{Au, AU_PER_PX};\nuse euclid::default::Size2D;\nuse euclid::{Scale, SideOffsets2D};\nuse parking_lot::RwLock;\nuse servo_arc::Arc;\nuse std::sync::atomic::{AtomicBool, AtomicU32, Ordering};\nuse std::{cmp, fmt};\nuse style_traits::{CSSPixel, DevicePixel};\n\npub(super) struct ExtraDeviceData {\n    /// NB: The document owns the styleset, who owns the stylist, and thus the\n    /// `Device`, so having a raw document pointer here is fine.\n    document: *const structs::Document,\n}\n\nunsafe impl Sync for Device {}\nunsafe impl Send for Device {}\n\nimpl Device {\n    /// Trivially constructs a new `Device`.\n    pub fn new(document: *const structs::Document) -> Self {\n        assert!(!document.is_null());\n        let doc = unsafe { &*document };\n        let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };\n        let default_values = ComputedValues::default_values(doc);\n        let root_style = RwLock::new(Arc::clone(&default_values));\n        Device {\n            default_values: default_values,\n            root_style: root_style,\n            root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),\n            root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()),\n            root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()),\n            root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()),\n            root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()),\n            root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()),\n            used_root_font_size: AtomicBool::new(false),\n            used_root_line_height: AtomicBool::new(false),\n            used_root_font_metrics: RwLock::new(false),\n            used_font_metrics: AtomicBool::new(false),\n            used_viewport_size: AtomicBool::new(false),\n            used_dynamic_viewport_size: AtomicBool::new(false),\n            environment: CssEnvironment,\n            // This gets updated when we see the <body>, so it doesn't really\n            // matter which color-scheme we look at here.\n            body_text_color: AtomicU32::new(prefs.mLightColors.mDefault),\n            extra: ExtraDeviceData { document },\n        }\n    }\n\n    /// Returns the computed line-height for the font in a given computed values instance.\n    ///\n    /// If you pass down an element, then the used line-height is returned.\n    pub fn calc_line_height(\n        &self,\n        font: &crate::properties::style_structs::Font,\n        writing_mode: WritingMode,\n        element: Option<GeckoElement>,\n    ) -> NonNegativeLength {\n        let pres_context = self.pres_context();\n        let line_height = font.clone_line_height();\n        let au = Au(unsafe {\n            bindings::Gecko_CalcLineHeight(\n                &line_height,\n                pres_context.map_or(std::ptr::null(), |pc| pc),\n                writing_mode.is_text_vertical(),\n                &**font,\n                element.map_or(std::ptr::null(), |e| e.0),\n            )\n        });\n        NonNegativeLength::new(au.to_f32_px())\n    }\n\n    /// Whether any animation name may be referenced from the style of any\n    /// element.\n    pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return false,\n        };\n\n        unsafe {\n            bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())\n        }\n    }\n\n    /// The quirks mode of the document.\n    pub fn quirks_mode(&self) -> QuirksMode {\n        self.document().mCompatMode.into()\n    }\n\n    /// Gets the base size given a generic font family and a language.\n    pub fn base_size_for_generic(&self, language: &Atom, generic: GenericFontFamily) -> Length {\n        unsafe { bindings::Gecko_GetBaseSize(self.document(), language.as_ptr(), generic) }\n    }\n\n    /// Gets the size of the scrollbar in CSS pixels.\n    pub fn scrollbar_inline_size(&self) -> Length {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            // XXX: we could have a more reasonable default perhaps.\n            None => return Length::new(0.0),\n        };\n        Length::new(unsafe { bindings::Gecko_GetScrollbarInlineSize(pc) })\n    }\n\n    /// Queries font metrics\n    pub fn query_font_metrics(\n        &self,\n        vertical: bool,\n        font: &crate::properties::style_structs::Font,\n        base_size: Length,\n        flags: QueryFontMetricsFlags,\n        track_usage: bool,\n    ) -> FontMetrics {\n        if track_usage {\n            self.used_font_metrics.store(true, Ordering::Relaxed);\n        }\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return Default::default(),\n        };\n        let gecko_metrics =\n            unsafe { bindings::Gecko_GetFontMetrics(pc, vertical, &**font, base_size, flags) };\n        FontMetrics {\n            x_height: Some(gecko_metrics.mXSize),\n            zero_advance_measure: if gecko_metrics.mChSize.px() >= 0. {\n                Some(gecko_metrics.mChSize)\n            } else {\n                None\n            },\n            cap_height: if gecko_metrics.mCapHeight.px() >= 0. {\n                Some(gecko_metrics.mCapHeight)\n            } else {\n                None\n            },\n            ic_width: if gecko_metrics.mIcWidth.px() >= 0. {\n                Some(gecko_metrics.mIcWidth)\n            } else {\n                None\n            },\n            ascent: gecko_metrics.mAscent,\n            script_percent_scale_down: if gecko_metrics.mScriptPercentScaleDown > 0. {\n                Some(gecko_metrics.mScriptPercentScaleDown)\n            } else {\n                None\n            },\n            script_script_percent_scale_down: if gecko_metrics.mScriptScriptPercentScaleDown > 0. {\n                Some(gecko_metrics.mScriptScriptPercentScaleDown)\n            } else {\n                None\n            },\n        }\n    }\n\n    /// Gets the document pointer.\n    #[inline]\n    pub fn document(&self) -> &structs::Document {\n        unsafe { &*self.extra.document }\n    }\n\n    /// Gets the pres context associated with this document.\n    #[inline]\n    pub fn pres_context(&self) -> Option<&structs::nsPresContext> {\n        unsafe {\n            self.document()\n                .mPresShell\n                .as_ref()?\n                .mPresContext\n                .mRawPtr\n                .as_ref()\n        }\n    }\n\n    /// Gets the preference stylesheet prefs for our document.\n    #[inline]\n    pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {\n        unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }\n    }\n\n    /// Recreates the default computed values.\n    pub fn reset_computed_values(&mut self) {\n        self.default_values = ComputedValues::default_values(self.document());\n    }\n\n    /// Rebuild all the cached data.\n    pub fn rebuild_cached_data(&mut self) {\n        self.reset_computed_values();\n        self.used_root_font_size.store(false, Ordering::Relaxed);\n        self.used_root_line_height.store(false, Ordering::Relaxed);\n        self.used_root_font_metrics = RwLock::new(false);\n        self.used_font_metrics.store(false, Ordering::Relaxed);\n        self.used_viewport_size.store(false, Ordering::Relaxed);\n        self.used_dynamic_viewport_size\n            .store(false, Ordering::Relaxed);\n    }\n\n    /// Recreates all the temporary state that the `Device` stores.\n    ///\n    /// This includes the viewport override from `@viewport` rules, and also the\n    /// default computed values.\n    pub fn reset(&mut self) {\n        self.reset_computed_values();\n    }\n\n    /// Returns whether this document is in print preview.\n    pub fn is_print_preview(&self) -> bool {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return false,\n        };\n        pc.mType == structs::nsPresContext_nsPresContextType_eContext_PrintPreview\n    }\n\n    /// Returns the current media type of the device.\n    pub fn media_type(&self) -> MediaType {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return MediaType::screen(),\n        };\n\n        // Gecko allows emulating random media with mMediaEmulationData.mMedium.\n        let medium_to_use = if !pc.mMediaEmulationData.mMedium.mRawPtr.is_null() {\n            pc.mMediaEmulationData.mMedium.mRawPtr\n        } else {\n            pc.mMedium as *const structs::nsAtom as *mut _\n        };\n\n        MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))\n    }\n\n    // It may make sense to account for @page rule margins here somehow, however\n    // it's not clear how that'd work, see:\n    // https://github.com/w3c/csswg-drafts/issues/5437\n    fn page_size_minus_default_margin(&self, pc: &structs::nsPresContext) -> Size2D<Au> {\n        debug_assert!(pc.mIsRootPaginatedDocument() != 0);\n        let area = &pc.mPageSize;\n        let margin = &pc.mDefaultPageMargin;\n        let width = area.width - margin.left - margin.right;\n        let height = area.height - margin.top - margin.bottom;\n        Size2D::new(Au(cmp::max(width, 0)), Au(cmp::max(height, 0)))\n    }\n\n    /// Returns the current viewport size in app units.\n    pub fn au_viewport_size(&self) -> Size2D<Au> {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return Size2D::new(Au(0), Au(0)),\n        };\n\n        if pc.mIsRootPaginatedDocument() != 0 {\n            return self.page_size_minus_default_margin(pc);\n        }\n\n        let area = &pc.mVisibleArea;\n        Size2D::new(Au(area.width), Au(area.height))\n    }\n\n    /// Returns the current viewport size in app units, recording that it's been\n    /// used for viewport unit resolution.\n    pub fn au_viewport_size_for_viewport_unit_resolution(\n        &self,\n        variant: ViewportVariant,\n    ) -> Size2D<Au> {\n        self.used_viewport_size.store(true, Ordering::Relaxed);\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return Size2D::new(Au(0), Au(0)),\n        };\n\n        if pc.mIsRootPaginatedDocument() != 0 {\n            return self.page_size_minus_default_margin(pc);\n        }\n\n        match variant {\n            ViewportVariant::UADefault => {\n                let size = &pc.mSizeForViewportUnits;\n                Size2D::new(Au(size.width), Au(size.height))\n            },\n            ViewportVariant::Small => {\n                let size = &pc.mVisibleArea;\n                Size2D::new(Au(size.width), Au(size.height))\n            },\n            ViewportVariant::Large => {\n                let size = &pc.mVisibleArea;\n                // Looks like IntCoordTyped is treated as if it's u32 in Rust.\n                debug_assert!(\n                    /* pc.mDynamicToolbarMaxHeight >=0 && */\n                    pc.mDynamicToolbarMaxHeight < i32::MAX as u32\n                );\n                Size2D::new(\n                    Au(size.width),\n                    Au(size.height\n                        + pc.mDynamicToolbarMaxHeight as i32 * pc.mCurAppUnitsPerDevPixel),\n                )\n            },\n            ViewportVariant::Dynamic => {\n                self.used_dynamic_viewport_size\n                    .store(true, Ordering::Relaxed);\n                let size = &pc.mVisibleArea;\n                // Looks like IntCoordTyped is treated as if it's u32 in Rust.\n                debug_assert!(\n                    /* pc.mDynamicToolbarHeight >=0 && */\n                    pc.mDynamicToolbarHeight < i32::MAX as u32\n                );\n                Size2D::new(\n                    Au(size.width),\n                    Au(size.height\n                        + (pc.mDynamicToolbarMaxHeight - pc.mDynamicToolbarHeight) as i32\n                            * pc.mCurAppUnitsPerDevPixel),\n                )\n            },\n        }\n    }\n\n    /// Returns whether visited styles are enabled.\n    pub fn visited_styles_enabled(&self) -> bool {\n        unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) }\n    }\n\n    /// Returns the number of app units per device pixel we're using currently.\n    pub fn app_units_per_device_pixel(&self) -> i32 {\n        match self.pres_context() {\n            Some(pc) => pc.mCurAppUnitsPerDevPixel,\n            None => AU_PER_PX,\n        }\n    }\n\n    /// Returns app units per pixel at 1x full-zoom.\n    fn app_units_per_device_pixel_at_unit_full_zoom(&self) -> i32 {\n        match self.pres_context() {\n            Some(pc) => unsafe { (*pc.mDeviceContext.mRawPtr).mAppUnitsPerDevPixelAtUnitFullZoom },\n            None => AU_PER_PX,\n        }\n    }\n\n    /// Returns the device pixel ratio, ignoring the full zoom factor.\n    pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> {\n        let au_per_px = AU_PER_PX as f32;\n        Scale::new(au_per_px / self.app_units_per_device_pixel_at_unit_full_zoom() as f32)\n    }\n\n    /// Returns the device pixel ratio.\n    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return Scale::new(1.),\n        };\n\n        if pc.mMediaEmulationData.mDPPX > 0.0 {\n            return Scale::new(pc.mMediaEmulationData.mDPPX);\n        }\n\n        let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;\n        let au_per_px = AU_PER_PX as f32;\n        Scale::new(au_per_px / au_per_dpx)\n    }\n\n    /// Returns whether document colors are enabled.\n    #[inline]\n    pub fn forced_colors(&self) -> ForcedColors {\n        self.pres_context()\n            .map_or(ForcedColors::None, |pc| pc.mForcedColors)\n    }\n\n    /// Computes a system color and returns it as an nscolor.\n    pub(crate) fn system_nscolor(\n        &self,\n        system_color: SystemColor,\n        color_scheme: ColorSchemeFlags,\n    ) -> u32 {\n        unsafe { bindings::Gecko_ComputeSystemColor(system_color, self.document(), &color_scheme) }\n    }\n\n    /// Returns whether the used color-scheme for `color-scheme` should be dark.\n    pub(crate) fn is_dark_color_scheme(&self, color_scheme: ColorSchemeFlags) -> bool {\n        unsafe { bindings::Gecko_IsDarkColorScheme(self.document(), &color_scheme) }\n    }\n\n    /// Returns the default background color.\n    ///\n    /// This is only for forced-colors/high-contrast, so looking at light colors\n    /// is ok.\n    pub fn default_background_color(&self) -> AbsoluteColor {\n        AbsoluteColor::from_nscolor(\n            self.system_nscolor(SystemColor::Canvas, ColorScheme::normal().bits),\n        )\n    }\n\n    /// Returns the default foreground color.\n    ///\n    /// See above for looking at light colors only.\n    pub fn default_color(&self) -> AbsoluteColor {\n        AbsoluteColor::from_nscolor(\n            self.system_nscolor(SystemColor::Canvastext, ColorScheme::normal().bits),\n        )\n    }\n\n    /// Returns the current effective text zoom.\n    #[inline]\n    fn text_zoom(&self) -> f32 {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return 1.,\n        };\n        pc.mTextZoom\n    }\n\n    /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).\n    #[inline]\n    pub fn zoom_text(&self, size: Length) -> Length {\n        size.scale_by(self.text_zoom())\n    }\n\n    /// Un-apply text zoom.\n    #[inline]\n    pub fn unzoom_text(&self, size: Length) -> Length {\n        size.scale_by(1. / self.text_zoom())\n    }\n\n    /// Returns safe area insets\n    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {\n        let pc = match self.pres_context() {\n            Some(pc) => pc,\n            None => return SideOffsets2D::zero(),\n        };\n        let mut top = 0.0;\n        let mut right = 0.0;\n        let mut bottom = 0.0;\n        let mut left = 0.0;\n        unsafe {\n            bindings::Gecko_GetSafeAreaInsets(pc, &mut top, &mut right, &mut bottom, &mut left)\n        };\n        SideOffsets2D::new(top, right, bottom, left)\n    }\n\n    /// Returns true if the given MIME type is supported\n    pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {\n        unsafe {\n            bindings::Gecko_IsSupportedImageMimeType(mime_type.as_ptr(), mime_type.len() as u32)\n        }\n    }\n\n    /// Return whether the document is a chrome document.\n    ///\n    /// This check is consistent with how we enable chrome rules for chrome:// and resource://\n    /// stylesheets (and thus chrome:// documents).\n    #[inline]\n    pub fn chrome_rules_enabled_for_document(&self) -> bool {\n        self.document().mChromeRulesEnabled()\n    }\n}\n\nimpl fmt::Debug for Device {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        use nsstring::nsCString;\n\n        let mut doc_uri = nsCString::new();\n        unsafe {\n            bindings::Gecko_nsIURI_Debug((*self.document()).mDocumentURI.raw(), &mut doc_uri)\n        };\n\n        f.debug_struct(\"Device\")\n            .field(\"document_url\", &doc_uri)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "style/device/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Media-query device and expression representation.\n\nuse crate::color::AbsoluteColor;\nuse crate::custom_properties::CssEnvironment;\n#[cfg(feature = \"servo\")]\nuse crate::derives::*;\nuse crate::properties::ComputedValues;\nuse crate::values::computed::font::QueryFontMetricsFlags;\nuse crate::values::computed::Length;\nuse parking_lot::RwLock;\nuse servo_arc::Arc;\nuse std::mem;\nuse std::sync::atomic::{AtomicBool, AtomicU32, Ordering};\n\n#[cfg(feature = \"gecko\")]\nuse crate::device::gecko::ExtraDeviceData;\n#[cfg(feature = \"servo\")]\nuse crate::device::servo::ExtraDeviceData;\n\n#[cfg(feature = \"gecko\")]\npub mod gecko;\n#[cfg(feature = \"servo\")]\npub mod servo;\n\n/// A device is a structure that represents the current media a given document\n/// is displayed in.\n///\n/// This is the struct against which media queries are evaluated, has a default\n/// values computed, and contains all the viewport rule state.\n///\n/// This structure also contains atomics used for computing root font-relative\n/// units. These atomics use relaxed ordering, since when computing the style\n/// of the root element, there can't be any other style being computed at the\n/// same time (given we need the style of the parent to compute everything else).\n///\n/// In Gecko, it wraps a pres context.\n#[cfg_attr(feature = \"servo\", derive(Debug, MallocSizeOf))]\npub struct Device {\n    /// The default computed values for this Device.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc is shared\")]\n    default_values: Arc<ComputedValues>,\n    /// Current computed style of the root element, used for calculations of\n    /// root font-relative units.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n    root_style: RwLock<Arc<ComputedValues>>,\n    /// Font size of the root element, used for rem units in other elements.\n    root_font_size: AtomicU32,\n    /// Line height of the root element, used for rlh units in other elements.\n    root_line_height: AtomicU32,\n    /// X-height of the root element, used for rex units in other elements.\n    root_font_metrics_ex: AtomicU32,\n    /// Cap-height of the root element, used for rcap units in other elements.\n    root_font_metrics_cap: AtomicU32,\n    /// Advance measure (ch) of the root element, used for rch units in other elements.\n    root_font_metrics_ch: AtomicU32,\n    /// Ideographic advance measure of the root element, used for ric units in other elements.\n    root_font_metrics_ic: AtomicU32,\n    /// Whether any styles computed in the document relied on the root font-size\n    /// by using rem units.\n    used_root_font_size: AtomicBool,\n    /// Whether any styles computed in the document relied on the root line-height\n    /// by using rlh units.\n    used_root_line_height: AtomicBool,\n    /// Whether any styles computed in the document relied on the root font metrics\n    /// by using rcap, rch, rex, or ric units. This is a lock instead of an atomic\n    /// in order to prevent concurrent writes to the root font metric values.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Pure stack type\")]\n    used_root_font_metrics: RwLock<bool>,\n    /// Whether any styles computed in the document relied on font metrics.\n    used_font_metrics: AtomicBool,\n    /// Whether any styles computed in the document relied on the viewport size\n    /// by using vw/vh/vmin/vmax units.\n    used_viewport_size: AtomicBool,\n    /// Whether any styles computed in the document relied on the viewport size\n    /// by using dvw/dvh/dvmin/dvmax units.\n    used_dynamic_viewport_size: AtomicBool,\n    /// The CssEnvironment object responsible of getting CSS environment\n    /// variables.\n    environment: CssEnvironment,\n    /// The body text color, stored as an `nscolor`, used for the \"tables\n    /// inherit from body\" quirk.\n    ///\n    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>\n    body_text_color: AtomicU32,\n\n    /// Extra Gecko-specific or Servo-specific data.\n    extra: ExtraDeviceData,\n}\n\nimpl Device {\n    /// Get the relevant environment to resolve `env()` functions.\n    #[inline]\n    pub fn environment(&self) -> &CssEnvironment {\n        &self.environment\n    }\n\n    /// Returns the default computed values as a reference, in order to match\n    /// Servo.\n    pub fn default_computed_values(&self) -> &ComputedValues {\n        &self.default_values\n    }\n\n    /// Returns the default computed values as an `Arc`.\n    pub fn default_computed_values_arc(&self) -> &Arc<ComputedValues> {\n        &self.default_values\n    }\n\n    /// Store a pointer to the root element's computed style, for use in\n    /// calculation of root font-relative metrics.\n    pub fn set_root_style(&self, style: &Arc<ComputedValues>) {\n        *self.root_style.write() = style.clone();\n    }\n\n    /// Get the font size of the root element (for rem)\n    pub fn root_font_size(&self) -> Length {\n        self.used_root_font_size.store(true, Ordering::Relaxed);\n        Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))\n    }\n\n    /// Set the font size of the root element (for rem), in zoom-independent CSS pixels.\n    pub fn set_root_font_size(&self, size: f32) {\n        self.root_font_size.store(size.to_bits(), Ordering::Relaxed)\n    }\n\n    /// Get the line height of the root element (for rlh)\n    pub fn root_line_height(&self) -> Length {\n        self.used_root_line_height.store(true, Ordering::Relaxed);\n        Length::new(f32::from_bits(\n            self.root_line_height.load(Ordering::Relaxed),\n        ))\n    }\n\n    /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels.\n    pub fn set_root_line_height(&self, size: f32) {\n        self.root_line_height\n            .store(size.to_bits(), Ordering::Relaxed);\n    }\n\n    /// Get the x-height of the root element (for rex)\n    pub fn root_font_metrics_ex(&self) -> Length {\n        self.ensure_root_font_metrics_updated();\n        Length::new(f32::from_bits(\n            self.root_font_metrics_ex.load(Ordering::Relaxed),\n        ))\n    }\n\n    /// Set the x-height of the root element (for rex), in zoom-independent CSS pixels.\n    pub fn set_root_font_metrics_ex(&self, size: f32) -> bool {\n        let size = size.to_bits();\n        let previous = self.root_font_metrics_ex.swap(size, Ordering::Relaxed);\n        previous != size\n    }\n\n    /// Get the cap-height of the root element (for rcap)\n    pub fn root_font_metrics_cap(&self) -> Length {\n        self.ensure_root_font_metrics_updated();\n        Length::new(f32::from_bits(\n            self.root_font_metrics_cap.load(Ordering::Relaxed),\n        ))\n    }\n\n    /// Set the cap-height of the root element (for rcap), in zoom-independent CSS pixels.\n    pub fn set_root_font_metrics_cap(&self, size: f32) -> bool {\n        let size = size.to_bits();\n        let previous = self.root_font_metrics_cap.swap(size, Ordering::Relaxed);\n        previous != size\n    }\n\n    /// Get the advance measure of the root element (for rch)\n    pub fn root_font_metrics_ch(&self) -> Length {\n        self.ensure_root_font_metrics_updated();\n        Length::new(f32::from_bits(\n            self.root_font_metrics_ch.load(Ordering::Relaxed),\n        ))\n    }\n\n    /// Set the advance measure of the root element (for rch), in zoom-independent CSS pixels.\n    pub fn set_root_font_metrics_ch(&self, size: f32) -> bool {\n        let size = size.to_bits();\n        let previous = self.root_font_metrics_ch.swap(size, Ordering::Relaxed);\n        previous != size\n    }\n\n    /// Get the ideographic advance measure of the root element (for ric)\n    pub fn root_font_metrics_ic(&self) -> Length {\n        self.ensure_root_font_metrics_updated();\n        Length::new(f32::from_bits(\n            self.root_font_metrics_ic.load(Ordering::Relaxed),\n        ))\n    }\n\n    /// Set the ideographic advance measure of the root element (for ric), in zoom-independent CSS pixels.\n    pub fn set_root_font_metrics_ic(&self, size: f32) -> bool {\n        let size = size.to_bits();\n        let previous = self.root_font_metrics_ic.swap(size, Ordering::Relaxed);\n        previous != size\n    }\n\n    fn ensure_root_font_metrics_updated(&self) {\n        let mut guard = self.used_root_font_metrics.write();\n        let previously_computed = mem::replace(&mut *guard, true);\n        if !previously_computed {\n            self.update_root_font_metrics();\n        }\n    }\n\n    /// Compute the root element's font metrics, and returns a bool indicating whether\n    /// the font metrics have changed since the previous restyle.\n    pub fn update_root_font_metrics(&self) -> bool {\n        let root_style = self.root_style.read();\n        let root_effective_zoom = (*root_style).effective_zoom;\n        let root_font_size = (*root_style).get_font().clone_font_size().computed_size();\n\n        let root_font_metrics = self.query_font_metrics(\n            (*root_style).writing_mode.is_upright(),\n            &(*root_style).get_font(),\n            root_font_size,\n            QueryFontMetricsFlags::USE_USER_FONT_SET\n                | QueryFontMetricsFlags::NEEDS_CH\n                | QueryFontMetricsFlags::NEEDS_IC,\n            /* track_usage = */ false,\n        );\n\n        let mut root_font_metrics_changed = false;\n        root_font_metrics_changed |= self.set_root_font_metrics_ex(\n            root_effective_zoom.unzoom(root_font_metrics.x_height_or_default(root_font_size).px()),\n        );\n        root_font_metrics_changed |= self.set_root_font_metrics_ch(\n            root_effective_zoom.unzoom(\n                root_font_metrics\n                    .zero_advance_measure_or_default(\n                        root_font_size,\n                        (*root_style).writing_mode.is_upright(),\n                    )\n                    .px(),\n            ),\n        );\n        root_font_metrics_changed |= self.set_root_font_metrics_cap(\n            root_effective_zoom.unzoom(root_font_metrics.cap_height_or_default().px()),\n        );\n        root_font_metrics_changed |= self.set_root_font_metrics_ic(\n            root_effective_zoom.unzoom(root_font_metrics.ic_width_or_default(root_font_size).px()),\n        );\n\n        root_font_metrics_changed\n    }\n\n    /// Returns whether we ever looked up the root font size of the Device.\n    pub fn used_root_font_size(&self) -> bool {\n        self.used_root_font_size.load(Ordering::Relaxed)\n    }\n\n    /// Returns whether we ever looked up the root line-height of the device.\n    pub fn used_root_line_height(&self) -> bool {\n        self.used_root_line_height.load(Ordering::Relaxed)\n    }\n\n    /// Returns whether we ever looked up the root font metrics of the device.\n    pub fn used_root_font_metrics(&self) -> bool {\n        *self.used_root_font_metrics.read()\n    }\n\n    /// Returns whether we ever looked up the viewport size of the Device.\n    pub fn used_viewport_size(&self) -> bool {\n        self.used_viewport_size.load(Ordering::Relaxed)\n    }\n\n    /// Returns whether we ever looked up the dynamic viewport size of the Device.\n    pub fn used_dynamic_viewport_size(&self) -> bool {\n        self.used_dynamic_viewport_size.load(Ordering::Relaxed)\n    }\n\n    /// Returns whether font metrics have been queried.\n    pub fn used_font_metrics(&self) -> bool {\n        self.used_font_metrics.load(Ordering::Relaxed)\n    }\n\n    /// Returns the body text color.\n    pub fn body_text_color(&self) -> AbsoluteColor {\n        AbsoluteColor::from_nscolor(self.body_text_color.load(Ordering::Relaxed))\n    }\n\n    /// Sets the body text color for the \"inherit color from body\" quirk.\n    ///\n    /// <https://quirks.spec.whatwg.org/#the-tables-inherit-color-from-body-quirk>\n    pub fn set_body_text_color(&self, color: AbsoluteColor) {\n        self.body_text_color\n            .store(color.to_nscolor(), Ordering::Relaxed)\n    }\n}\n"
  },
  {
    "path": "style/device/servo.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Servo-specific logic for [`Device`].\n\nuse crate::color::AbsoluteColor;\nuse crate::context::QuirksMode;\nuse crate::custom_properties::CssEnvironment;\nuse crate::font_metrics::FontMetrics;\nuse crate::logical_geometry::WritingMode;\nuse crate::media_queries::MediaType;\nuse crate::properties::style_structs::Font;\nuse crate::properties::ComputedValues;\nuse crate::queries::values::PrefersColorScheme;\nuse crate::values::computed::font::GenericFontFamily;\nuse crate::values::computed::{CSSPixelLength, Length, LineHeight, NonNegativeLength};\nuse crate::values::specified::color::{ColorSchemeFlags, ForcedColors, SystemColor};\nuse crate::values::specified::font::{\n    QueryFontMetricsFlags, FONT_MEDIUM_CAP_PX, FONT_MEDIUM_CH_PX, FONT_MEDIUM_EX_PX,\n    FONT_MEDIUM_IC_PX, FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX,\n};\nuse crate::values::specified::ViewportVariant;\nuse crate::values::KeyframesName;\nuse app_units::{Au, AU_PER_PX};\nuse euclid::default::Size2D as UntypedSize2D;\nuse euclid::{Scale, SideOffsets2D, Size2D};\nuse malloc_size_of_derive::MallocSizeOf;\nuse mime::Mime;\nuse parking_lot::RwLock;\nuse servo_arc::Arc;\nuse std::fmt::Debug;\nuse std::sync::atomic::{AtomicBool, AtomicU32, Ordering};\nuse style_traits::{CSSPixel, DevicePixel};\n\nuse crate::device::Device;\n\n/// A trait used to query font metrics in clients of Stylo. This is used by Device to\n/// query font metrics in a way that is specific to the client using Stylo.\npub trait FontMetricsProvider: Debug + Sync {\n    /// Query the font metrics for the given font and the given base font size.\n    fn query_font_metrics(\n        &self,\n        vertical: bool,\n        font: &Font,\n        base_size: CSSPixelLength,\n        flags: QueryFontMetricsFlags,\n    ) -> FontMetrics;\n    /// Gets the base size given a generic font family.\n    fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length;\n}\n\n#[derive(Debug, MallocSizeOf)]\npub(super) struct ExtraDeviceData {\n    /// The current media type used by de device.\n    media_type: MediaType,\n    /// The current viewport size, in CSS pixels.\n    viewport_size: Size2D<f32, CSSPixel>,\n    /// The current device pixel ratio, from CSS pixels to device pixels.\n    device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,\n    /// The current quirks mode.\n    #[ignore_malloc_size_of = \"Pure stack type\"]\n    quirks_mode: QuirksMode,\n    /// Whether the user prefers light mode or dark mode\n    #[ignore_malloc_size_of = \"Pure stack type\"]\n    prefers_color_scheme: PrefersColorScheme,\n    /// An implementation of a trait which implements support for querying font metrics.\n    #[ignore_malloc_size_of = \"Owned by embedder\"]\n    font_metrics_provider: Box<dyn FontMetricsProvider>,\n}\n\nimpl Device {\n    /// Trivially construct a new `Device`.\n    pub fn new(\n        media_type: MediaType,\n        quirks_mode: QuirksMode,\n        viewport_size: Size2D<f32, CSSPixel>,\n        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,\n        font_metrics_provider: Box<dyn FontMetricsProvider>,\n        default_values: Arc<ComputedValues>,\n        prefers_color_scheme: PrefersColorScheme,\n    ) -> Device {\n        let root_style = RwLock::new(Arc::clone(&default_values));\n        Device {\n            root_style,\n            root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),\n            root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()),\n            root_font_metrics_ex: AtomicU32::new(FONT_MEDIUM_EX_PX.to_bits()),\n            root_font_metrics_cap: AtomicU32::new(FONT_MEDIUM_CAP_PX.to_bits()),\n            root_font_metrics_ch: AtomicU32::new(FONT_MEDIUM_CH_PX.to_bits()),\n            root_font_metrics_ic: AtomicU32::new(FONT_MEDIUM_IC_PX.to_bits()),\n            used_root_font_size: AtomicBool::new(false),\n            used_root_line_height: AtomicBool::new(false),\n            used_root_font_metrics: RwLock::new(false),\n            used_font_metrics: AtomicBool::new(false),\n            used_viewport_size: AtomicBool::new(false),\n            used_dynamic_viewport_size: AtomicBool::new(false),\n            environment: CssEnvironment,\n            default_values,\n            body_text_color: AtomicU32::new(AbsoluteColor::BLACK.to_nscolor()),\n            extra: ExtraDeviceData {\n                media_type,\n                viewport_size,\n                device_pixel_ratio,\n                quirks_mode,\n                prefers_color_scheme,\n                font_metrics_provider,\n            },\n        }\n    }\n\n    /// Returns the computed line-height for the font in a given computed values instance.\n    ///\n    /// If you pass down an element, then the used line-height is returned.\n    pub fn calc_line_height(\n        &self,\n        font: &crate::properties::style_structs::Font,\n        _writing_mode: WritingMode,\n        _element: Option<()>,\n    ) -> NonNegativeLength {\n        (match font.line_height {\n            // TODO: compute `normal` from the font metrics\n            LineHeight::Normal => CSSPixelLength::new(0.),\n            LineHeight::Number(number) => font.font_size.computed_size() * number.0,\n            LineHeight::Length(length) => length.0,\n        })\n        .into()\n    }\n\n    /// Get the quirks mode of the current device.\n    pub fn quirks_mode(&self) -> QuirksMode {\n        self.extra.quirks_mode\n    }\n\n    /// Gets the base size given a generic font family.\n    pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length {\n        self.extra\n            .font_metrics_provider\n            .base_size_for_generic(generic)\n    }\n\n    /// Whether a given animation name may be referenced from style.\n    pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool {\n        // Assume it is, since we don't have any good way to prove it's not.\n        true\n    }\n\n    /// Get the viewport size on this [`Device`].\n    pub fn viewport_size(&self) -> Size2D<f32, CSSPixel> {\n        self.extra.viewport_size\n    }\n\n    /// Set the viewport size on this [`Device`].\n    ///\n    /// Note that this does not update any associated `Stylist`. For this you must call\n    /// `Stylist::media_features_change_changed_style` and\n    /// `Stylist::force_stylesheet_origins_dirty`.\n    pub fn set_viewport_size(&mut self, viewport_size: Size2D<f32, CSSPixel>) {\n        self.extra.viewport_size = viewport_size;\n    }\n\n    /// Returns the viewport size of the current device in app units, needed,\n    /// among other things, to resolve viewport units.\n    #[inline]\n    pub fn au_viewport_size(&self) -> UntypedSize2D<Au> {\n        Size2D::new(\n            Au::from_f32_px(self.extra.viewport_size.width),\n            Au::from_f32_px(self.extra.viewport_size.height),\n        )\n    }\n\n    /// Like the above, but records that we've used viewport units.\n    pub fn au_viewport_size_for_viewport_unit_resolution(\n        &self,\n        _: ViewportVariant,\n    ) -> UntypedSize2D<Au> {\n        self.used_viewport_size.store(true, Ordering::Relaxed);\n        // Servo doesn't have dynamic UA interfaces that affect the viewport,\n        // so we can just ignore the ViewportVariant.\n        self.au_viewport_size()\n    }\n\n    /// Returns the number of app units per device pixel we're using currently.\n    pub fn app_units_per_device_pixel(&self) -> i32 {\n        (AU_PER_PX as f32 / self.extra.device_pixel_ratio.0) as i32\n    }\n\n    /// Returns the device pixel ratio, ignoring the full zoom factor.\n    pub fn device_pixel_ratio_ignoring_full_zoom(&self) -> Scale<f32, CSSPixel, DevicePixel> {\n        self.extra.device_pixel_ratio\n    }\n\n    /// Returns the device pixel ratio.\n    pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {\n        self.extra.device_pixel_ratio\n    }\n\n    /// Set a new device pixel ratio on this [`Device`].\n    ///\n    /// Note that this does not update any associated `Stylist`. For this you must call\n    /// `Stylist::media_features_change_changed_style` and\n    /// `Stylist::force_stylesheet_origins_dirty`.\n    pub fn set_device_pixel_ratio(\n        &mut self,\n        device_pixel_ratio: Scale<f32, CSSPixel, DevicePixel>,\n    ) {\n        self.extra.device_pixel_ratio = device_pixel_ratio;\n    }\n\n    /// Gets the size of the scrollbar in CSS pixels.\n    pub fn scrollbar_inline_size(&self) -> CSSPixelLength {\n        // TODO: implement this.\n        CSSPixelLength::new(0.0)\n    }\n\n    /// Queries font metrics using the [`FontMetricsProvider`] interface.\n    pub fn query_font_metrics(\n        &self,\n        vertical: bool,\n        font: &Font,\n        base_size: CSSPixelLength,\n        flags: QueryFontMetricsFlags,\n        track_usage: bool,\n    ) -> FontMetrics {\n        if track_usage {\n            self.used_font_metrics.store(true, Ordering::Relaxed);\n        }\n        self.extra\n            .font_metrics_provider\n            .query_font_metrics(vertical, font, base_size, flags)\n    }\n\n    /// Return the media type of the current device.\n    pub fn media_type(&self) -> MediaType {\n        self.extra.media_type.clone()\n    }\n\n    /// Returns whether document colors are enabled.\n    pub fn forced_colors(&self) -> ForcedColors {\n        ForcedColors::None\n    }\n\n    /// Returns the default background color.\n    pub fn default_background_color(&self) -> AbsoluteColor {\n        AbsoluteColor::WHITE\n    }\n\n    /// Returns the default foreground color.\n    pub fn default_color(&self) -> AbsoluteColor {\n        AbsoluteColor::BLACK\n    }\n\n    /// Set the [`PrefersColorScheme`] value on this [`Device`].\n    ///\n    /// Note that this does not update any associated `Stylist`. For this you must call\n    /// `Stylist::media_features_change_changed_style` and\n    /// `Stylist::force_stylesheet_origins_dirty`.\n    pub fn set_color_scheme(&mut self, new_color_scheme: PrefersColorScheme) {\n        self.extra.prefers_color_scheme = new_color_scheme;\n    }\n\n    /// Returns the color scheme of this [`Device`].\n    pub fn color_scheme(&self) -> PrefersColorScheme {\n        self.extra.prefers_color_scheme\n    }\n\n    pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool {\n        false\n    }\n\n    pub(crate) fn system_color(\n        &self,\n        system_color: SystemColor,\n        color_scheme_flags: ColorSchemeFlags,\n    ) -> AbsoluteColor {\n        fn srgb(r: u8, g: u8, b: u8) -> AbsoluteColor {\n            AbsoluteColor::srgb_legacy(r, g, b, 1f32)\n        }\n\n        // Refer to spec\n        // <https://www.w3.org/TR/css-color-4/#css-system-colors>\n        if self.is_dark_color_scheme(color_scheme_flags) {\n            // Note: is_dark_color_scheme always returns true, so this code is dead code.\n            match system_color {\n                SystemColor::Accentcolor => srgb(10, 132, 255),\n                SystemColor::Accentcolortext => srgb(255, 255, 255),\n                SystemColor::Activetext => srgb(255, 0, 0),\n                SystemColor::Linktext => srgb(158, 158, 255),\n                SystemColor::Visitedtext => srgb(208, 173, 240),\n                SystemColor::Buttonborder\n                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.\n                | SystemColor::Activeborder\n                | SystemColor::Inactiveborder\n                | SystemColor::Threeddarkshadow\n                | SystemColor::Threedshadow\n                | SystemColor::Windowframe => srgb(255, 255, 255),\n                SystemColor::Buttonface\n                // Deprecated system colors (CSS Color 4) mapped to Buttonface.\n                | SystemColor::Buttonhighlight\n                | SystemColor::Buttonshadow\n                | SystemColor::Threedface\n                | SystemColor::Threedhighlight\n                | SystemColor::Threedlightshadow => srgb(107, 107, 107),\n                SystemColor::Buttontext => srgb(245, 245, 245),\n                SystemColor::Canvas\n                // Deprecated system colors (CSS Color 4) mapped to Canvas.\n                | SystemColor::Activecaption\n                | SystemColor::Appworkspace\n                | SystemColor::Background\n                | SystemColor::Inactivecaption\n                | SystemColor::Infobackground\n                | SystemColor::Menu\n                | SystemColor::Scrollbar\n                | SystemColor::Window => srgb(30, 30, 30),\n                SystemColor::Canvastext\n                // Deprecated system colors (CSS Color 4) mapped to Canvastext.\n                | SystemColor::Captiontext\n                | SystemColor::Infotext\n                | SystemColor::Menutext\n                | SystemColor::Windowtext => srgb(232, 232, 232),\n                SystemColor::Field => srgb(45, 45, 45),\n                SystemColor::Fieldtext => srgb(240, 240, 240),\n                SystemColor::Graytext\n                // Deprecated system colors (CSS Color 4) mapped to Graytext.\n                | SystemColor::Inactivecaptiontext => srgb(155, 155, 155),\n                SystemColor::Highlight => srgb(38, 79, 120),\n                SystemColor::Highlighttext => srgb(255, 255, 255),\n                SystemColor::Mark => srgb(102, 92, 0),\n                SystemColor::Marktext => srgb(255, 255, 255),\n                SystemColor::Selecteditem => srgb(153, 200, 255),\n                SystemColor::Selecteditemtext => srgb(59, 59, 59),\n            }\n        } else {\n            match system_color {\n                SystemColor::Accentcolor => srgb(0, 102, 204),\n                SystemColor::Accentcolortext => srgb(255, 255, 255),\n                SystemColor::Activetext => srgb(238, 0, 0),\n                SystemColor::Linktext => srgb(0, 0, 238),\n                SystemColor::Visitedtext => srgb(85, 26, 139),\n                SystemColor::Buttonborder\n                // Deprecated system colors (CSS Color 4) mapped to Buttonborder.\n                | SystemColor::Activeborder\n                | SystemColor::Inactiveborder\n                | SystemColor::Threeddarkshadow\n                | SystemColor::Threedshadow\n                | SystemColor::Windowframe => srgb(169, 169, 169),\n                SystemColor::Buttonface\n                // Deprecated system colors (CSS Color 4) mapped to Buttonface.\n                | SystemColor::Buttonhighlight\n                | SystemColor::Buttonshadow\n                | SystemColor::Threedface\n                | SystemColor::Threedhighlight\n                | SystemColor::Threedlightshadow => srgb(220, 220, 220),\n                SystemColor::Buttontext => srgb(0, 0, 0),\n                SystemColor::Canvas\n                // Deprecated system colors (CSS Color 4) mapped to Canvas.\n                | SystemColor::Activecaption\n                | SystemColor::Appworkspace\n                | SystemColor::Background\n                | SystemColor::Inactivecaption\n                | SystemColor::Infobackground\n                | SystemColor::Menu\n                | SystemColor::Scrollbar\n                | SystemColor::Window => srgb(255, 255, 255),\n                SystemColor::Canvastext\n                // Deprecated system colors (CSS Color 4) mapped to Canvastext.\n                | SystemColor::Captiontext\n                | SystemColor::Infotext\n                | SystemColor::Menutext\n                | SystemColor::Windowtext => srgb(0, 0, 0),\n                SystemColor::Field => srgb(255, 255, 255),\n                SystemColor::Fieldtext => srgb(0, 0, 0),\n                SystemColor::Graytext\n                // Deprecated system colors (CSS Color 4) mapped to Graytext.\n                | SystemColor::Inactivecaptiontext => srgb(109, 109, 109),\n                SystemColor::Highlight => srgb(0, 65, 198),\n                SystemColor::Highlighttext => srgb(0, 0, 0),\n                SystemColor::Mark => srgb(255, 235, 59),\n                SystemColor::Marktext => srgb(0, 0, 0),\n                SystemColor::Selecteditem => srgb(0, 102, 204),\n                SystemColor::Selecteditemtext => srgb(255, 255, 255),\n            }\n        }\n    }\n\n    /// Returns safe area insets\n    pub fn safe_area_insets(&self) -> SideOffsets2D<f32, CSSPixel> {\n        SideOffsets2D::zero()\n    }\n\n    /// Returns true if the given MIME type is supported\n    pub fn is_supported_mime_type(&self, mime_type: &str) -> bool {\n        match mime_type.parse::<Mime>() {\n            Ok(m) => {\n                // Keep this in sync with 'image_classifer' from\n                // components/net/mime_classifier.rs\n                m == mime::IMAGE_BMP\n                    || m == mime::IMAGE_GIF\n                    || m == mime::IMAGE_PNG\n                    || m == mime::IMAGE_JPEG\n                    || m == \"image/x-icon\"\n                    || m == \"image/webp\"\n            },\n            _ => false,\n        }\n    }\n\n    /// Return whether the document is a chrome document.\n    #[inline]\n    pub fn chrome_rules_enabled_for_document(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "style/dom.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Types and traits used to access the DOM from style calculation.\n\n#![allow(unsafe_code)]\n#![deny(missing_docs)]\n\nuse crate::applicable_declarations::ApplicableDeclarationBlock;\nuse crate::context::SharedStyleContext;\n#[cfg(feature = \"gecko\")]\nuse crate::context::UpdateAnimationsTasks;\nuse crate::data::{ElementData, ElementDataMut, ElementDataRef};\nuse crate::device::Device;\nuse crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock};\nuse crate::selector_map::PrecomputedHashMap;\nuse crate::selector_parser::{AttrValue, Lang, PseudoElement, RestyleDamage, SelectorImpl};\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::stylesheets::scope_rule::ImplicitScopeRoot;\nuse crate::stylist::CascadeData;\nuse crate::values::computed::Display;\nuse crate::values::AtomIdent;\nuse crate::{LocalName, Namespace, WeakAtom};\nuse dom::ElementState;\nuse selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode};\nuse selectors::sink::Push;\nuse selectors::{Element as SelectorsElement, OpaqueElement};\nuse servo_arc::{Arc, ArcBorrow};\nuse smallvec::SmallVec;\nuse std::fmt;\nuse std::fmt::Debug;\nuse std::hash::Hash;\nuse std::ops::Deref;\n\npub use style_traits::dom::OpaqueNode;\n\n/// Simple trait to provide basic information about the type of an element.\n///\n/// We avoid exposing the full type id, since computing it in the general case\n/// would be difficult for Gecko nodes.\npub trait NodeInfo {\n    /// Whether this node is an element.\n    fn is_element(&self) -> bool;\n    /// Whether this node is a text node.\n    fn is_text_node(&self) -> bool;\n}\n\n/// A node iterator that only returns node that don't need layout.\npub struct LayoutIterator<T>(pub T);\n\nimpl<T, N> Iterator for LayoutIterator<T>\nwhere\n    T: Iterator<Item = N>,\n    N: NodeInfo,\n{\n    type Item = N;\n\n    fn next(&mut self) -> Option<N> {\n        loop {\n            let n = self.0.next()?;\n            // Filter out nodes that layout should ignore.\n            if n.is_text_node() || n.is_element() {\n                return Some(n);\n            }\n        }\n    }\n}\n\n/// An iterator over the DOM children of a node.\npub struct DomChildren<N>(Option<N>);\nimpl<N> Iterator for DomChildren<N>\nwhere\n    N: TNode,\n{\n    type Item = N;\n\n    fn next(&mut self) -> Option<N> {\n        let n = self.0.take()?;\n        self.0 = n.next_sibling();\n        Some(n)\n    }\n}\n\n/// An iterator over the DOM descendants of a node in pre-order.\npub struct DomDescendants<N> {\n    previous: Option<N>,\n    scope: N,\n}\n\nimpl<N> DomDescendants<N>\nwhere\n    N: TNode,\n{\n    /// Returns the next element ignoring all of our subtree.\n    #[inline]\n    pub fn next_skipping_children(&mut self) -> Option<N> {\n        let prev = self.previous.take()?;\n        self.previous = prev.next_in_preorder_skipping_children(self.scope);\n        self.previous\n    }\n}\n\nimpl<N> Iterator for DomDescendants<N>\nwhere\n    N: TNode,\n{\n    type Item = N;\n\n    #[inline]\n    fn next(&mut self) -> Option<N> {\n        let prev = self.previous.take()?;\n        self.previous = prev.next_in_preorder(self.scope);\n        self.previous\n    }\n}\n\n/// The `TDocument` trait, to represent a document node.\npub trait TDocument: Sized + Copy + Clone {\n    /// The concrete `TNode` type.\n    type ConcreteNode: TNode<ConcreteDocument = Self>;\n\n    /// Get this document as a `TNode`.\n    fn as_node(&self) -> Self::ConcreteNode;\n\n    /// Returns whether this document is an HTML document.\n    fn is_html_document(&self) -> bool;\n\n    /// Returns the quirks mode of this document.\n    fn quirks_mode(&self) -> QuirksMode;\n\n    /// Get a list of elements with a given ID in this document, sorted by\n    /// tree position.\n    ///\n    /// Can return an error to signal that this list is not available, or also\n    /// return an empty slice.\n    fn elements_with_id<'a>(\n        &self,\n        _id: &AtomIdent,\n    ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>\n    where\n        Self: 'a,\n    {\n        Err(())\n    }\n\n    /// This document's shared lock.\n    fn shared_lock(&self) -> &SharedRwLock;\n}\n\n/// The `TNode` trait. This is the main generic trait over which the style\n/// system can be implemented.\npub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {\n    /// The concrete `TElement` type.\n    type ConcreteElement: TElement<ConcreteNode = Self>;\n\n    /// The concrete `TDocument` type.\n    type ConcreteDocument: TDocument<ConcreteNode = Self>;\n\n    /// The concrete `TShadowRoot` type.\n    type ConcreteShadowRoot: TShadowRoot<ConcreteNode = Self>;\n\n    /// Get this node's parent node.\n    fn parent_node(&self) -> Option<Self>;\n\n    /// Get this node's first child.\n    fn first_child(&self) -> Option<Self>;\n\n    /// Get this node's last child.\n    fn last_child(&self) -> Option<Self>;\n\n    /// Get this node's previous sibling.\n    fn prev_sibling(&self) -> Option<Self>;\n\n    /// Get this node's next sibling.\n    fn next_sibling(&self) -> Option<Self>;\n\n    /// Get the owner document of this node.\n    fn owner_doc(&self) -> Self::ConcreteDocument;\n\n    /// Iterate over the DOM children of a node.\n    #[inline(always)]\n    fn dom_children(&self) -> DomChildren<Self> {\n        DomChildren(self.first_child())\n    }\n\n    /// Returns whether the node is attached to a document.\n    fn is_in_document(&self) -> bool;\n\n    /// Iterate over the DOM children of a node, in preorder.\n    #[inline(always)]\n    fn dom_descendants(&self) -> DomDescendants<Self> {\n        DomDescendants {\n            previous: Some(*self),\n            scope: *self,\n        }\n    }\n\n    /// Returns the next node after this one, in a pre-order tree-traversal of\n    /// the subtree rooted at scoped_to.\n    #[inline]\n    fn next_in_preorder(&self, scoped_to: Self) -> Option<Self> {\n        if let Some(c) = self.first_child() {\n            return Some(c);\n        }\n        self.next_in_preorder_skipping_children(scoped_to)\n    }\n\n    /// Returns the next node in tree order, skipping the children of the current node.\n    ///\n    /// This is useful when we know that a subtree cannot contain matches, allowing us\n    /// to skip entire subtrees during traversal.\n    #[inline]\n    fn next_in_preorder_skipping_children(&self, scoped_to: Self) -> Option<Self> {\n        let mut current = *self;\n        loop {\n            if current == scoped_to {\n                return None;\n            }\n\n            if let Some(s) = current.next_sibling() {\n                return Some(s);\n            }\n\n            debug_assert!(\n                current.parent_node().is_some(),\n                \"Not a descendant of the scope?\"\n            );\n            current = current.parent_node()?;\n        }\n    }\n\n    /// Returns the depth of this node in the DOM.\n    fn depth(&self) -> usize {\n        let mut depth = 0;\n        let mut curr = *self;\n        while let Some(parent) = curr.traversal_parent() {\n            depth += 1;\n            curr = parent.as_node();\n        }\n        depth\n    }\n\n    /// Get this node's parent element from the perspective of a restyle\n    /// traversal.\n    fn traversal_parent(&self) -> Option<Self::ConcreteElement>;\n\n    /// Get this node's parent element if present.\n    fn parent_element(&self) -> Option<Self::ConcreteElement> {\n        self.parent_node().and_then(|n| n.as_element())\n    }\n\n    /// Get this node's parent element, or shadow host if it's a shadow root.\n    fn parent_element_or_host(&self) -> Option<Self::ConcreteElement> {\n        let parent = self.parent_node()?;\n        if let Some(e) = parent.as_element() {\n            return Some(e);\n        }\n        if let Some(root) = parent.as_shadow_root() {\n            return Some(root.host());\n        }\n        None\n    }\n\n    /// Converts self into an `OpaqueNode`.\n    fn opaque(&self) -> OpaqueNode;\n\n    /// A debug id, only useful, mm... for debugging.\n    fn debug_id(self) -> usize;\n\n    /// Get this node as an element, if it's one.\n    fn as_element(&self) -> Option<Self::ConcreteElement>;\n\n    /// Get this node as a document, if it's one.\n    fn as_document(&self) -> Option<Self::ConcreteDocument>;\n\n    /// Get this node as a ShadowRoot, if it's one.\n    fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot>;\n}\n\n/// Wrapper to output the subtree rather than the single node when formatting\n/// for Debug.\npub struct ShowSubtree<N: TNode>(pub N);\nimpl<N: TNode> Debug for ShowSubtree<N> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"DOM Subtree:\")?;\n        fmt_subtree(f, &|f, n| write!(f, \"{:?}\", n), self.0, 1)\n    }\n}\n\n/// Wrapper to output the subtree along with the ElementData when formatting\n/// for Debug.\npub struct ShowSubtreeData<N: TNode>(pub N);\nimpl<N: TNode> Debug for ShowSubtreeData<N> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"DOM Subtree:\")?;\n        fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1)\n    }\n}\n\n/// Wrapper to output the subtree along with the ElementData and primary\n/// ComputedValues when formatting for Debug. This is extremely verbose.\n#[cfg(feature = \"servo\")]\npub struct ShowSubtreeDataAndPrimaryValues<N: TNode>(pub N);\n#[cfg(feature = \"servo\")]\nimpl<N: TNode> Debug for ShowSubtreeDataAndPrimaryValues<N> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        writeln!(f, \"DOM Subtree:\")?;\n        fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1)\n    }\n}\n\nfn fmt_with_data<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {\n    if let Some(el) = n.as_element() {\n        write!(\n            f,\n            \"{:?} dd={} aodd={} data={:?}\",\n            el,\n            el.has_dirty_descendants(),\n            el.has_animation_only_dirty_descendants(),\n            el.borrow_data(),\n        )\n    } else {\n        write!(f, \"{:?}\", n)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nfn fmt_with_data_and_primary_values<N: TNode>(f: &mut fmt::Formatter, n: N) -> fmt::Result {\n    if let Some(el) = n.as_element() {\n        let dd = el.has_dirty_descendants();\n        let aodd = el.has_animation_only_dirty_descendants();\n        let data = el.borrow_data();\n        let values = data.as_ref().and_then(|d| d.styles.get_primary());\n        write!(\n            f,\n            \"{:?} dd={} aodd={} data={:?} values={:?}\",\n            el, dd, aodd, &data, values\n        )\n    } else {\n        write!(f, \"{:?}\", n)\n    }\n}\n\nfn fmt_subtree<F, N: TNode>(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32) -> fmt::Result\nwhere\n    F: Fn(&mut fmt::Formatter, N) -> fmt::Result,\n{\n    for _ in 0..indent {\n        write!(f, \"  \")?;\n    }\n    stringify(f, n)?;\n    if let Some(e) = n.as_element() {\n        for kid in e.traversal_children() {\n            writeln!(f, \"\")?;\n            fmt_subtree(f, stringify, kid, indent + 1)?;\n        }\n    }\n\n    Ok(())\n}\n\n/// The ShadowRoot trait.\npub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {\n    /// The concrete node type.\n    type ConcreteNode: TNode<ConcreteShadowRoot = Self>;\n\n    /// Get this ShadowRoot as a node.\n    fn as_node(&self) -> Self::ConcreteNode;\n\n    /// Get the shadow host that hosts this ShadowRoot.\n    fn host(&self) -> <Self::ConcreteNode as TNode>::ConcreteElement;\n\n    /// Get the style data for this ShadowRoot.\n    fn style_data<'a>(&self) -> Option<&'a CascadeData>\n    where\n        Self: 'a;\n\n    /// Get the list of shadow parts for this shadow root.\n    fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]\n    where\n        Self: 'a,\n    {\n        &[]\n    }\n\n    /// Get a list of elements with a given ID in this shadow root, sorted by\n    /// tree position.\n    ///\n    /// Can return an error to signal that this list is not available, or also\n    /// return an empty slice.\n    fn elements_with_id<'a>(\n        &self,\n        _id: &AtomIdent,\n    ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>\n    where\n        Self: 'a,\n    {\n        Err(())\n    }\n\n    /// Get the implicit scope for a stylesheet in given index.\n    fn implicit_scope_for_sheet(&self, _sheet_index: usize) -> Option<ImplicitScopeRoot> {\n        None\n    }\n}\n\n/// The element trait, the main abstraction the style crate acts over.\npub trait TElement:\n    Eq\n    + PartialEq\n    + Debug\n    + Hash\n    + Sized\n    + Copy\n    + Clone\n    + SelectorsElement<Impl = SelectorImpl>\n    + AttributeProvider\n{\n    /// The concrete node type.\n    type ConcreteNode: TNode<ConcreteElement = Self>;\n\n    /// A concrete children iterator type in order to iterate over the `Node`s.\n    ///\n    /// TODO(emilio): We should eventually replace this with the `impl Trait`\n    /// syntax.\n    type TraversalChildrenIterator: Iterator<Item = Self::ConcreteNode>;\n\n    /// Get this element as a node.\n    fn as_node(&self) -> Self::ConcreteNode;\n\n    /// A debug-only check that the device's owner doc matches the actual doc\n    /// we're the root of.\n    ///\n    /// Otherwise we may set document-level state incorrectly, like the root\n    /// font-size used for rem units.\n    fn owner_doc_matches_for_testing(&self, _: &Device) -> bool {\n        true\n    }\n\n    /// Whether this element should match user and content rules.\n    ///\n    /// We use this for Native Anonymous Content in Gecko.\n    fn matches_user_and_content_rules(&self) -> bool {\n        true\n    }\n\n    /// Get this node's parent element from the perspective of a restyle\n    /// traversal.\n    fn traversal_parent(&self) -> Option<Self> {\n        self.as_node().traversal_parent()\n    }\n\n    /// Get this node's children from the perspective of a restyle traversal.\n    fn traversal_children(&self) -> LayoutIterator<Self::TraversalChildrenIterator>;\n\n    /// Returns the parent element we should inherit from.\n    ///\n    /// This is pretty much always the parent element itself, except in the case\n    /// of Gecko's Native Anonymous Content, which uses the traversal parent\n    /// (i.e. the flattened tree parent) and which also may need to find the\n    /// closest non-NAC ancestor.\n    fn inheritance_parent(&self) -> Option<Self> {\n        self.parent_element()\n    }\n\n    /// Execute `f` for each anonymous content child (apart from ::before and\n    /// ::after) whose originating element is `self`.\n    fn each_anonymous_content_child<F>(&self, _f: F)\n    where\n        F: FnMut(Self),\n    {\n    }\n\n    /// Return whether this element is an element in the HTML namespace.\n    fn is_html_element(&self) -> bool;\n\n    /// Return whether this element is an element in the MathML namespace.\n    fn is_mathml_element(&self) -> bool;\n\n    /// Return whether this element is an element in the SVG namespace.\n    fn is_svg_element(&self) -> bool;\n\n    /// Return whether this element is an element in the XUL namespace.\n    fn is_xul_element(&self) -> bool {\n        false\n    }\n\n    /// Returns the bloom filter for this element's subtree, used for fast\n    /// querySelector optimization by allowing subtrees to be skipped.\n    /// Each element's filter includes hashes for all of it's class names and\n    /// attribute names (not values), along with the names for all descendent\n    /// elements.\n    ///\n    /// The default implementation returns all bits set, meaning the bloom filter\n    /// never filters anything.\n    fn subtree_bloom_filter(&self) -> u64 {\n        u64::MAX\n    }\n\n    /// Check if this element's subtree may contain elements with the given bloom hash.\n    fn bloom_may_have_hash(&self, bloom_hash: u64) -> bool {\n        let bloom = self.subtree_bloom_filter();\n        (bloom & bloom_hash) == bloom_hash\n    }\n\n    /// Convert a 32-bit atom hash to a bloom filter value using k=2 hash functions.\n    /// This must match the C++ implementation of HashForBloomFilter in Element.cpp\n    fn hash_for_bloom_filter(hash: u32) -> u64 {\n        // On 32-bit platforms, we have 31 bits available + 1 tag bit.\n        // On 64-bit platforms, we have 63 bits available + 1 tag bit.\n        #[cfg(target_pointer_width = \"32\")]\n        const BLOOM_BITS: u32 = 31;\n\n        #[cfg(target_pointer_width = \"64\")]\n        const BLOOM_BITS: u32 = 63;\n\n        let mut filter = 1u64;\n        filter |= 1u64 << (1 + (hash % BLOOM_BITS));\n        filter |= 1u64 << (1 + ((hash >> 6) % BLOOM_BITS));\n        filter\n    }\n\n    /// Return the list of slotted nodes of this node.\n    fn slotted_nodes(&self) -> &[Self::ConcreteNode] {\n        &[]\n    }\n\n    /// Get this element's style attribute.\n    fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>>;\n\n    /// Unset the style attribute's dirty bit.\n    /// Servo doesn't need to manage ditry bit for style attribute.\n    fn unset_dirty_style_attribute(&self) {}\n\n    /// Get this element's SMIL override declarations.\n    fn smil_override(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {\n        None\n    }\n\n    /// Get the combined animation and transition rules.\n    ///\n    /// FIXME(emilio): Is this really useful?\n    fn animation_declarations(&self, context: &SharedStyleContext) -> AnimationDeclarations {\n        if !self.may_have_animations() {\n            return Default::default();\n        }\n\n        AnimationDeclarations {\n            animations: self.animation_rule(context),\n            transitions: self.transition_rule(context),\n        }\n    }\n\n    /// Get this element's animation rule.\n    fn animation_rule(\n        &self,\n        _: &SharedStyleContext,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;\n\n    /// Get this element's transition rule.\n    fn transition_rule(\n        &self,\n        context: &SharedStyleContext,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>>;\n\n    /// Get this element's state, for non-tree-structural pseudos.\n    fn state(&self) -> ElementState;\n\n    /// Returns whether this element has a `part` attribute.\n    fn has_part_attr(&self) -> bool;\n\n    /// Returns whether this element exports any part from its shadow tree.\n    fn exports_any_part(&self) -> bool;\n\n    /// The ID for this element.\n    fn id(&self) -> Option<&WeakAtom>;\n\n    /// Internal iterator for the classes of this element.\n    fn each_class<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent);\n\n    /// Internal iterator for the classes of this element.\n    fn each_custom_state<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent);\n\n    /// Internal iterator for the part names of this element.\n    fn each_part<F>(&self, _callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n    }\n\n    /// Internal iterator for the attribute names of this element.\n    fn each_attr_name<F>(&self, callback: F)\n    where\n        F: FnMut(&LocalName);\n\n    /// Internal iterator for the part names that this element exports for a\n    /// given part name.\n    fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n    }\n\n    /// Whether a given element may generate a pseudo-element.\n    ///\n    /// This is useful to avoid computing, for example, pseudo styles for\n    /// `::-first-line` or `::-first-letter`, when we know it won't affect us.\n    ///\n    /// TODO(emilio, bz): actually implement the logic for it.\n    fn may_generate_pseudo(&self, pseudo: &PseudoElement, _primary_style: &ComputedValues) -> bool {\n        // ::before/::after are always supported for now, though we could try to\n        // optimize out leaf elements.\n\n        // ::first-letter and ::first-line are only supported for block-inside\n        // things, and only in Gecko, not Servo.  Unfortunately, Gecko has\n        // block-inside things that might have any computed display value due to\n        // things like fieldsets, legends, etc.  Need to figure out how this\n        // should work.\n        debug_assert!(\n            pseudo.is_eager(),\n            \"Someone called may_generate_pseudo with a non-eager pseudo.\"\n        );\n        true\n    }\n\n    /// Returns true if this element may have a descendant needing style processing.\n    ///\n    /// Note that we cannot guarantee the existence of such an element, because\n    /// it may have been removed from the DOM between marking it for restyle and\n    /// the actual restyle traversal.\n    fn has_dirty_descendants(&self) -> bool;\n\n    /// Returns whether state or attributes that may change style have changed\n    /// on the element, and thus whether the element has been snapshotted to do\n    /// restyle hint computation.\n    fn has_snapshot(&self) -> bool;\n\n    /// Returns whether the current snapshot if present has been handled.\n    fn handled_snapshot(&self) -> bool;\n\n    /// Flags this element as having handled already its snapshot.\n    unsafe fn set_handled_snapshot(&self);\n\n    /// Returns whether the element's styles are up-to-date after traversal\n    /// (i.e. in post traversal).\n    fn has_current_styles(&self, data: &ElementData) -> bool {\n        if self.has_snapshot() && !self.handled_snapshot() {\n            return false;\n        }\n\n        data.has_styles() &&\n        // TODO(hiro): When an animating element moved into subtree of\n        // contenteditable element, there remains animation restyle hints in\n        // post traversal. It's generally harmless since the hints will be\n        // processed in a next styling but ideally it should be processed soon.\n        //\n        // Without this, we get failures in:\n        //   layout/style/crashtests/1383319.html\n        //   layout/style/crashtests/1383001.html\n        //\n        // https://bugzilla.mozilla.org/show_bug.cgi?id=1389675 tracks fixing\n        // this.\n        !data.hint.has_non_animation_invalidations()\n    }\n\n    /// Flag that this element has a descendant for style processing.\n    ///\n    /// Only safe to call with exclusive access to the element.\n    unsafe fn set_dirty_descendants(&self);\n\n    /// Flag that this element has no descendant for style processing.\n    ///\n    /// Only safe to call with exclusive access to the element.\n    unsafe fn unset_dirty_descendants(&self);\n\n    /// Similar to the dirty_descendants but for representing a descendant of\n    /// the element needs to be updated in animation-only traversal.\n    fn has_animation_only_dirty_descendants(&self) -> bool {\n        false\n    }\n\n    /// Flag that this element has a descendant for animation-only restyle\n    /// processing.\n    ///\n    /// Only safe to call with exclusive access to the element.\n    unsafe fn set_animation_only_dirty_descendants(&self) {}\n\n    /// Flag that this element has no descendant for animation-only restyle processing.\n    ///\n    /// Only safe to call with exclusive access to the element.\n    unsafe fn unset_animation_only_dirty_descendants(&self) {}\n\n    /// Clear all bits related describing the dirtiness of descendants.\n    ///\n    /// In Gecko, this corresponds to the regular dirty descendants bit, the\n    /// animation-only dirty descendants bit, and the lazy frame construction\n    /// descendants bit.\n    unsafe fn clear_descendant_bits(&self) {\n        self.unset_dirty_descendants();\n    }\n\n    /// Returns true if this element is a visited link.\n    ///\n    /// Servo doesn't support visited styles yet.\n    fn is_visited_link(&self) -> bool {\n        false\n    }\n\n    /// Returns the pseudo-element implemented by this element, if any.\n    ///\n    /// Gecko traverses pseudo-elements during the style traversal, and we need\n    /// to know this so we can properly grab the pseudo-element style from the\n    /// parent element.\n    ///\n    /// Note that we still need to compute the pseudo-elements before-hand,\n    /// given otherwise we don't know if we need to create an element or not.\n    fn implemented_pseudo_element(&self) -> Option<PseudoElement> {\n        None\n    }\n\n    /// Atomically stores the number of children of this node that we will\n    /// need to process during bottom-up traversal.\n    fn store_children_to_process(&self, n: isize);\n\n    /// Atomically notes that a child has been processed during bottom-up\n    /// traversal. Returns the number of children left to process.\n    fn did_process_child(&self) -> isize;\n\n    /// Gets a reference to the ElementData container, or creates one.\n    ///\n    /// Unsafe because it can race to allocate and leak if not used with\n    /// exclusive access to the element.\n    unsafe fn ensure_data(&self) -> ElementDataMut<'_>;\n\n    /// Clears the element data reference, if any.\n    ///\n    /// Unsafe following the same reasoning as ensure_data.\n    unsafe fn clear_data(&self);\n\n    /// Whether there is an ElementData container.\n    fn has_data(&self) -> bool;\n\n    /// Immutably borrows the ElementData.\n    fn borrow_data(&self) -> Option<ElementDataRef<'_>>;\n\n    /// Mutably borrows the ElementData.\n    fn mutate_data(&self) -> Option<ElementDataMut<'_>>;\n\n    /// Whether we should skip any root- or item-based display property\n    /// blockification on this element.  (This function exists so that Gecko\n    /// native anonymous content can opt out of this style fixup.)\n    fn skip_item_display_fixup(&self) -> bool;\n\n    /// In Gecko, element has a flag that represents the element may have\n    /// any type of animations or not to bail out animation stuff early.\n    /// Whereas Servo doesn't have such flag.\n    fn may_have_animations(&self) -> bool;\n\n    /// Creates a task to update various animation state on a given (pseudo-)element.\n    #[cfg(feature = \"gecko\")]\n    fn update_animations(\n        &self,\n        before_change_style: Option<Arc<ComputedValues>>,\n        tasks: UpdateAnimationsTasks,\n    );\n\n    /// Returns true if the element has relevant animations. Relevant\n    /// animations are those animations that are affecting the element's style\n    /// or are scheduled to do so in the future.\n    fn has_animations(&self, context: &SharedStyleContext) -> bool;\n\n    /// Returns true if the element has a CSS animation. The `context` and `pseudo_element`\n    /// arguments are only used by Servo, since it stores animations globally and pseudo-elements\n    /// are not in the DOM.\n    fn has_css_animations(\n        &self,\n        context: &SharedStyleContext,\n        pseudo_element: Option<PseudoElement>,\n    ) -> bool;\n\n    /// Returns true if the element has a CSS transition (including running transitions and\n    /// completed transitions). The `context` and `pseudo_element` arguments are only used\n    /// by Servo, since it stores animations globally and pseudo-elements are not in the DOM.\n    fn has_css_transitions(\n        &self,\n        context: &SharedStyleContext,\n        pseudo_element: Option<PseudoElement>,\n    ) -> bool;\n\n    /// Returns true if the element has animation restyle hints.\n    fn has_animation_restyle_hints(&self) -> bool {\n        let data = match self.borrow_data() {\n            Some(d) => d,\n            None => return false,\n        };\n        return data.hint.has_animation_hint();\n    }\n\n    /// Called when a highlight pseudo-element (::selection, ::highlight,\n    /// ::target-text) style is invalidated. These pseudos need explicit repaint\n    /// triggering since their styles are resolved lazily during painting.\n    fn note_highlight_pseudo_style_invalidated(&self) {}\n\n    /// The shadow root this element is a host of.\n    fn shadow_root(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;\n\n    /// The shadow root which roots the subtree this element is contained in.\n    fn containing_shadow(&self) -> Option<<Self::ConcreteNode as TNode>::ConcreteShadowRoot>;\n\n    /// Return the element which we can use to look up rules in the selector\n    /// maps.\n    ///\n    /// This is always the element itself, except in the case where we are an\n    /// element-backed pseudo-element, in which case we return the originating\n    /// element.\n    fn rule_hash_target(&self) -> Self {\n        let mut cur = *self;\n        while cur.is_pseudo_element() {\n            cur = cur\n                .pseudo_element_originating_element()\n                .expect(\"Trying to collect rules for a detached pseudo-element\")\n        }\n        cur\n    }\n\n    /// Executes the callback for each applicable style rule data which isn't\n    /// the main document's data (which stores UA / author rules).\n    ///\n    /// The element passed to the callback is the containing shadow host for the\n    /// data if it comes from Shadow DOM.\n    ///\n    /// Returns whether normal document author rules should apply.\n    ///\n    /// TODO(emilio): We could separate the invalidation data for elements\n    /// matching in other scopes to avoid over-invalidation.\n    fn each_applicable_non_document_style_rule_data<'a, F>(&self, mut f: F) -> bool\n    where\n        Self: 'a,\n        F: FnMut(&'a CascadeData, Self),\n    {\n        use crate::rule_collector::containing_shadow_ignoring_svg_use;\n\n        let target = self.rule_hash_target();\n        let matches_user_and_content_rules = target.matches_user_and_content_rules();\n        let mut doc_rules_apply = matches_user_and_content_rules;\n\n        // Use the same rules to look for the containing host as we do for rule\n        // collection.\n        if let Some(shadow) = containing_shadow_ignoring_svg_use(target) {\n            doc_rules_apply = false;\n            if let Some(data) = shadow.style_data() {\n                f(data, shadow.host());\n            }\n        }\n\n        if let Some(shadow) = target.shadow_root() {\n            if let Some(data) = shadow.style_data() {\n                f(data, shadow.host());\n            }\n        }\n\n        let mut current = target.assigned_slot();\n        while let Some(slot) = current {\n            // Slots can only have assigned nodes when in a shadow tree.\n            let shadow = slot.containing_shadow().unwrap();\n            if let Some(data) = shadow.style_data() {\n                if data.any_slotted_rule() {\n                    f(data, shadow.host());\n                }\n            }\n            current = slot.assigned_slot();\n        }\n\n        if target.has_part_attr() {\n            if let Some(mut inner_shadow) = target.containing_shadow() {\n                loop {\n                    let inner_shadow_host = inner_shadow.host();\n                    match inner_shadow_host.containing_shadow() {\n                        Some(shadow) => {\n                            if let Some(data) = shadow.style_data() {\n                                if data.any_part_rule() {\n                                    f(data, shadow.host())\n                                }\n                            }\n                            // TODO: Could be more granular.\n                            if !inner_shadow_host.exports_any_part() {\n                                break;\n                            }\n                            inner_shadow = shadow;\n                        },\n                        None => {\n                            // TODO(emilio): Should probably distinguish with\n                            // MatchesDocumentRules::{No,Yes,IfPart} or something so that we could\n                            // skip some work.\n                            doc_rules_apply = matches_user_and_content_rules;\n                            break;\n                        },\n                    }\n                }\n            }\n        }\n\n        doc_rules_apply\n    }\n\n    /// Returns true if one of the transitions needs to be updated on this element. We check all\n    /// the transition properties to make sure that updating transitions is necessary.\n    /// This method should only be called if might_needs_transitions_update returns true when\n    /// passed the same parameters.\n    #[cfg(feature = \"gecko\")]\n    fn needs_transitions_update(\n        &self,\n        before_change_style: &ComputedValues,\n        after_change_style: &ComputedValues,\n    ) -> bool;\n\n    /// Returns the value of the `xml:lang=\"\"` attribute (or, if appropriate,\n    /// the `lang=\"\"` attribute) on this element.\n    fn lang_attr(&self) -> Option<AttrValue>;\n\n    /// Returns whether this element's language matches the language tag\n    /// `value`.  If `override_lang` is not `None`, it specifies the value\n    /// of the `xml:lang=\"\"` or `lang=\"\"` attribute to use in place of\n    /// looking at the element and its ancestors.  (This argument is used\n    /// to implement matching of `:lang()` against snapshots.)\n    fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool;\n\n    /// Returns whether this element is the main body element of the HTML\n    /// document it is on.\n    fn is_html_document_body_element(&self) -> bool;\n\n    /// Generate the proper applicable declarations due to presentational hints,\n    /// and insert them into `hints`.\n    fn synthesize_presentational_hints_for_legacy_attributes<V>(\n        &self,\n        visited_handling: VisitedHandlingMode,\n        hints: &mut V,\n    ) where\n        V: Push<ApplicableDeclarationBlock>;\n\n    /// Generate the proper applicable declarations due to view transition dynamic rules, and\n    /// insert them into `rules`.\n    /// https://drafts.csswg.org/css-view-transitions-1/#document-dynamic-view-transition-style-sheet\n    fn synthesize_view_transition_dynamic_rules<V>(&self, _rules: &mut V)\n    where\n        V: Push<ApplicableDeclarationBlock>,\n    {\n    }\n\n    /// Returns element's local name.\n    fn local_name(&self) -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedLocalName;\n\n    /// Returns element's namespace.\n    fn namespace(&self)\n        -> &<SelectorImpl as selectors::parser::SelectorImpl>::BorrowedNamespaceUrl;\n\n    /// Returns the size of the element to be used in container size queries.\n    /// This will usually be the size of the content area of the primary box,\n    /// but can be None if there is no box or if some axis lacks size containment.\n    fn query_container_size(\n        &self,\n        display: &Display,\n    ) -> euclid::default::Size2D<Option<app_units::Au>>;\n\n    /// Returns true if the element has all of specified selector flags.\n    fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;\n\n    /// Returns the search direction for relative selector invalidation, if it is on the search path.\n    fn relative_selector_search_direction(&self) -> ElementSelectorFlags;\n\n    /// Returns the implicit scope root for given sheet index and host.\n    fn implicit_scope_for_sheet_in_shadow_root(\n        _opaque_host: OpaqueElement,\n        _sheet_index: usize,\n    ) -> Option<ImplicitScopeRoot> {\n        None\n    }\n\n    /// Compute the damage incurred by the change from the `_old` to `_new`.\n    fn compute_layout_damage(_old: &ComputedValues, _new: &ComputedValues) -> RestyleDamage {\n        Default::default()\n    }\n}\n\n/// The attribute provider trait\npub trait AttributeProvider {\n    /// Return the value of the given custom attibute if it exists.\n    fn get_attr(&self, attr: &LocalName, namespace: &Namespace) -> Option<String>;\n}\n\n/// A set of the attributes used to compute a style that uses `attr()`\npub type AttributeReferences = Option<Box<PrecomputedHashMap<LocalName, SmallVec<[Namespace; 1]>>>>;\n\n/// A data structure to keep track of the names queried from a provider.\npub struct AttributeTracker<'a> {\n    /// The element that queries for attributes.\n    pub provider: &'a dyn AttributeProvider,\n    /// The set of attributes we have queried.\n    pub references: AttributeReferences,\n}\n\nimpl<'a> AttributeTracker<'a> {\n    /// Construct a new attribute tracker trivially.\n    pub fn new(provider: &'a dyn AttributeProvider) -> Self {\n        Self {\n            provider,\n            references: None,\n        }\n    }\n\n    /// Consstruct a new dummy attribute tracker\n    pub fn new_dummy() -> Self {\n        Self {\n            provider: &DummyAttributeProvider {},\n            references: None,\n        }\n    }\n\n    /// Extract the queried references and consume self\n    pub fn finalize(self) -> AttributeReferences {\n        self.references\n    }\n\n    /// Query the value and save the name of the attribtue.\n    pub fn query(&mut self, name: &LocalName, namespace: &Namespace) -> Option<String> {\n        // We need to save namespaces in case we are thinking of sharing this element's\n        // style with another.\n        // i.e if elment a has ns1::attr=\"blue\"\n        // and element b has ns2::attr=\"blue\"\n        // a and b can only share style if ns1 and ns2 resolve to the same namespace.\n        self.references\n            .get_or_insert_default()\n            .entry(name.clone())\n            .or_default()\n            .push(namespace.clone());\n        self.provider.get_attr(name, namespace)\n    }\n}\n\n/// A dummy AttributeProvider that returns none to any attribute query.\n#[derive(Clone, Debug, PartialEq)]\nstruct DummyAttributeProvider;\n\nimpl AttributeProvider for DummyAttributeProvider {\n    fn get_attr(&self, _attr: &LocalName, _namespace: &Namespace) -> Option<String> {\n        None\n    }\n}\n\n/// TNode and TElement aren't Send because we want to be careful and explicit\n/// about our parallel traversal. However, there are certain situations\n/// (including but not limited to the traversal) where we need to send DOM\n/// objects to other threads.\n///\n/// That's the reason why `SendNode` exists.\n#[derive(Clone, Debug, PartialEq)]\npub struct SendNode<N: TNode>(N);\nunsafe impl<N: TNode> Send for SendNode<N> {}\nimpl<N: TNode> SendNode<N> {\n    /// Unsafely construct a SendNode.\n    pub unsafe fn new(node: N) -> Self {\n        SendNode(node)\n    }\n}\nimpl<N: TNode> Deref for SendNode<N> {\n    type Target = N;\n    fn deref(&self) -> &N {\n        &self.0\n    }\n}\n\n/// Same reason as for the existence of SendNode, SendElement does the proper\n/// things for a given `TElement`.\n#[derive(Debug, Eq, Hash, PartialEq)]\npub struct SendElement<E: TElement>(E);\nunsafe impl<E: TElement> Send for SendElement<E> {}\nimpl<E: TElement> SendElement<E> {\n    /// Unsafely construct a SendElement.\n    pub unsafe fn new(el: E) -> Self {\n        SendElement(el)\n    }\n}\nimpl<E: TElement> Deref for SendElement<E> {\n    type Target = E;\n    fn deref(&self) -> &E {\n        &self.0\n    }\n}\n"
  },
  {
    "path": "style/dom_apis.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic implementations of some DOM APIs so they can be shared between Servo\n//! and Gecko.\n\nuse crate::context::QuirksMode;\nuse crate::dom::{TDocument, TElement, TNode, TShadowRoot};\nuse crate::invalidation::element::invalidation_map::Dependency;\nuse crate::invalidation::element::invalidator::{\n    DescendantInvalidationLists, Invalidation, SiblingTraversalMap,\n};\nuse crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};\nuse crate::selector_parser::SelectorImpl;\nuse crate::values::AtomIdent;\nuse selectors::attr::CaseSensitivity;\nuse selectors::attr::{AttrSelectorOperation, NamespaceConstraint};\nuse selectors::matching::{\n    self, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags,\n    SelectorCaches,\n};\nuse selectors::parser::{Combinator, Component, LocalName};\nuse selectors::{Element, OpaqueElement, SelectorList};\nuse smallvec::SmallVec;\n\n/// <https://dom.spec.whatwg.org/#dom-element-matches>\npub fn element_matches<E>(\n    element: &E,\n    selector_list: &SelectorList<E::Impl>,\n    quirks_mode: QuirksMode,\n) -> bool\nwhere\n    E: Element,\n{\n    let mut selector_caches = SelectorCaches::default();\n\n    let mut context = MatchingContext::new(\n        MatchingMode::Normal,\n        None,\n        &mut selector_caches,\n        quirks_mode,\n        NeedsSelectorFlags::No,\n        MatchingForInvalidation::No,\n    );\n    context.scope_element = Some(element.opaque());\n    context.current_host = element.containing_shadow_host().map(|e| e.opaque());\n    matching::matches_selector_list(selector_list, element, &mut context)\n}\n\n/// <https://dom.spec.whatwg.org/#dom-element-closest>\npub fn element_closest<E>(\n    element: E,\n    selector_list: &SelectorList<E::Impl>,\n    quirks_mode: QuirksMode,\n) -> Option<E>\nwhere\n    E: Element,\n{\n    let mut selector_caches = SelectorCaches::default();\n\n    let mut context = MatchingContext::new(\n        MatchingMode::Normal,\n        None,\n        &mut selector_caches,\n        quirks_mode,\n        NeedsSelectorFlags::No,\n        MatchingForInvalidation::No,\n    );\n    context.scope_element = Some(element.opaque());\n    context.current_host = element.containing_shadow_host().map(|e| e.opaque());\n\n    let mut current = Some(element);\n    while let Some(element) = current.take() {\n        if matching::matches_selector_list(selector_list, &element, &mut context) {\n            return Some(element);\n        }\n        current = element.parent_element();\n    }\n\n    return None;\n}\n\n/// A selector query abstraction, in order to be generic over QuerySelector and\n/// QuerySelectorAll.\npub trait SelectorQuery<E: TElement> {\n    /// The output of the query.\n    type Output;\n\n    /// Whether the query should stop after the first element has been matched.\n    fn should_stop_after_first_match() -> bool;\n\n    /// Append an element matching after the first query.\n    fn append_element(output: &mut Self::Output, element: E);\n\n    /// Returns true if the output is empty.\n    fn is_empty(output: &Self::Output) -> bool;\n}\n\n/// The result of a querySelectorAll call.\npub type QuerySelectorAllResult<E> = SmallVec<[E; 128]>;\n\n/// A query for all the elements in a subtree.\npub struct QueryAll;\n\nimpl<E: TElement> SelectorQuery<E> for QueryAll {\n    type Output = QuerySelectorAllResult<E>;\n\n    fn should_stop_after_first_match() -> bool {\n        false\n    }\n\n    fn append_element(output: &mut Self::Output, element: E) {\n        output.push(element);\n    }\n\n    fn is_empty(output: &Self::Output) -> bool {\n        output.is_empty()\n    }\n}\n\n/// A query for the first in-tree match of all the elements in a subtree.\npub struct QueryFirst;\n\nimpl<E: TElement> SelectorQuery<E> for QueryFirst {\n    type Output = Option<E>;\n\n    fn should_stop_after_first_match() -> bool {\n        true\n    }\n\n    fn append_element(output: &mut Self::Output, element: E) {\n        if output.is_none() {\n            *output = Some(element)\n        }\n    }\n\n    fn is_empty(output: &Self::Output) -> bool {\n        output.is_none()\n    }\n}\n\nstruct QuerySelectorProcessor<'a, 'b, E, Q>\nwhere\n    E: TElement + 'a,\n    Q: SelectorQuery<E>,\n    Q::Output: 'a,\n{\n    results: &'a mut Q::Output,\n    matching_context: MatchingContext<'b, E::Impl>,\n    traversal_map: SiblingTraversalMap<E>,\n    dependencies: &'a [Dependency],\n}\n\nimpl<'a, 'b, E, Q> InvalidationProcessor<'a, 'b, E> for QuerySelectorProcessor<'a, 'b, E, Q>\nwhere\n    E: TElement + 'a,\n    Q: SelectorQuery<E>,\n    Q::Output: 'a,\n{\n    fn light_tree_only(&self) -> bool {\n        true\n    }\n\n    fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option<OpaqueElement>) -> bool {\n        debug_assert!(\n            false,\n            \"How? We should only have parent-less dependencies here!\"\n        );\n        true\n    }\n\n    fn collect_invalidations(\n        &mut self,\n        element: E,\n        self_invalidations: &mut InvalidationVector<'a>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n        _sibling_invalidations: &mut InvalidationVector<'a>,\n    ) -> bool {\n        // TODO(emilio): If the element is not a root element, and\n        // selector_list has any descendant combinator, we need to do extra work\n        // in order to handle properly things like:\n        //\n        //   <div id=\"a\">\n        //     <div id=\"b\">\n        //       <div id=\"c\"></div>\n        //     </div>\n        //   </div>\n        //\n        // b.querySelector('#a div'); // Should return \"c\".\n        //\n        // For now, assert it's a root element.\n        debug_assert!(element.parent_element().is_none());\n\n        let target_vector = if self.matching_context.scope_element.is_some() {\n            &mut descendant_invalidations.dom_descendants\n        } else {\n            self_invalidations\n        };\n\n        for dependency in self.dependencies.iter() {\n            target_vector.push(Invalidation::new(\n                dependency,\n                self.matching_context.current_host.clone(),\n                self.matching_context.scope_element.clone(),\n            ))\n        }\n\n        false\n    }\n\n    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {\n        &mut self.matching_context\n    }\n\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {\n        &self.traversal_map\n    }\n\n    fn should_process_descendants(&mut self, _: E) -> bool {\n        if Q::should_stop_after_first_match() {\n            return Q::is_empty(&self.results);\n        }\n\n        true\n    }\n\n    fn invalidated_self(&mut self, e: E) {\n        Q::append_element(self.results, e);\n    }\n\n    fn invalidated_sibling(&mut self, e: E, _of: E) {\n        Q::append_element(self.results, e);\n    }\n\n    fn recursion_limit_exceeded(&mut self, _e: E) {}\n    fn invalidated_descendants(&mut self, _e: E, _child: E) {}\n}\n\nenum Operation {\n    Reject,\n    Accept,\n    RejectSkippingChildren,\n}\n\nimpl From<bool> for Operation {\n    #[inline(always)]\n    fn from(matches: bool) -> Self {\n        if matches {\n            Operation::Accept\n        } else {\n            Operation::Reject\n        }\n    }\n}\n\nfn collect_all_elements<E, Q, F>(root: E::ConcreteNode, results: &mut Q::Output, mut filter: F)\nwhere\n    E: TElement,\n    Q: SelectorQuery<E>,\n    F: FnMut(E) -> Operation,\n{\n    let mut iter = root.dom_descendants();\n    let mut cur = iter.next();\n    while let Some(node) = cur {\n        let element = match node.as_element() {\n            Some(e) => e,\n            None => {\n                cur = iter.next();\n                continue;\n            },\n        };\n        match filter(element) {\n            // Element matches - add to results and continue traversing its children.\n            Operation::Accept => {\n                Q::append_element(results, element);\n                if Q::should_stop_after_first_match() {\n                    return;\n                }\n            },\n            // Element doesn't match - skip it but continue traversing its children.\n            Operation::Reject => {},\n            // Element doesn't match and skip entire subtree.\n            Operation::RejectSkippingChildren => {\n                cur = iter.next_skipping_children();\n                continue;\n            },\n        }\n        cur = iter.next();\n    }\n}\n\n/// Returns whether a given element connected to `root` is descendant of `root`.\n///\n/// NOTE(emilio): if root == element, this returns false.\nfn connected_element_is_descendant_of<E>(element: E, root: E::ConcreteNode) -> bool\nwhere\n    E: TElement,\n{\n    // Optimize for when the root is a document or a shadow root and the element\n    // is connected to that root.\n    if root.as_document().is_some() {\n        debug_assert!(element.as_node().is_in_document(), \"Not connected?\");\n        debug_assert_eq!(\n            root,\n            root.owner_doc().as_node(),\n            \"Where did this element come from?\",\n        );\n        return true;\n    }\n\n    if root.as_shadow_root().is_some() {\n        debug_assert_eq!(\n            element.containing_shadow().unwrap().as_node(),\n            root,\n            \"Not connected?\"\n        );\n        return true;\n    }\n\n    let mut current = element.as_node().parent_node();\n    while let Some(n) = current.take() {\n        if n == root {\n            return true;\n        }\n\n        current = n.parent_node();\n    }\n    false\n}\n\n/// Fast path for iterating over every element with a given id in the document\n/// or shadow root that `root` is connected to.\nfn fast_connected_elements_with_id<'a, N>(\n    root: N,\n    id: &AtomIdent,\n    case_sensitivity: CaseSensitivity,\n) -> Result<&'a [N::ConcreteElement], ()>\nwhere\n    N: TNode + 'a,\n{\n    if case_sensitivity != CaseSensitivity::CaseSensitive {\n        return Err(());\n    }\n\n    if root.is_in_document() {\n        return root.owner_doc().elements_with_id(id);\n    }\n\n    if let Some(shadow) = root.as_shadow_root() {\n        return shadow.elements_with_id(id);\n    }\n\n    if let Some(shadow) = root.as_element().and_then(|e| e.containing_shadow()) {\n        return shadow.elements_with_id(id);\n    }\n\n    Err(())\n}\n\n/// Collects elements with a given id under `root`, that pass `filter`.\nfn collect_elements_with_id<E, Q, F>(\n    root: E::ConcreteNode,\n    id: &AtomIdent,\n    results: &mut Q::Output,\n    class_and_id_case_sensitivity: CaseSensitivity,\n    mut filter: F,\n) where\n    E: TElement,\n    Q: SelectorQuery<E>,\n    F: FnMut(E) -> bool,\n{\n    let elements = match fast_connected_elements_with_id(root, id, class_and_id_case_sensitivity) {\n        Ok(elements) => elements,\n        Err(()) => {\n            collect_all_elements::<E, Q, _>(root, results, |e| {\n                Operation::from(e.has_id(id, class_and_id_case_sensitivity) && filter(e))\n            });\n\n            return;\n        },\n    };\n\n    for element in elements {\n        // If the element is not an actual descendant of the root, even though\n        // it's connected, we don't really care about it.\n        if !connected_element_is_descendant_of(*element, root) {\n            continue;\n        }\n\n        if !filter(*element) {\n            continue;\n        }\n\n        Q::append_element(results, *element);\n        if Q::should_stop_after_first_match() {\n            break;\n        }\n    }\n}\n\nfn has_attr<E>(element: E, local_name: &crate::LocalName) -> bool\nwhere\n    E: TElement,\n{\n    let mut found = false;\n    element.each_attr_name(|name| found |= name == local_name);\n    found\n}\n\n#[inline(always)]\nfn local_name_matches<E>(element: E, local_name: &LocalName<E::Impl>) -> bool\nwhere\n    E: TElement,\n{\n    let LocalName {\n        ref name,\n        ref lower_name,\n    } = *local_name;\n\n    let chosen_name = if name == lower_name || element.is_html_element_in_html_document() {\n        lower_name\n    } else {\n        name\n    };\n\n    element.local_name() == &**chosen_name\n}\n\nfn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {\n    let (name, name_lower) = match component {\n        Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),\n        Component::AttributeInNoNamespaceExists {\n            ref local_name,\n            ref local_name_lower,\n            ..\n        } => (local_name, local_name_lower),\n        Component::AttributeOther(ref attr) => (&attr.local_name, &attr.local_name_lower),\n        _ => return None,\n    };\n    if name != name_lower {\n        return None; // TODO: Maybe optimize this?\n    }\n    Some(name)\n}\n\nfn get_id(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {\n    use selectors::attr::AttrSelectorOperator;\n    Some(match component {\n        Component::ID(ref id) => id,\n        Component::AttributeInNoNamespace {\n            ref operator,\n            ref local_name,\n            ref value,\n            ..\n        } => {\n            if *local_name != local_name!(\"id\") {\n                return None;\n            }\n            if *operator != AttrSelectorOperator::Equal {\n                return None;\n            }\n            AtomIdent::cast(&value.0)\n        },\n        _ => return None,\n    })\n}\n\n/// Fast paths for querySelector with a single simple selector.\nfn query_selector_single_query<E, Q>(\n    root: E::ConcreteNode,\n    component: &Component<E::Impl>,\n    results: &mut Q::Output,\n    class_and_id_case_sensitivity: CaseSensitivity,\n) -> Result<(), ()>\nwhere\n    E: TElement,\n    Q: SelectorQuery<E>,\n{\n    match *component {\n        Component::ExplicitUniversalType => {\n            collect_all_elements::<E, Q, _>(root, results, |_| Operation::Accept)\n        },\n        Component::Class(ref class) => {\n            // Bloom filter can only be used when case sensitive.\n            let bloom_hash = if class_and_id_case_sensitivity == CaseSensitivity::CaseSensitive {\n                Some(E::hash_for_bloom_filter(class.0.get_hash()))\n            } else {\n                None\n            };\n\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                if bloom_hash.is_some_and(|hash| !element.bloom_may_have_hash(hash)) {\n                    return Operation::RejectSkippingChildren;\n                }\n                Operation::from(element.has_class(class, class_and_id_case_sensitivity))\n            });\n        },\n        Component::LocalName(ref local_name) => {\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                Operation::from(local_name_matches(element, local_name))\n            })\n        },\n        Component::AttributeInNoNamespaceExists {\n            ref local_name,\n            ref local_name_lower,\n        } => {\n            // For HTML elements: C++ hashes lowercase\n            // For XUL/SVG/MathML elements: C++ hashes original case\n            let hash_original = E::hash_for_bloom_filter(local_name.0.get_hash());\n            let hash_lower = if local_name.0 == local_name_lower.0 {\n                hash_original\n            } else {\n                E::hash_for_bloom_filter(local_name_lower.0.get_hash())\n            };\n\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                // Check bloom filter first\n                let bloom_found_hash = if hash_original == hash_lower\n                    || !element.as_node().owner_doc().is_html_document()\n                {\n                    element.bloom_may_have_hash(hash_original)\n                } else if element.is_html_element_in_html_document() {\n                    // HTML elements store lowercase hashes\n                    element.bloom_may_have_hash(hash_lower)\n                } else {\n                    // Non-HTML elements in HTML documents might have HTML descendants\n                    // with lowercase-only hashes, so check both\n                    element.bloom_may_have_hash(hash_original)\n                        || element.bloom_may_have_hash(hash_lower)\n                };\n\n                if !bloom_found_hash {\n                    return Operation::RejectSkippingChildren;\n                }\n\n                Operation::from(element.has_attr_in_no_namespace(matching::select_name(\n                    &element,\n                    local_name,\n                    local_name_lower,\n                )))\n            });\n        },\n        Component::AttributeInNoNamespace {\n            ref local_name,\n            ref value,\n            operator,\n            case_sensitivity,\n        } => {\n            let empty_namespace = selectors::parser::namespace_empty_string::<E::Impl>();\n            let namespace_constraint = NamespaceConstraint::Specific(&empty_namespace);\n\n            // Only use bloom filter to check for attribute name existence.\n            let bloom_hash = E::hash_for_bloom_filter(local_name.0.get_hash());\n\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                if !element.bloom_may_have_hash(bloom_hash) {\n                    return Operation::RejectSkippingChildren;\n                }\n                Operation::from(element.attr_matches(\n                    &namespace_constraint,\n                    local_name,\n                    &AttrSelectorOperation::WithValue {\n                        operator,\n                        case_sensitivity: matching::to_unconditional_case_sensitivity(\n                            case_sensitivity,\n                            &element,\n                        ),\n                        value,\n                    },\n                ))\n            });\n        },\n        ref other => {\n            let id = match get_id(other) {\n                Some(id) => id,\n                // TODO(emilio): More fast paths?\n                None => return Err(()),\n            };\n            collect_elements_with_id::<E, Q, _>(\n                root,\n                id,\n                results,\n                class_and_id_case_sensitivity,\n                |_| true,\n            );\n        },\n    }\n\n    Ok(())\n}\n\nenum SimpleFilter<'a> {\n    Class(&'a AtomIdent),\n    Attr(&'a crate::LocalName),\n    LocalName(&'a LocalName<SelectorImpl>),\n}\n\n/// Fast paths for a given selector query.\n///\n/// When there's only one component, we go directly to\n/// `query_selector_single_query`, otherwise, we try to optimize by looking just\n/// at the subtrees rooted at ids in the selector, and otherwise we try to look\n/// up by class name or local name in the rightmost compound.\n///\n/// FIXME(emilio, nbp): This may very well be a good candidate for code to be\n/// replaced by HolyJit :)\nfn query_selector_fast<E, Q>(\n    root: E::ConcreteNode,\n    selector_list: &SelectorList<E::Impl>,\n    results: &mut Q::Output,\n    matching_context: &mut MatchingContext<E::Impl>,\n) -> Result<(), ()>\nwhere\n    E: TElement,\n    Q: SelectorQuery<E>,\n{\n    // We need to return elements in document order, and reordering them\n    // afterwards is kinda silly.\n    if selector_list.len() > 1 {\n        return Err(());\n    }\n\n    let selector = &selector_list.slice()[0];\n    let class_and_id_case_sensitivity = matching_context.classes_and_ids_case_sensitivity();\n    // Let's just care about the easy cases for now.\n    if selector.len() == 1 {\n        if query_selector_single_query::<E, Q>(\n            root,\n            selector.iter().next().unwrap(),\n            results,\n            class_and_id_case_sensitivity,\n        )\n        .is_ok()\n        {\n            return Ok(());\n        }\n    }\n\n    let mut iter = selector.iter();\n    let mut combinator: Option<Combinator> = None;\n\n    // We want to optimize some cases where there's no id involved whatsoever,\n    // like `.foo .bar`, but we don't want to make `#foo .bar` slower because of\n    // that.\n    let mut simple_filter = None;\n\n    'selector_loop: loop {\n        debug_assert!(combinator.map_or(true, |c| !c.is_sibling()));\n\n        'component_loop: for component in &mut iter {\n            match *component {\n                Component::Class(ref class) => {\n                    if combinator.is_none() {\n                        simple_filter = Some(SimpleFilter::Class(class));\n                    }\n                },\n                Component::LocalName(ref local_name) => {\n                    if combinator.is_none() {\n                        // Prefer to look at class rather than local-name if\n                        // both are present.\n                        if let Some(SimpleFilter::Class(..)) = simple_filter {\n                            continue;\n                        }\n                        simple_filter = Some(SimpleFilter::LocalName(local_name));\n                    }\n                },\n                ref other => {\n                    if let Some(id) = get_id(other) {\n                        if combinator.is_none() {\n                            // In the rightmost compound, just find descendants of root that match\n                            // the selector list with that id.\n                            collect_elements_with_id::<E, Q, _>(\n                                root,\n                                id,\n                                results,\n                                class_and_id_case_sensitivity,\n                                |e| {\n                                    matching::matches_selector_list(\n                                        selector_list,\n                                        &e,\n                                        matching_context,\n                                    )\n                                },\n                            );\n                            return Ok(());\n                        }\n\n                        let elements = fast_connected_elements_with_id(\n                            root,\n                            id,\n                            class_and_id_case_sensitivity,\n                        )?;\n                        if elements.is_empty() {\n                            return Ok(());\n                        }\n\n                        // Results need to be in document order. Let's not bother\n                        // reordering or deduplicating nodes, which we would need to\n                        // do if one element with the given id were a descendant of\n                        // another element with that given id.\n                        if !Q::should_stop_after_first_match() && elements.len() > 1 {\n                            continue;\n                        }\n\n                        for element in elements {\n                            // If the element is not a descendant of the root, then\n                            // it may have descendants that match our selector that\n                            // _are_ descendants of the root, and other descendants\n                            // that match our selector that are _not_.\n                            //\n                            // So we can't just walk over the element's descendants\n                            // and match the selector against all of them, nor can\n                            // we skip looking at this element's descendants.\n                            //\n                            // Give up on trying to optimize based on this id and\n                            // keep walking our selector.\n                            if !connected_element_is_descendant_of(*element, root) {\n                                continue 'component_loop;\n                            }\n\n                            query_selector_slow::<E, Q>(\n                                element.as_node(),\n                                selector_list,\n                                results,\n                                matching_context,\n                            );\n\n                            if Q::should_stop_after_first_match() && !Q::is_empty(&results) {\n                                break;\n                            }\n                        }\n\n                        return Ok(());\n                    }\n                    if combinator.is_none() && simple_filter.is_none() {\n                        if let Some(attr_name) = get_attr_name(other) {\n                            simple_filter = Some(SimpleFilter::Attr(attr_name));\n                        }\n                    }\n                },\n            }\n        }\n\n        loop {\n            let next_combinator = match iter.next_sequence() {\n                None => break 'selector_loop,\n                Some(c) => c,\n            };\n\n            // We don't want to scan stuff affected by sibling combinators,\n            // given we scan the subtree of elements with a given id (and we\n            // don't want to care about scanning the siblings' subtrees).\n            if next_combinator.is_sibling() {\n                // Advance to the next combinator.\n                for _ in &mut iter {}\n                continue;\n            }\n\n            combinator = Some(next_combinator);\n            break;\n        }\n    }\n\n    // We got here without finding any ID or such that we could handle. Try to\n    // use one of the simple filters.\n    let simple_filter = match simple_filter {\n        Some(f) => f,\n        None => return Err(()),\n    };\n\n    match simple_filter {\n        SimpleFilter::Class(ref class) => {\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                Operation::from(\n                    element.has_class(class, class_and_id_case_sensitivity)\n                        && matching::matches_selector_list(\n                            selector_list,\n                            &element,\n                            matching_context,\n                        ),\n                )\n            });\n        },\n        SimpleFilter::LocalName(ref local_name) => {\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                Operation::from(\n                    local_name_matches(element, local_name)\n                        && matching::matches_selector_list(\n                            selector_list,\n                            &element,\n                            matching_context,\n                        ),\n                )\n            });\n        },\n        SimpleFilter::Attr(ref local_name) => {\n            collect_all_elements::<E, Q, _>(root, results, |element| {\n                Operation::from(\n                    has_attr(element, local_name)\n                        && matching::matches_selector_list(\n                            selector_list,\n                            &element,\n                            matching_context,\n                        ),\n                )\n            });\n        },\n    }\n\n    Ok(())\n}\n\n// Slow path for a given selector query.\nfn query_selector_slow<E, Q>(\n    root: E::ConcreteNode,\n    selector_list: &SelectorList<E::Impl>,\n    results: &mut Q::Output,\n    matching_context: &mut MatchingContext<E::Impl>,\n) where\n    E: TElement,\n    Q: SelectorQuery<E>,\n{\n    collect_all_elements::<E, Q, _>(root, results, |element| {\n        Operation::from(matching::matches_selector_list(\n            selector_list,\n            &element,\n            matching_context,\n        ))\n    });\n}\n\n/// Whether the invalidation machinery should be used for this query.\n#[derive(PartialEq)]\npub enum MayUseInvalidation {\n    /// We may use it if we deem it useful.\n    Yes,\n    /// Don't use it.\n    No,\n}\n\n/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>\npub fn query_selector<E, Q>(\n    root: E::ConcreteNode,\n    selector_list: &SelectorList<E::Impl>,\n    results: &mut Q::Output,\n    may_use_invalidation: MayUseInvalidation,\n) where\n    E: TElement,\n    Q: SelectorQuery<E>,\n{\n    use crate::invalidation::element::invalidator::TreeStyleInvalidator;\n\n    let mut selector_caches = SelectorCaches::default();\n    let quirks_mode = root.owner_doc().quirks_mode();\n\n    let mut matching_context = MatchingContext::new(\n        MatchingMode::Normal,\n        None,\n        &mut selector_caches,\n        quirks_mode,\n        NeedsSelectorFlags::No,\n        MatchingForInvalidation::No,\n    );\n    let root_element = root.as_element();\n    matching_context.scope_element = root_element.map(|e| e.opaque());\n    matching_context.current_host = match root_element {\n        Some(root) => root.containing_shadow_host().map(|host| host.opaque()),\n        None => root.as_shadow_root().map(|root| root.host().opaque()),\n    };\n\n    let fast_result =\n        query_selector_fast::<E, Q>(root, selector_list, results, &mut matching_context);\n\n    if fast_result.is_ok() {\n        return;\n    }\n\n    // Slow path: Use the invalidation machinery if we're a root, and tree\n    // traversal otherwise.\n    //\n    // See the comment in collect_invalidations to see why only if we're a root.\n    //\n    // The invalidation mechanism is only useful in presence of combinators.\n    //\n    // We could do that check properly here, though checking the length of the\n    // selectors is a good heuristic.\n    //\n    // A selector with a combinator needs to have a length of at least 3: A\n    // simple selector, a combinator, and another simple selector.\n    let invalidation_may_be_useful = may_use_invalidation == MayUseInvalidation::Yes\n        && selector_list.slice().iter().any(|s| s.len() > 2);\n\n    if root_element.is_some() || !invalidation_may_be_useful {\n        query_selector_slow::<E, Q>(root, selector_list, results, &mut matching_context);\n    } else {\n        let dependencies = selector_list\n            .slice()\n            .iter()\n            .map(|selector| Dependency::for_full_selector_invalidation(selector.clone()))\n            .collect::<SmallVec<[_; 5]>>();\n        let mut processor = QuerySelectorProcessor::<E, Q> {\n            results,\n            matching_context,\n            traversal_map: SiblingTraversalMap::default(),\n            dependencies: &dependencies,\n        };\n\n        for node in root.dom_children() {\n            if let Some(e) = node.as_element() {\n                TreeStyleInvalidator::new(e, /* stack_limit_checker = */ None, &mut processor)\n                    .invalidate();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "style/driver.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Implements traversal over the DOM tree. The traversal starts in sequential\n//! mode, and optionally parallelizes as it discovers work.\n\n#![deny(missing_docs)]\n\nuse crate::context::{PerThreadTraversalStatistics, StyleContext};\nuse crate::context::{ThreadLocalStyleContext, TraversalStatistics};\nuse crate::dom::{SendNode, TElement, TNode};\nuse crate::parallel;\nuse crate::scoped_tls::ScopedTLS;\nuse crate::traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};\nuse std::collections::VecDeque;\nuse std::time::Instant;\n\n#[cfg(feature = \"servo\")]\nfn should_report_statistics() -> bool {\n    false\n}\n\n#[cfg(feature = \"gecko\")]\nfn should_report_statistics() -> bool {\n    unsafe { !crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton.is_null() }\n}\n\n#[cfg(feature = \"servo\")]\nfn report_statistics(_stats: &PerThreadTraversalStatistics) {\n    unreachable!(\"Servo never report stats\");\n}\n\n#[cfg(feature = \"gecko\")]\nfn report_statistics(stats: &PerThreadTraversalStatistics) {\n    // This should only be called in the main thread, or it may be racy\n    // to update the statistics in a global variable.\n    unsafe {\n        debug_assert!(crate::gecko_bindings::bindings::Gecko_IsMainThread());\n        let gecko_stats = crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton;\n        (*gecko_stats).mElementsTraversed += stats.elements_traversed;\n        (*gecko_stats).mElementsStyled += stats.elements_styled;\n        (*gecko_stats).mElementsMatched += stats.elements_matched;\n        (*gecko_stats).mStylesShared += stats.styles_shared;\n        (*gecko_stats).mStylesReused += stats.styles_reused;\n    }\n}\n\nfn with_pool_in_place_scope<'scope>(\n    work_unit_max: usize,\n    pool: Option<&rayon::ThreadPool>,\n    closure: impl FnOnce(Option<&rayon::ScopeFifo<'scope>>) + Send + 'scope,\n) {\n    if work_unit_max == 0 || pool.is_none() {\n        closure(None);\n    } else {\n        let pool = pool.unwrap();\n        pool.in_place_scope_fifo(|scope| {\n            #[cfg(feature = \"gecko\")]\n            debug_assert_eq!(\n                pool.current_thread_index(),\n                Some(0),\n                \"Main thread should be the first thread\"\n            );\n            if cfg!(feature = \"gecko\") || pool.current_thread_index().is_some() {\n                closure(Some(scope));\n            } else {\n                scope.spawn_fifo(|scope| closure(Some(scope)));\n            }\n        });\n    }\n}\n\n/// See documentation of the pref for performance characteristics.\nfn work_unit_max() -> usize {\n    static_prefs::pref!(\"layout.css.stylo-work-unit-size\") as usize\n}\n\n/// Do a DOM traversal for top-down and (optionally) bottom-up processing, generic over `D`.\n///\n/// We use an adaptive traversal strategy. We start out with simple sequential processing, until we\n/// arrive at a wide enough level in the DOM that the parallel traversal would parallelize it.\n/// If a thread pool is provided, we then transfer control over to the parallel traversal.\n///\n/// Returns true if the traversal was parallel, and also returns the statistics object containing\n/// information on nodes traversed (on nightly only). Not all of its fields will be initialized\n/// since we don't call finish().\npub fn traverse_dom<E, D>(\n    traversal: &D,\n    token: PreTraverseToken<E>,\n    pool: Option<&rayon::ThreadPool>,\n) -> E\nwhere\n    E: TElement,\n    D: DomTraversal<E>,\n{\n    let root = token\n        .traversal_root()\n        .expect(\"Should've ensured we needed to traverse\");\n\n    let report_stats = should_report_statistics();\n    let dump_stats = traversal.shared_context().options.dump_style_statistics;\n    let start_time = if dump_stats {\n        Some(Instant::now())\n    } else {\n        None\n    };\n\n    // Declare the main-thread context, as well as the worker-thread contexts,\n    // which we may or may not instantiate. It's important to declare the worker-\n    // thread contexts first, so that they get dropped second. This matters because:\n    //   * ThreadLocalContexts borrow AtomicRefCells in TLS.\n    //   * Dropping a ThreadLocalContext can run SequentialTasks.\n    //   * Sequential tasks may call into functions like\n    //     Servo_StyleSet_GetBaseComputedValuesForElement, which instantiate a\n    //     ThreadLocalStyleContext on the main thread. If the main thread\n    //     ThreadLocalStyleContext has not released its TLS borrow by that point,\n    //     we'll panic on double-borrow.\n    let mut scoped_tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);\n    // Process the nodes breadth-first. This helps keep similar traversal characteristics for the\n    // style sharing cache.\n    let work_unit_max = work_unit_max();\n\n    let send_root = unsafe { SendNode::new(root.as_node()) };\n    with_pool_in_place_scope(work_unit_max, pool, |maybe_scope| {\n        let mut tlc = scoped_tls.ensure(parallel::create_thread_local_context);\n        let mut context = StyleContext {\n            shared: traversal.shared_context(),\n            thread_local: &mut tlc,\n        };\n\n        let mut discovered = VecDeque::with_capacity(work_unit_max * 2);\n        let current_dom_depth = send_root.depth();\n        let opaque_root = send_root.opaque();\n        discovered.push_back(send_root);\n        parallel::style_trees(\n            &mut context,\n            discovered,\n            opaque_root,\n            work_unit_max,\n            PerLevelTraversalData { current_dom_depth },\n            maybe_scope,\n            traversal,\n            &scoped_tls,\n        );\n    });\n\n    // Collect statistics from thread-locals if requested.\n    if dump_stats || report_stats {\n        let mut aggregate = PerThreadTraversalStatistics::default();\n        for slot in scoped_tls.slots() {\n            if let Some(cx) = slot.get_mut() {\n                aggregate += cx.statistics.clone();\n            }\n        }\n\n        if report_stats {\n            report_statistics(&aggregate);\n        }\n        // dump statistics to stdout if requested\n        if dump_stats {\n            let parallel = pool.is_some();\n            let stats =\n                TraversalStatistics::new(aggregate, traversal, parallel, start_time.unwrap());\n            if stats.is_large {\n                println!(\"{}\", stats);\n            }\n        }\n    }\n\n    root\n}\n"
  },
  {
    "path": "style/error_reporting.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Types used to report parsing errors.\n\n#![deny(missing_docs)]\n\nuse crate::selector_parser::SelectorImpl;\nuse crate::stylesheets::UrlExtraData;\nuse cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};\nuse selectors::parser::{Combinator, Component, RelativeSelector, Selector};\nuse selectors::visitor::{SelectorListKind, SelectorVisitor};\nuse selectors::SelectorList;\nuse std::fmt;\nuse style_traits::ParseError;\n\n/// Errors that can be encountered while parsing CSS.\n#[derive(Debug)]\npub enum ContextualParseError<'a> {\n    /// A property declaration was not recognized.\n    UnsupportedPropertyDeclaration(&'a str, ParseError<'a>, &'a [SelectorList<SelectorImpl>]),\n    /// A property descriptor was not recognized.\n    UnsupportedPropertyDescriptor(&'a str, ParseError<'a>),\n    /// A font face descriptor was not recognized.\n    UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>),\n    /// A font feature values descriptor was not recognized.\n    UnsupportedFontFeatureValuesDescriptor(&'a str, ParseError<'a>),\n    /// A font palette values descriptor was not recognized.\n    UnsupportedFontPaletteValuesDescriptor(&'a str, ParseError<'a>),\n    /// A keyframe rule was not valid.\n    InvalidKeyframeRule(&'a str, ParseError<'a>),\n    /// A font feature values rule was not valid.\n    InvalidFontFeatureValuesRule(&'a str, ParseError<'a>),\n    /// A rule was invalid for some reason.\n    InvalidRule(&'a str, ParseError<'a>),\n    /// A rule was not recognized.\n    UnsupportedRule(&'a str, ParseError<'a>),\n    /// A viewport descriptor declaration was not recognized.\n    UnsupportedViewportDescriptorDeclaration(&'a str, ParseError<'a>),\n    /// A counter style descriptor declaration was not recognized.\n    UnsupportedCounterStyleDescriptorDeclaration(&'a str, ParseError<'a>),\n    /// A counter style rule had no symbols.\n    InvalidCounterStyleWithoutSymbols(String),\n    /// A counter style rule had less than two symbols.\n    InvalidCounterStyleNotEnoughSymbols(String),\n    /// A counter style rule did not have additive-symbols.\n    InvalidCounterStyleWithoutAdditiveSymbols,\n    /// A counter style rule had extends with symbols.\n    InvalidCounterStyleExtendsWithSymbols,\n    /// A counter style rule had extends with additive-symbols.\n    InvalidCounterStyleExtendsWithAdditiveSymbols,\n    /// A media rule was invalid for some reason.\n    InvalidMediaRule(&'a str, ParseError<'a>),\n    /// A value was not recognized.\n    UnsupportedValue(&'a str, ParseError<'a>),\n    /// A never-matching `:host` selector was found.\n    NeverMatchingHostSelector(String),\n    /// A view-transition declaration was not recognized.\n    UnsupportedViewTransitionDescriptor(&'a str, ParseError<'a>),\n}\n\nimpl<'a> fmt::Display for ContextualParseError<'a> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fn token_to_str(t: &Token, f: &mut fmt::Formatter) -> fmt::Result {\n            match *t {\n                Token::Ident(ref i) => write!(f, \"identifier {}\", i),\n                Token::AtKeyword(ref kw) => write!(f, \"keyword @{}\", kw),\n                Token::Hash(ref h) => write!(f, \"hash #{}\", h),\n                Token::IDHash(ref h) => write!(f, \"id selector #{}\", h),\n                Token::QuotedString(ref s) => write!(f, \"quoted string \\\"{}\\\"\", s),\n                Token::UnquotedUrl(ref u) => write!(f, \"url {}\", u),\n                Token::Delim(ref d) => write!(f, \"delimiter {}\", d),\n                Token::Number {\n                    int_value: Some(i), ..\n                } => write!(f, \"number {}\", i),\n                Token::Number { value, .. } => write!(f, \"number {}\", value),\n                Token::Percentage {\n                    int_value: Some(i), ..\n                } => write!(f, \"percentage {}\", i),\n                Token::Percentage { unit_value, .. } => {\n                    write!(f, \"percentage {}\", unit_value * 100.)\n                },\n                Token::Dimension {\n                    value, ref unit, ..\n                } => write!(f, \"dimension {}{}\", value, unit),\n                Token::WhiteSpace(_) => write!(f, \"whitespace\"),\n                Token::Comment(_) => write!(f, \"comment\"),\n                Token::Colon => write!(f, \"colon (:)\"),\n                Token::Semicolon => write!(f, \"semicolon (;)\"),\n                Token::Comma => write!(f, \"comma (,)\"),\n                Token::IncludeMatch => write!(f, \"include match (~=)\"),\n                Token::DashMatch => write!(f, \"dash match (|=)\"),\n                Token::PrefixMatch => write!(f, \"prefix match (^=)\"),\n                Token::SuffixMatch => write!(f, \"suffix match ($=)\"),\n                Token::SubstringMatch => write!(f, \"substring match (*=)\"),\n                Token::CDO => write!(f, \"CDO (<!--)\"),\n                Token::CDC => write!(f, \"CDC (-->)\"),\n                Token::Function(ref name) => write!(f, \"function {}\", name),\n                Token::ParenthesisBlock => write!(f, \"parenthesis (\"),\n                Token::SquareBracketBlock => write!(f, \"square bracket [\"),\n                Token::CurlyBracketBlock => write!(f, \"curly bracket {{\"),\n                Token::BadUrl(ref _u) => write!(f, \"bad url parse error\"),\n                Token::BadString(ref _s) => write!(f, \"bad string parse error\"),\n                Token::CloseParenthesis => write!(f, \"unmatched close parenthesis\"),\n                Token::CloseSquareBracket => write!(f, \"unmatched close square bracket\"),\n                Token::CloseCurlyBracket => write!(f, \"unmatched close curly bracket\"),\n            }\n        }\n\n        fn parse_error_to_str(err: &ParseError, f: &mut fmt::Formatter) -> fmt::Result {\n            match err.kind {\n                ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(ref t)) => {\n                    write!(f, \"found unexpected \")?;\n                    token_to_str(t, f)\n                },\n                ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput) => {\n                    write!(f, \"unexpected end of input\")\n                },\n                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleInvalid(ref i)) => {\n                    write!(f, \"@ rule invalid: {}\", i)\n                },\n                ParseErrorKind::Basic(BasicParseErrorKind::AtRuleBodyInvalid) => {\n                    write!(f, \"@ rule invalid\")\n                },\n                ParseErrorKind::Basic(BasicParseErrorKind::QualifiedRuleInvalid) => {\n                    write!(f, \"qualified rule invalid\")\n                },\n                ParseErrorKind::Custom(ref err) => write!(f, \"{:?}\", err),\n            }\n        }\n\n        match *self {\n            ContextualParseError::UnsupportedPropertyDeclaration(decl, ref err, _selectors) => {\n                write!(f, \"Unsupported property declaration: '{}', \", decl)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @property descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @font-face descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedFontFeatureValuesDescriptor(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @font-feature-values descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedFontPaletteValuesDescriptor(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @font-palette-values descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::InvalidKeyframeRule(rule, ref err) => {\n                write!(f, \"Invalid keyframe rule: '{}', \", rule)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::InvalidFontFeatureValuesRule(rule, ref err) => {\n                write!(f, \"Invalid font feature value rule: '{}', \", rule)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::InvalidRule(rule, ref err) => {\n                write!(f, \"Invalid rule: '{}', \", rule)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedRule(rule, ref err) => {\n                write!(f, \"Unsupported rule: '{}', \", rule)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedViewportDescriptorDeclaration(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @viewport descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedCounterStyleDescriptorDeclaration(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @counter-style descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::InvalidCounterStyleWithoutSymbols(ref system) => write!(\n                f,\n                \"Invalid @counter-style rule: 'system: {}' without 'symbols'\",\n                system\n            ),\n            ContextualParseError::InvalidCounterStyleNotEnoughSymbols(ref system) => write!(\n                f,\n                \"Invalid @counter-style rule: 'system: {}' less than two 'symbols'\",\n                system\n            ),\n            ContextualParseError::InvalidCounterStyleWithoutAdditiveSymbols => write!(\n                f,\n                \"Invalid @counter-style rule: 'system: additive' without 'additive-symbols'\"\n            ),\n            ContextualParseError::InvalidCounterStyleExtendsWithSymbols => write!(\n                f,\n                \"Invalid @counter-style rule: 'system: extends …' with 'symbols'\"\n            ),\n            ContextualParseError::InvalidCounterStyleExtendsWithAdditiveSymbols => write!(\n                f,\n                \"Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'\"\n            ),\n            ContextualParseError::InvalidMediaRule(media_rule, ref err) => {\n                write!(f, \"Invalid media rule: {}, \", media_rule)?;\n                parse_error_to_str(err, f)\n            },\n            ContextualParseError::UnsupportedValue(_value, ref err) => parse_error_to_str(err, f),\n            ContextualParseError::NeverMatchingHostSelector(ref selector) => {\n                write!(f, \":host selector is not featureless: {}\", selector)\n            },\n            ContextualParseError::UnsupportedViewTransitionDescriptor(decl, ref err) => {\n                write!(\n                    f,\n                    \"Unsupported @view-transition descriptor declaration: '{}', \",\n                    decl\n                )?;\n                parse_error_to_str(err, f)\n            },\n        }\n    }\n}\n\n/// A generic trait for an error reporter.\npub trait ParseErrorReporter {\n    /// Called when the style engine detects an error.\n    ///\n    /// Returns the current input being parsed, the source location it was\n    /// reported from, and a message.\n    fn report_error(\n        &self,\n        url: &UrlExtraData,\n        location: SourceLocation,\n        error: ContextualParseError,\n    );\n}\n\n/// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)\n/// at `info` level.\n///\n/// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`\n/// environment variable.\n/// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)\n#[cfg(feature = \"servo\")]\npub struct RustLogReporter;\n\n#[cfg(feature = \"servo\")]\nimpl ParseErrorReporter for RustLogReporter {\n    fn report_error(\n        &self,\n        url: &UrlExtraData,\n        location: SourceLocation,\n        error: ContextualParseError,\n    ) {\n        if log_enabled!(log::Level::Info) {\n            info!(\n                \"Url:\\t{}\\n{}:{} {}\",\n                url.as_str(),\n                location.line,\n                location.column,\n                error\n            )\n        }\n    }\n}\n\n/// Any warning a selector may generate.\n/// TODO(dshin): Bug 1860634 - Merge with never matching host selector warning, which is part of the rule parser.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\npub enum SelectorWarningKind {\n    /// Relative Selector with not enough constraint, either outside or inside the selector. e.g. `*:has(.a)`, `.a:has(*)`.\n    /// May cause expensive invalidations for every element inserted and/or removed.\n    UnconstraintedRelativeSelector,\n    /// `:scope` can have 3 meanings, but in all cases, the relationship is defined strictly by an ancestor-descendant\n    /// relationship. This means that any presence of sibling selectors to its right would make it never match.\n    SiblingCombinatorAfterScopeSelector,\n}\n\nimpl SelectorWarningKind {\n    /// Get all warnings for this selector.\n    pub fn from_selector(selector: &Selector<SelectorImpl>) -> Vec<Self> {\n        let mut result = vec![];\n        if UnconstrainedRelativeSelectorVisitor::has_warning(selector, 0, false) {\n            result.push(SelectorWarningKind::UnconstraintedRelativeSelector);\n        }\n        if SiblingCombinatorAfterScopeSelectorVisitor::has_warning(selector) {\n            result.push(SelectorWarningKind::SiblingCombinatorAfterScopeSelector);\n        }\n        result\n    }\n}\n\n/// Per-compound state for finding unconstrained relative selectors.\nstruct PerCompoundState {\n    /// Is there a relative selector in this compound?\n    relative_selector_found: bool,\n    /// Is this compound constrained in any way?\n    constrained: bool,\n    /// Nested below, or inside relative selector?\n    in_relative_selector: bool,\n}\n\nimpl PerCompoundState {\n    fn new(in_relative_selector: bool) -> Self {\n        Self {\n            relative_selector_found: false,\n            constrained: false,\n            in_relative_selector,\n        }\n    }\n}\n\n/// Visitor to check if there's any unconstrained relative selector.\nstruct UnconstrainedRelativeSelectorVisitor {\n    compound_state: PerCompoundState,\n}\n\nimpl UnconstrainedRelativeSelectorVisitor {\n    fn new(in_relative_selector: bool) -> Self {\n        Self {\n            compound_state: PerCompoundState::new(in_relative_selector),\n        }\n    }\n\n    fn has_warning(\n        selector: &Selector<SelectorImpl>,\n        offset: usize,\n        in_relative_selector: bool,\n    ) -> bool {\n        let relative_selector = matches!(\n            selector.iter_raw_parse_order_from(0).next().unwrap(),\n            Component::RelativeSelectorAnchor\n        );\n        debug_assert!(\n            !relative_selector || offset == 0,\n            \"Checking relative selector from non-rightmost?\"\n        );\n        let mut visitor = Self::new(in_relative_selector);\n        let mut iter = if relative_selector {\n            selector.iter_skip_relative_selector_anchor()\n        } else {\n            selector.iter_from(offset)\n        };\n        loop {\n            visitor.compound_state = PerCompoundState::new(in_relative_selector);\n\n            for s in &mut iter {\n                s.visit(&mut visitor);\n            }\n\n            if (visitor.compound_state.relative_selector_found\n                || visitor.compound_state.in_relative_selector)\n                && !visitor.compound_state.constrained\n            {\n                return true;\n            }\n\n            if iter.next_sequence().is_none() {\n                break;\n            }\n        }\n        false\n    }\n}\n\nimpl SelectorVisitor for UnconstrainedRelativeSelectorVisitor {\n    type Impl = SelectorImpl;\n\n    fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {\n        match c {\n            // Deferred to visit_selector_list\n            Component::Is(..)\n            | Component::Where(..)\n            | Component::Negation(..)\n            | Component::Has(..) => (),\n            Component::ExplicitUniversalType => (),\n            _ => self.compound_state.constrained |= true,\n        };\n        true\n    }\n\n    fn visit_selector_list(\n        &mut self,\n        _list_kind: SelectorListKind,\n        list: &[Selector<Self::Impl>],\n    ) -> bool {\n        let mut all_constrained = true;\n        for s in list {\n            let mut offset = 0;\n            // First, check the rightmost compound for constraint at this level.\n            if !self.compound_state.in_relative_selector {\n                let mut nested = Self::new(false);\n                let mut iter = s.iter();\n                loop {\n                    for c in &mut iter {\n                        c.visit(&mut nested);\n                        offset += 1;\n                    }\n\n                    let c = iter.next_sequence();\n                    offset += 1;\n                    if c.map_or(true, |c| !c.is_pseudo_element()) {\n                        break;\n                    }\n                }\n                // Every single selector in the list must be constrained.\n                all_constrained &= nested.compound_state.constrained;\n            }\n\n            if offset >= s.len() {\n                continue;\n            }\n\n            // Then, recurse in to check at the deeper level.\n            if Self::has_warning(s, offset, self.compound_state.in_relative_selector) {\n                self.compound_state.constrained = false;\n                if !self.compound_state.in_relative_selector {\n                    self.compound_state.relative_selector_found = true;\n                }\n                return false;\n            }\n        }\n        self.compound_state.constrained |= all_constrained;\n        true\n    }\n\n    fn visit_relative_selector_list(&mut self, list: &[RelativeSelector<Self::Impl>]) -> bool {\n        debug_assert!(\n            !self.compound_state.in_relative_selector,\n            \"Nested relative selector\"\n        );\n        self.compound_state.relative_selector_found = true;\n\n        for rs in list {\n            // If the inside is unconstrained, we are unconstrained no matter what.\n            if Self::has_warning(&rs.selector, 0, true) {\n                self.compound_state.constrained = false;\n                return false;\n            }\n        }\n        true\n    }\n}\n\nstruct SiblingCombinatorAfterScopeSelectorVisitor {\n    right_combinator_is_sibling: bool,\n    found: bool,\n}\n\nimpl SiblingCombinatorAfterScopeSelectorVisitor {\n    fn new(right_combinator_is_sibling: bool) -> Self {\n        Self {\n            right_combinator_is_sibling,\n            found: false,\n        }\n    }\n    fn has_warning(selector: &Selector<SelectorImpl>) -> bool {\n        if !selector.has_scope_selector() {\n            return false;\n        }\n        let visitor = SiblingCombinatorAfterScopeSelectorVisitor::new(false);\n        visitor.find_never_matching_scope_selector(selector)\n    }\n\n    fn find_never_matching_scope_selector(mut self, selector: &Selector<SelectorImpl>) -> bool {\n        selector.visit(&mut self);\n        self.found\n    }\n}\n\nimpl SelectorVisitor for SiblingCombinatorAfterScopeSelectorVisitor {\n    type Impl = SelectorImpl;\n\n    fn visit_simple_selector(&mut self, c: &Component<Self::Impl>) -> bool {\n        if !matches!(c, Component::Scope | Component::ImplicitScope) {\n            return true;\n        }\n        // e.g. `:scope ~ .a` will never match.\n        if self.right_combinator_is_sibling {\n            self.found = true;\n        }\n        true\n    }\n\n    fn visit_selector_list(\n        &mut self,\n        _list_kind: SelectorListKind,\n        list: &[Selector<Self::Impl>],\n    ) -> bool {\n        for s in list {\n            let list_visitor = Self::new(self.right_combinator_is_sibling);\n            self.found |= list_visitor.find_never_matching_scope_selector(s);\n        }\n        true\n    }\n\n    fn visit_complex_selector(&mut self, combinator_to_right: Option<Combinator>) -> bool {\n        if let Some(c) = combinator_to_right {\n            // Subject compounds' state is determined by the outer visitor. e.g: When there's `:is(.a .b) ~ .c`,\n            // the inner visitor is assumed to be constructed with right_combinator_is_sibling == true.\n            self.right_combinator_is_sibling = c.is_sibling();\n        }\n        true\n    }\n\n    // It's harder to discern if use of :scope <sibling-combinator> is invalid - at least for now, defer.\n}\n"
  },
  {
    "path": "style/font_face.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@font-face`][ff] at-rule.\n//!\n//! [ff]: https://drafts.csswg.org/css-fonts/#at-font-face-rule\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::values::generics::font::FontStyle as GenericFontStyle;\nuse crate::values::specified::{url::SpecifiedUrl, Angle};\nuse cssparser::{Parser, RuleBodyParser, SourceLocation};\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\npub use crate::properties::font_face::{DescriptorId, DescriptorParser, Descriptors};\npub use crate::values::computed::font::{FamilyName, FontStretch};\npub use crate::values::specified::font::{\n    AbsoluteFontWeight, FontFeatureSettings, FontLanguageOverride,\n    FontStretch as SpecifiedFontStretch, FontVariationSettings, MetricsOverride,\n    SpecifiedFontStyle,\n};\n\n/// A source for a font-face rule.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub enum Source {\n    /// A `url()` source.\n    Url(UrlSource),\n    /// A `local()` source.\n    #[css(function)]\n    Local(FamilyName),\n}\n\n/// A list of sources for the font-face src descriptor.\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[css(comma)]\npub struct SourceList(#[css(iterable)] pub Vec<Source>);\n\n// We can't just use OneOrMoreSeparated to derive Parse for the Source list,\n// because we want to filter out components that parsed as None, then fail if no\n// valid components remain. So we provide our own implementation here.\nimpl Parse for SourceList {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Parse the comma-separated list, then let filter_map discard any None items.\n        let list = input\n            .parse_comma_separated(|input| {\n                let s = input.parse_entirely(|input| Source::parse(context, input));\n                while input.next().is_ok() {}\n                Ok(s.ok())\n            })?\n            .into_iter()\n            .filter_map(|s| s)\n            .collect::<Vec<Source>>();\n        if list.is_empty() {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        } else {\n            Ok(SourceList(list))\n        }\n    }\n}\n\n/// Keywords for the font-face src descriptor's format() function.\n/// ('None' and 'Unknown' are for internal use in gfx, not exposed to CSS.)\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum FontFaceSourceFormatKeyword {\n    #[css(skip)]\n    None,\n    Collection,\n    EmbeddedOpentype,\n    Opentype,\n    Svg,\n    Truetype,\n    Woff,\n    Woff2,\n    #[css(skip)]\n    Unknown,\n}\n\n/// Flags for the @font-face tech() function, indicating font technologies\n/// required by the resource.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct FontFaceSourceTechFlags(u16);\nbitflags! {\n    impl FontFaceSourceTechFlags: u16 {\n        /// Font requires OpenType feature support.\n        const FEATURES_OPENTYPE = 1 << 0;\n        /// Font requires Apple Advanced Typography support.\n        const FEATURES_AAT = 1 << 1;\n        /// Font requires Graphite shaping support.\n        const FEATURES_GRAPHITE = 1 << 2;\n        /// Font requires COLRv0 rendering support (simple list of colored layers).\n        const COLOR_COLRV0 = 1 << 3;\n        /// Font requires COLRv1 rendering support (graph of paint operations).\n        const COLOR_COLRV1 = 1 << 4;\n        /// Font requires SVG glyph rendering support.\n        const COLOR_SVG = 1 << 5;\n        /// Font has bitmap glyphs in 'sbix' format.\n        const COLOR_SBIX = 1 << 6;\n        /// Font has bitmap glyphs in 'CBDT' format.\n        const COLOR_CBDT = 1 << 7;\n        /// Font requires OpenType Variations support.\n        const VARIATIONS = 1 << 8;\n        /// Font requires CPAL palette selection support.\n        const PALETTES = 1 << 9;\n        /// Font requires support for incremental downloading.\n        const INCREMENTAL = 1 << 10;\n    }\n}\n\nimpl FontFaceSourceTechFlags {\n    /// Parse a single font-technology keyword and return its flag.\n    pub fn parse_one<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"features-opentype\" => Self::FEATURES_OPENTYPE,\n            \"features-aat\" => Self::FEATURES_AAT,\n            \"features-graphite\" => Self::FEATURES_GRAPHITE,\n            \"color-colrv0\" => Self::COLOR_COLRV0,\n            \"color-colrv1\" => Self::COLOR_COLRV1,\n            \"color-svg\" => Self::COLOR_SVG,\n            \"color-sbix\" => Self::COLOR_SBIX,\n            \"color-cbdt\" => Self::COLOR_CBDT,\n            \"variations\" => Self::VARIATIONS,\n            \"palettes\" => Self::PALETTES,\n            \"incremental\" => Self::INCREMENTAL,\n        })\n    }\n}\n\nimpl Parse for FontFaceSourceTechFlags {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        // We don't actually care about the return value of parse_comma_separated,\n        // because we insert the flags into result as we go.\n        let mut result = Self::empty();\n        input.parse_comma_separated(|input| {\n            let flag = Self::parse_one(input)?;\n            result.insert(flag);\n            Ok(())\n        })?;\n        if !result.is_empty() {\n            Ok(result)\n        } else {\n            Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        }\n    }\n}\n\n#[allow(unused_assignments)]\nimpl ToCss for FontFaceSourceTechFlags {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        let mut first = true;\n\n        macro_rules! write_if_flag {\n            ($s:expr => $f:ident) => {\n                if self.contains(Self::$f) {\n                    if first {\n                        first = false;\n                    } else {\n                        dest.write_str(\", \")?;\n                    }\n                    dest.write_str($s)?;\n                }\n            };\n        }\n\n        write_if_flag!(\"features-opentype\" => FEATURES_OPENTYPE);\n        write_if_flag!(\"features-aat\" => FEATURES_AAT);\n        write_if_flag!(\"features-graphite\" => FEATURES_GRAPHITE);\n        write_if_flag!(\"color-colrv0\" => COLOR_COLRV0);\n        write_if_flag!(\"color-colrv1\" => COLOR_COLRV1);\n        write_if_flag!(\"color-svg\" => COLOR_SVG);\n        write_if_flag!(\"color-sbix\" => COLOR_SBIX);\n        write_if_flag!(\"color-cbdt\" => COLOR_CBDT);\n        write_if_flag!(\"variations\" => VARIATIONS);\n        write_if_flag!(\"palettes\" => PALETTES);\n        write_if_flag!(\"incremental\" => INCREMENTAL);\n\n        Ok(())\n    }\n}\n\n/// <https://drafts.csswg.org/css-fonts/#font-face-rule>\n#[derive(Clone, Debug, ToShmem, PartialEq)]\npub struct FontFaceRule {\n    /// The descriptors of the @font-face rule.\n    pub descriptors: Descriptors,\n    /// The parser location of the rule.\n    pub source_location: SourceLocation,\n}\n\nimpl FontFaceRule {\n    /// Returns an empty rule.\n    pub fn empty(source_location: SourceLocation) -> Self {\n        Self {\n            descriptors: Default::default(),\n            source_location,\n        }\n    }\n}\n\n/// A POD representation for Gecko. All pointers here are non-owned and as such\n/// can't outlive the rule they came from, but we can't enforce that via C++.\n///\n/// All the strings are of course utf8.\n#[cfg(feature = \"gecko\")]\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum FontFaceSourceListComponent {\n    Url(*const crate::gecko::url::CssUrl),\n    Local(*mut crate::gecko_bindings::structs::nsAtom),\n    FormatHintKeyword(FontFaceSourceFormatKeyword),\n    FormatHintString {\n        length: usize,\n        utf8_bytes: *const u8,\n    },\n    TechFlags(FontFaceSourceTechFlags),\n}\n\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum FontFaceSourceFormat {\n    Keyword(FontFaceSourceFormatKeyword),\n    String(String),\n}\n\n/// A `UrlSource` represents a font-face source that has been specified with a\n/// `url()` function.\n///\n/// <https://drafts.csswg.org/css-fonts/#src-desc>\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub struct UrlSource {\n    /// The specified url.\n    pub url: SpecifiedUrl,\n    /// The format hint specified with the `format()` function, if present.\n    pub format_hint: Option<FontFaceSourceFormat>,\n    /// The font technology flags specified with the `tech()` function, if any.\n    pub tech_flags: FontFaceSourceTechFlags,\n}\n\nimpl ToCss for UrlSource {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.url.to_css(dest)?;\n        if let Some(hint) = &self.format_hint {\n            dest.write_str(\" format(\")?;\n            hint.to_css(dest)?;\n            dest.write_char(')')?;\n        }\n        if !self.tech_flags.is_empty() {\n            dest.write_str(\" tech(\")?;\n            self.tech_flags.to_css(dest)?;\n            dest.write_char(')')?;\n        }\n        Ok(())\n    }\n}\n\n/// A font-display value for a @font-face rule.\n/// The font-display descriptor determines how a font face is displayed based\n/// on whether and when it is downloaded and ready to use.\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToComputedValue, ToCss, ToShmem,\n)]\n#[repr(u8)]\npub enum FontDisplay {\n    Auto,\n    Block,\n    Swap,\n    Fallback,\n    Optional,\n}\n\nmacro_rules! impl_range {\n    ($range:ident, $component:ident) => {\n        impl Parse for $range {\n            fn parse<'i, 't>(\n                context: &ParserContext,\n                input: &mut Parser<'i, 't>,\n            ) -> Result<Self, ParseError<'i>> {\n                let first = $component::parse(context, input)?;\n                let second = input\n                    .try_parse(|input| $component::parse(context, input))\n                    .unwrap_or_else(|_| first.clone());\n                Ok($range(first, second))\n            }\n        }\n        impl ToCss for $range {\n            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n            where\n                W: fmt::Write,\n            {\n                self.0.to_css(dest)?;\n                if self.0 != self.1 {\n                    dest.write_char(' ')?;\n                    self.1.to_css(dest)?;\n                }\n                Ok(())\n            }\n        }\n    };\n}\n\n/// The font-weight descriptor:\n///\n/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-weight\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct FontWeightRange(pub AbsoluteFontWeight, pub AbsoluteFontWeight);\nimpl_range!(FontWeightRange, AbsoluteFontWeight);\n\n/// The computed representation of the above so Gecko can read them easily.\n///\n/// This one is needed because cbindgen doesn't know how to generate\n/// specified::Number.\n#[repr(C)]\n#[allow(missing_docs)]\npub struct ComputedFontWeightRange(f32, f32);\n\n#[inline]\nfn sort_range<T: PartialOrd>(a: T, b: T) -> (T, T) {\n    if a > b {\n        (b, a)\n    } else {\n        (a, b)\n    }\n}\n\nimpl FontWeightRange {\n    /// Returns a computed font-stretch range.\n    pub fn compute(&self) -> ComputedFontWeightRange {\n        let (min, max) = sort_range(self.0.compute().value(), self.1.compute().value());\n        ComputedFontWeightRange(min, max)\n    }\n}\n\n/// The font-stretch descriptor:\n///\n/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-stretch\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct FontStretchRange(pub SpecifiedFontStretch, pub SpecifiedFontStretch);\nimpl_range!(FontStretchRange, SpecifiedFontStretch);\n\n/// The computed representation of the above, so that Gecko can read them\n/// easily.\n#[repr(C)]\n#[allow(missing_docs)]\npub struct ComputedFontStretchRange(FontStretch, FontStretch);\n\nimpl FontStretchRange {\n    /// Returns a computed font-stretch range.\n    pub fn compute(&self) -> ComputedFontStretchRange {\n        fn compute_stretch(s: &SpecifiedFontStretch) -> FontStretch {\n            match *s {\n                SpecifiedFontStretch::Keyword(ref kw) => kw.compute(),\n                SpecifiedFontStretch::Stretch(ref p) => FontStretch::from_percentage(p.0.get()),\n                SpecifiedFontStretch::System(..) => unreachable!(),\n            }\n        }\n\n        let (min, max) = sort_range(compute_stretch(&self.0), compute_stretch(&self.1));\n        ComputedFontStretchRange(min, max)\n    }\n}\n\n/// The font-style descriptor:\n///\n/// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-style\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\n#[allow(missing_docs)]\npub enum FontStyle {\n    Italic,\n    Oblique(Angle, Angle),\n}\n\n/// The computed representation of the above, with angles in degrees, so that\n/// Gecko can read them easily.\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum ComputedFontStyleDescriptor {\n    Italic,\n    Oblique(f32, f32),\n}\n\nimpl Parse for FontStyle {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // We parse 'normal' explicitly here to distinguish it from 'oblique 0deg',\n        // because we must not accept a following angle.\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(FontStyle::Oblique(Angle::zero(), Angle::zero()));\n        }\n\n        let style = SpecifiedFontStyle::parse(context, input)?;\n        Ok(match style {\n            GenericFontStyle::Italic => FontStyle::Italic,\n            GenericFontStyle::Oblique(angle) => {\n                let second_angle = input\n                    .try_parse(|input| SpecifiedFontStyle::parse_angle(context, input))\n                    .unwrap_or_else(|_| angle.clone());\n\n                FontStyle::Oblique(angle, second_angle)\n            },\n        })\n    }\n}\n\nimpl ToCss for FontStyle {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *self {\n            FontStyle::Italic => dest.write_str(\"italic\"),\n            FontStyle::Oblique(ref first, ref second) => {\n                // Not first.is_zero() because we don't want to serialize\n                // `oblique calc(0deg)` as `normal`.\n                if *first == Angle::zero() && first == second {\n                    return dest.write_str(\"normal\");\n                }\n                dest.write_str(\"oblique\")?;\n                if *first != SpecifiedFontStyle::default_angle() || first != second {\n                    dest.write_char(' ')?;\n                    first.to_css(dest)?;\n                }\n                if first != second {\n                    dest.write_char(' ')?;\n                    second.to_css(dest)?;\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\nimpl FontStyle {\n    /// Returns a computed font-style descriptor.\n    pub fn compute(&self) -> ComputedFontStyleDescriptor {\n        match *self {\n            FontStyle::Italic => ComputedFontStyleDescriptor::Italic,\n            FontStyle::Oblique(ref first, ref second) => {\n                let (min, max) = sort_range(\n                    SpecifiedFontStyle::compute_angle_degrees(first),\n                    SpecifiedFontStyle::compute_angle_degrees(second),\n                );\n                ComputedFontStyleDescriptor::Oblique(min, max)\n            },\n        }\n    }\n}\n\n/// Parse the block inside a `@font-face` rule.\n///\n/// Note that the prelude parsing code lives in the `stylesheets` module.\npub fn parse_font_face_block(\n    context: &ParserContext,\n    input: &mut Parser,\n    source_location: SourceLocation,\n) -> FontFaceRule {\n    let mut rule = FontFaceRule::empty(source_location);\n    {\n        let mut parser = DescriptorParser {\n            context,\n            descriptors: &mut rule.descriptors,\n        };\n        let mut iter = RuleBodyParser::new(input, &mut parser);\n        while let Some(declaration) = iter.next() {\n            if let Err((error, slice)) = declaration {\n                let location = error.location;\n                let error = ContextualParseError::UnsupportedFontFaceDescriptor(slice, error);\n                context.log_css_error(location, error)\n            }\n        }\n    }\n    rule\n}\n\n\nimpl Parse for Source {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Source, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_function_matching(\"local\"))\n            .is_ok()\n        {\n            return input\n                .parse_nested_block(|input| FamilyName::parse(context, input))\n                .map(Source::Local);\n        }\n\n        let url = SpecifiedUrl::parse(context, input)?;\n\n        // Parsing optional format()\n        let format_hint = if input\n            .try_parse(|input| input.expect_function_matching(\"format\"))\n            .is_ok()\n        {\n            input.parse_nested_block(|input| {\n                if let Ok(kw) = input.try_parse(FontFaceSourceFormatKeyword::parse) {\n                    Ok(Some(FontFaceSourceFormat::Keyword(kw)))\n                } else {\n                    let s = input.expect_string()?.as_ref().to_owned();\n                    Ok(Some(FontFaceSourceFormat::String(s)))\n                }\n            })?\n        } else {\n            None\n        };\n\n        // Parse optional tech()\n        let tech_flags = if static_prefs::pref!(\"layout.css.font-tech.enabled\")\n            && input\n                .try_parse(|input| input.expect_function_matching(\"tech\"))\n                .is_ok()\n        {\n            input.parse_nested_block(|input| FontFaceSourceTechFlags::parse(context, input))?\n        } else {\n            FontFaceSourceTechFlags::empty()\n        };\n\n        Ok(Source::Url(UrlSource {\n            url,\n            format_hint,\n            tech_flags,\n        }))\n    }\n}\n\nimpl ToCssWithGuard for FontFaceRule {\n    // Serialization of FontFaceRule is not specced.\n    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@font-face { \")?;\n        self.descriptors.to_css(&mut CssWriter::new(dest))?;\n        dest.write_char('}')\n    }\n}\n"
  },
  {
    "path": "style/font_metrics.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Access to font metrics from the style system.\n\n#![deny(missing_docs)]\n\nuse crate::values::computed::Length;\n\n/// Represents the font metrics that style needs from a font to compute the\n/// value of certain CSS units like `ex`.\n#[derive(Clone, Debug, PartialEq)]\npub struct FontMetrics {\n    /// The x-height of the font.\n    pub x_height: Option<Length>,\n    /// The zero advance. This is usually writing mode dependent\n    pub zero_advance_measure: Option<Length>,\n    /// The cap-height of the font.\n    pub cap_height: Option<Length>,\n    /// The ideographic-width of the font.\n    pub ic_width: Option<Length>,\n    /// The ascent of the font (a value is always available for this).\n    pub ascent: Length,\n    /// Script scale down factor for math-depth 1.\n    /// https://w3c.github.io/mathml-core/#dfn-scriptpercentscaledown\n    pub script_percent_scale_down: Option<f32>,\n    /// Script scale down factor for math-depth 2.\n    /// https://w3c.github.io/mathml-core/#dfn-scriptscriptpercentscaledown\n    pub script_script_percent_scale_down: Option<f32>,\n}\n\nimpl Default for FontMetrics {\n    fn default() -> Self {\n        FontMetrics {\n            x_height: None,\n            zero_advance_measure: None,\n            cap_height: None,\n            ic_width: None,\n            ascent: Length::new(0.0),\n            script_percent_scale_down: None,\n            script_script_percent_scale_down: None,\n        }\n    }\n}\n\nimpl FontMetrics {\n    /// Returns the x-height, computing a fallback value if not present\n    pub fn x_height_or_default(&self, reference_font_size: Length) -> Length {\n        // https://drafts.csswg.org/css-values/#ex\n        //\n        //     In the cases where it is impossible or impractical to\n        //     determine the x-height, a value of 0.5em must be\n        //     assumed.\n        //\n        // (But note we use 0.5em of the used, not computed\n        // font-size)\n        self.x_height.unwrap_or_else(|| reference_font_size * 0.5)\n    }\n\n    /// Returns the zero advance measure, computing a fallback value if not present\n    pub fn zero_advance_measure_or_default(\n        &self,\n        reference_font_size: Length,\n        upright: bool,\n    ) -> Length {\n        // https://drafts.csswg.org/css-values/#ch\n        //\n        //     In the cases where it is impossible or impractical to\n        //     determine the measure of the “0” glyph, it must be\n        //     assumed to be 0.5em wide by 1em tall. Thus, the ch\n        //     unit falls back to 0.5em in the general case, and to\n        //     1em when it would be typeset upright (i.e.\n        //     writing-mode is vertical-rl or vertical-lr and\n        //     text-orientation is upright).\n        //\n        // Same caveat about computed vs. used font-size applies\n        // above.\n        self.zero_advance_measure.unwrap_or_else(|| {\n            if upright {\n                reference_font_size\n            } else {\n                reference_font_size * 0.5\n            }\n        })\n    }\n\n    /// Returns the cap-height, computing a fallback value if not present\n    pub fn cap_height_or_default(&self) -> Length {\n        // https://drafts.csswg.org/css-values/#cap\n        //\n        //     In the cases where it is impossible or impractical to\n        //     determine the cap-height, the font’s ascent must be\n        //     used.\n        //\n        self.cap_height.unwrap_or_else(|| self.ascent)\n    }\n\n    /// Returns the ideographic advance measure, computing a fallback value if not present\n    pub fn ic_width_or_default(&self, reference_font_size: Length) -> Length {\n        // https://drafts.csswg.org/css-values/#ic\n        //\n        //     In the cases where it is impossible or impractical to\n        //     determine the ideographic advance measure, it must be\n        //     assumed to be 1em.\n        //\n        // Same caveat about computed vs. used as for other\n        // metric-dependent units.\n        self.ic_width.unwrap_or_else(|| reference_font_size)\n    }\n}\n\n/// Type of font metrics to retrieve.\n#[derive(Clone, Debug, PartialEq)]\npub enum FontMetricsOrientation {\n    /// Get metrics for horizontal or vertical according to the Context's\n    /// writing mode, using horizontal metrics for vertical/mixed\n    MatchContextPreferHorizontal,\n    /// Get metrics for horizontal or vertical according to the Context's\n    /// writing mode, using vertical metrics for vertical/mixed\n    MatchContextPreferVertical,\n    /// Force getting horizontal metrics.\n    Horizontal,\n}\n"
  },
  {
    "path": "style/gecko/anon_boxes.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# This file contains the list of anonymous boxes and some flags, much like\n# longhands.toml and pseudo_elements.toml.\n#\n# Each anon box has its own section, with the following keys:\n#   inherits - whether the anon box inherits from its parent or not. Defaults to true.\n#   wrapper - whether the anon box is a wrapper around other frames during\n#             frame tree fix-up (e.g. table anonymous boxes, ruby anonymous boxes, anonymous\n#             flex item blocks, etc). Defaults to false.\n#\n# When adding an anon box, you also need to add the relevant atom to\n# StaticAtoms.py, with the camel-case name of the anon box, in the same order as\n# this file.\n\n[-moz-oof-placeholder]\ninherits = false\n# Framesets\n[-moz-hframeset-border]\ninherits = false\n[-moz-vframeset-border]\ninherits = false\n[-moz-frameset-blank]\ninherits = false\n\n# Table\n[-moz-table-column-group]\ninherits = false\n[-moz-table-column]\ninherits = false\n\n[-moz-page]\ninherits = false\n[-moz-page-break]\ninherits = false\n[-moz-page-content]\ninherits = false\n[-moz-printed-sheet]\ninherits = false\n\n# Applies to blocks that wrap contiguous runs of \"column-span: all\" elements in\n# multi-column subtrees, or the wrappers themselves, all the way up to the\n# column set wrappers.\n[-moz-column-span-wrapper]\ninherits = false\n\n# ::-moz-text, ::-moz-oof-placeholder, and ::-moz-first-letter-continuation are\n# non-elements which no rule will match.\n[-moz-text]\n# nsFirstLetterFrames for content outside the ::first-letter.\n[-moz-first-letter-continuation]\n\n[-moz-block-inside-inline-wrapper]\n[-moz-mathml-anonymous-block]\nwrapper = true\n[-moz-line-frame]\n\n[-moz-cell-content]\n[-moz-fieldset-content]\n[-moz-html-canvas-content]\n\n[-moz-inline-table]\nwrapper = true\n[-moz-table]\nwrapper = true\n[-moz-table-cell]\nwrapper = true\n[-moz-table-wrapper]\n[-moz-table-row-group]\nwrapper = true\n[-moz-table-row]\nwrapper = true\n\n[-moz-canvas]\ninherits = false\n[-moz-page-sequence]\n[-moz-scrolled-content]\n\n# A column set is a set of columns inside of ColumnSetWrapperFrame, which\n# applies to nsColumnSetFrame. It doesn't contain any column-span elements.\n[-moz-column-set]\n[-moz-column-content]\n\n# The root (viewport) frame\n[-moz-viewport]\ninherits = false\n# The root scrollframe.\n[-moz-viewport-scroll]\ninherits = false\n\n# Inside a flex/grid/-moz-box container, a contiguous run of text gets wrapped\n# in an anonymous block, which is then treated as a flex item.\n[-moz-anonymous-item]\nwrapper = true\n\n[-moz-block-ruby-content]\n[-moz-ruby]\nwrapper = true\n[-moz-ruby-base]\nwrapper = true\n[-moz-ruby-base-container]\nwrapper = true\n[-moz-ruby-text]\nwrapper = true\n[-moz-ruby-text-container]\nwrapper = true\n\n[-moz-svg-marker-anon-child]\n[-moz-svg-outer-svg-anon-child]\n[-moz-svg-foreign-content]\n[-moz-svg-text]\n"
  },
  {
    "path": "style/gecko/arc_types.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! This file lists all arc FFI types and defines corresponding addref and release functions. This\n//! list loosely corresponds to ServoLockedArcTypeList.inc file in Gecko.\n\n#![allow(non_snake_case, missing_docs)]\n\nuse crate::gecko::url::CssUrlData;\nuse crate::media_queries::MediaList;\nuse crate::properties::animated_properties::AnimationValue;\nuse crate::properties::{ComputedValues, PropertyDeclarationBlock};\nuse crate::shared_lock::Locked;\nuse crate::stylesheets::keyframes_rule::Keyframe;\nuse crate::stylesheets::{\n    AppearanceBaseRule, ContainerRule, CssRules, CustomMediaRule, DocumentRule,\n    FontFeatureValuesRule, FontPaletteValuesRule, LayerBlockRule, LayerStatementRule, MarginRule,\n    MediaRule, NamespaceRule, PropertyRule, ScopeRule, StartingStyleRule, StylesheetContents,\n    SupportsRule, ViewTransitionRule,\n};\npub use crate::stylesheets::{\n    LockedCounterStyleRule, LockedFontFaceRule, LockedImportRule, LockedKeyframesRule,\n    LockedNestedDeclarationsRule, LockedPageRule, LockedPositionTryRule, LockedStyleRule,\n};\nuse servo_arc::Arc;\n\nmacro_rules! impl_simple_arc_ffi {\n    ($ty:ty, $addref:ident, $release:ident) => {\n        #[no_mangle]\n        pub unsafe extern \"C\" fn $addref(obj: *const $ty) {\n            std::mem::forget(Arc::from_raw_addrefed(obj));\n        }\n\n        #[no_mangle]\n        pub unsafe extern \"C\" fn $release(obj: *const $ty) {\n            let _ = Arc::from_raw(obj);\n        }\n    };\n}\n\nmacro_rules! impl_locked_arc_ffi {\n    ($servo_type:ty, $alias:ident, $addref:ident, $release:ident) => {\n        /// A simple alias for a locked type.\n        pub type $alias = Locked<$servo_type>;\n        impl_simple_arc_ffi!($alias, $addref, $release);\n    };\n}\n\nimpl_locked_arc_ffi!(\n    CssRules,\n    LockedCssRules,\n    Servo_CssRules_AddRef,\n    Servo_CssRules_Release\n);\nimpl_locked_arc_ffi!(\n    PropertyDeclarationBlock,\n    LockedDeclarationBlock,\n    Servo_DeclarationBlock_AddRef,\n    Servo_DeclarationBlock_Release\n);\nimpl_simple_arc_ffi!(\n    LockedStyleRule,\n    Servo_StyleRule_AddRef,\n    Servo_StyleRule_Release\n);\nimpl_simple_arc_ffi!(\n    LockedImportRule,\n    Servo_ImportRule_AddRef,\n    Servo_ImportRule_Release\n);\nimpl_locked_arc_ffi!(\n    Keyframe,\n    LockedKeyframe,\n    Servo_Keyframe_AddRef,\n    Servo_Keyframe_Release\n);\nimpl_simple_arc_ffi!(\n    LockedKeyframesRule,\n    Servo_KeyframesRule_AddRef,\n    Servo_KeyframesRule_Release\n);\nimpl_simple_arc_ffi!(\n    LayerBlockRule,\n    Servo_LayerBlockRule_AddRef,\n    Servo_LayerBlockRule_Release\n);\nimpl_simple_arc_ffi!(\n    LayerStatementRule,\n    Servo_LayerStatementRule_AddRef,\n    Servo_LayerStatementRule_Release\n);\nimpl_locked_arc_ffi!(\n    MediaList,\n    LockedMediaList,\n    Servo_MediaList_AddRef,\n    Servo_MediaList_Release\n);\nimpl_simple_arc_ffi!(MediaRule, Servo_MediaRule_AddRef, Servo_MediaRule_Release);\nimpl_simple_arc_ffi!(\n    CustomMediaRule,\n    Servo_CustomMediaRule_AddRef,\n    Servo_CustomMediaRule_Release\n);\nimpl_simple_arc_ffi!(\n    NamespaceRule,\n    Servo_NamespaceRule_AddRef,\n    Servo_NamespaceRule_Release\n);\nimpl_simple_arc_ffi!(\n    MarginRule,\n    Servo_MarginRule_AddRef,\n    Servo_MarginRule_Release\n);\nimpl_simple_arc_ffi!(\n    LockedPageRule,\n    Servo_PageRule_AddRef,\n    Servo_PageRule_Release\n);\nimpl_simple_arc_ffi!(\n    PropertyRule,\n    Servo_PropertyRule_AddRef,\n    Servo_PropertyRule_Release\n);\nimpl_simple_arc_ffi!(\n    SupportsRule,\n    Servo_SupportsRule_AddRef,\n    Servo_SupportsRule_Release\n);\nimpl_simple_arc_ffi!(\n    ContainerRule,\n    Servo_ContainerRule_AddRef,\n    Servo_ContainerRule_Release\n);\nimpl_simple_arc_ffi!(\n    DocumentRule,\n    Servo_DocumentRule_AddRef,\n    Servo_DocumentRule_Release\n);\nimpl_simple_arc_ffi!(\n    FontFeatureValuesRule,\n    Servo_FontFeatureValuesRule_AddRef,\n    Servo_FontFeatureValuesRule_Release\n);\nimpl_simple_arc_ffi!(\n    FontPaletteValuesRule,\n    Servo_FontPaletteValuesRule_AddRef,\n    Servo_FontPaletteValuesRule_Release\n);\nimpl_simple_arc_ffi!(\n    LockedFontFaceRule,\n    Servo_FontFaceRule_AddRef,\n    Servo_FontFaceRule_Release\n);\nimpl_simple_arc_ffi!(\n    LockedCounterStyleRule,\n    Servo_CounterStyleRule_AddRef,\n    Servo_CounterStyleRule_Release\n);\n\nimpl_simple_arc_ffi!(\n    StylesheetContents,\n    Servo_StyleSheetContents_AddRef,\n    Servo_StyleSheetContents_Release\n);\nimpl_simple_arc_ffi!(\n    CssUrlData,\n    Servo_CssUrlData_AddRef,\n    Servo_CssUrlData_Release\n);\nimpl_simple_arc_ffi!(\n    ComputedValues,\n    Servo_ComputedStyle_AddRef,\n    Servo_ComputedStyle_Release\n);\nimpl_simple_arc_ffi!(\n    AnimationValue,\n    Servo_AnimationValue_AddRef,\n    Servo_AnimationValue_Release\n);\nimpl_simple_arc_ffi!(ScopeRule, Servo_ScopeRule_AddRef, Servo_ScopeRule_Release);\nimpl_simple_arc_ffi!(\n    StartingStyleRule,\n    Servo_StartingStyleRule_AddRef,\n    Servo_StartingStyleRule_Release\n);\nimpl_simple_arc_ffi!(\n    AppearanceBaseRule,\n    Servo_AppearanceBaseRule_AddRef,\n    Servo_AppearanceBaseRule_Release\n);\n\nimpl_simple_arc_ffi!(\n    LockedPositionTryRule,\n    Servo_PositionTryRule_AddRef,\n    Servo_PositionTryRule_Release\n);\nimpl_simple_arc_ffi!(\n    LockedNestedDeclarationsRule,\n    Servo_NestedDeclarationsRule_AddRef,\n    Servo_NestedDeclarationsRule_Release\n);\nimpl_simple_arc_ffi!(\n    ViewTransitionRule,\n    Servo_ViewTransitionRule_AddRef,\n    Servo_ViewTransitionRule_Release\n);\n"
  },
  {
    "path": "style/gecko/conversions.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! This module contains conversion helpers between Servo and Gecko types\n//! Ideally, it would be in geckolib itself, but coherence\n//! forces us to keep the traits and implementations here\n//!\n//! FIXME(emilio): This file should generally just die.\n\n#![allow(unsafe_code)]\n\nuse crate::gecko_bindings::structs::{nsresult, Matrix4x4Components};\nuse crate::stylesheets::RulesMutateError;\nuse crate::values::computed::transform::Matrix3D;\n\nimpl From<RulesMutateError> for nsresult {\n    fn from(other: RulesMutateError) -> Self {\n        match other {\n            RulesMutateError::Syntax => nsresult::NS_ERROR_DOM_SYNTAX_ERR,\n            RulesMutateError::IndexSize => nsresult::NS_ERROR_DOM_INDEX_SIZE_ERR,\n            RulesMutateError::HierarchyRequest => nsresult::NS_ERROR_DOM_HIERARCHY_REQUEST_ERR,\n            RulesMutateError::InvalidState => nsresult::NS_ERROR_DOM_INVALID_STATE_ERR,\n        }\n    }\n}\n\nimpl<'a> From<&'a Matrix4x4Components> for Matrix3D {\n    fn from(m: &'a Matrix4x4Components) -> Matrix3D {\n        Matrix3D {\n            m11: m[0],\n            m12: m[1],\n            m13: m[2],\n            m14: m[3],\n            m21: m[4],\n            m22: m[5],\n            m23: m[6],\n            m24: m[7],\n            m31: m[8],\n            m32: m[9],\n            m33: m[10],\n            m34: m[11],\n            m41: m[12],\n            m42: m[13],\n            m43: m[14],\n            m44: m[15],\n        }\n    }\n}\n\nimpl From<Matrix3D> for Matrix4x4Components {\n    fn from(matrix: Matrix3D) -> Self {\n        [\n            matrix.m11, matrix.m12, matrix.m13, matrix.m14, matrix.m21, matrix.m22, matrix.m23,\n            matrix.m24, matrix.m31, matrix.m32, matrix.m33, matrix.m34, matrix.m41, matrix.m42,\n            matrix.m43, matrix.m44,\n        ]\n    }\n}\n"
  },
  {
    "path": "style/gecko/data.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Data needed to style a Gecko document.\n\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs::{\n    self, ServoStyleSetSizes, StyleSheet as DomStyleSheet, StyleSheetInfo,\n};\nuse crate::invalidation::stylesheets::StylesheetInvalidationSet;\nuse crate::media_queries::MediaList;\nuse crate::properties::ComputedValues;\nuse crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};\nuse crate::stylesheets::scope_rule::ImplicitScopeRoot;\nuse crate::stylesheets::{StylesheetContents, StylesheetInDocument};\nuse crate::stylist::Stylist;\nuse atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};\nuse malloc_size_of::MallocSizeOfOps;\nuse selectors::Element;\nuse servo_arc::Arc;\nuse std::fmt;\n\nuse super::wrapper::GeckoElement;\n\n/// Little wrapper to a Gecko style sheet.\n#[derive(Eq, PartialEq)]\npub struct GeckoStyleSheet(*const DomStyleSheet);\n\n// NOTE(emilio): These are kind of a lie. We allow to make these Send + Sync so that other data\n// structures can also be Send and Sync, but Gecko's stylesheets are main-thread-reference-counted.\n//\n// We assert that we reference-count in the right thread (in the Addref/Release implementations).\n// Sending these to a different thread can't really happen (it could theoretically really happen if\n// we allowed @import rules inside a nested style rule, but that can't happen per spec and would be\n// a parser bug, caught by the asserts).\nunsafe impl Send for GeckoStyleSheet {}\nunsafe impl Sync for GeckoStyleSheet {}\n\nimpl fmt::Debug for GeckoStyleSheet {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        let contents = self.raw_contents();\n        formatter\n            .debug_struct(\"GeckoStyleSheet\")\n            .field(\"origin\", &contents.origin)\n            .field(\"url_data\", &contents.url_data)\n            .finish()\n    }\n}\n\nimpl GeckoStyleSheet {\n    /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer.\n    #[inline]\n    pub unsafe fn new(s: *const DomStyleSheet) -> Self {\n        debug_assert!(!s.is_null());\n        bindings::Gecko_StyleSheet_AddRef(s);\n        Self::from_addrefed(s)\n    }\n\n    /// Create a `GeckoStyleSheet` from a raw `DomStyleSheet` pointer that\n    /// already holds a strong reference.\n    #[inline]\n    pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self {\n        assert!(!s.is_null());\n        GeckoStyleSheet(s)\n    }\n\n    /// HACK(emilio): This is so that we can avoid crashing release due to\n    /// bug 1719963 and can hopefully get a useful report from fuzzers.\n    #[inline]\n    pub fn hack_is_null(&self) -> bool {\n        self.0.is_null()\n    }\n\n    /// Get the raw `StyleSheet` that we're wrapping.\n    pub fn raw(&self) -> &DomStyleSheet {\n        unsafe { &*self.0 }\n    }\n\n    fn inner(&self) -> &StyleSheetInfo {\n        unsafe { &*(self.raw().mInner as *const StyleSheetInfo) }\n    }\n\n    fn raw_contents(&self) -> &StylesheetContents {\n        debug_assert!(!self.inner().mContents.mRawPtr.is_null());\n        unsafe { &*self.inner().mContents.mRawPtr }\n    }\n}\n\nimpl Drop for GeckoStyleSheet {\n    fn drop(&mut self) {\n        unsafe { bindings::Gecko_StyleSheet_Release(self.0) };\n    }\n}\n\nimpl Clone for GeckoStyleSheet {\n    fn clone(&self) -> Self {\n        unsafe { bindings::Gecko_StyleSheet_AddRef(self.0) };\n        GeckoStyleSheet(self.0)\n    }\n}\n\nimpl StylesheetInDocument for GeckoStyleSheet {\n    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {\n        use crate::gecko_bindings::structs::mozilla::dom::MediaList as DomMediaList;\n        unsafe {\n            let dom_media_list = self.raw().mMedia.mRawPtr as *const DomMediaList;\n            if dom_media_list.is_null() {\n                return None;\n            }\n            let list = &*(*dom_media_list).mRawList.mRawPtr;\n            Some(list.read_with(guard))\n        }\n    }\n\n    // All the stylesheets Servo knows about are enabled, because that state is\n    // handled externally by Gecko.\n    #[inline]\n    fn enabled(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn contents<'a>(&'a self, _: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {\n        self.raw_contents()\n    }\n\n    fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {\n        unsafe {\n            let result = bindings::Gecko_StyleSheet_ImplicitScopeRoot(self.0);\n            if result.mRoot.is_null() {\n                return if result.mConstructed {\n                    Some(ImplicitScopeRoot::Constructed)\n                } else {\n                    // Could be genuinely not attached, like user stylesheet.\n                    None\n                };\n            }\n\n            let root = GeckoElement(result.mRoot.as_ref().unwrap()).opaque();\n            Some(if !result.mHost.is_null() {\n                let host = GeckoElement(result.mHost.as_ref().unwrap()).opaque();\n                if host == root {\n                    ImplicitScopeRoot::ShadowHost(root)\n                } else {\n                    ImplicitScopeRoot::InShadowTree(root)\n                }\n            } else {\n                ImplicitScopeRoot::InLightTree(root)\n            })\n        }\n    }\n}\n\n/// The container for data that a Servo-backed Gecko document needs to style\n/// itself.\npub struct PerDocumentStyleDataImpl {\n    /// Rule processor.\n    pub stylist: Stylist,\n\n    /// A cache from element to resolved style.\n    pub undisplayed_style_cache: crate::traversal::UndisplayedStyleCache,\n\n    /// The generation for which our cache is valid.\n    pub undisplayed_style_cache_generation: u64,\n}\n\n/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics\n/// and unexpected races while trying to mutate it.\n#[derive(Deref)]\npub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);\n\nimpl PerDocumentStyleData {\n    /// Create a `PerDocumentStyleData`.\n    pub fn new(document: *const structs::Document) -> Self {\n        let device = Device::new(document);\n        let quirks_mode = device.document().mCompatMode;\n\n        PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {\n            stylist: Stylist::new(device, quirks_mode.into()),\n            undisplayed_style_cache: Default::default(),\n            undisplayed_style_cache_generation: 0,\n        }))\n    }\n\n    /// Get an immutable reference to this style data.\n    pub fn borrow(&self) -> AtomicRef<'_, PerDocumentStyleDataImpl> {\n        self.0.borrow()\n    }\n\n    /// Get an mutable reference to this style data.\n    pub fn borrow_mut(&self) -> AtomicRefMut<'_, PerDocumentStyleDataImpl> {\n        self.0.borrow_mut()\n    }\n}\n\nimpl PerDocumentStyleDataImpl {\n    /// Recreate the style data if the stylesheets have changed.\n    pub fn flush_stylesheets(\n        &mut self,\n        guard: &SharedRwLockReadGuard,\n    ) -> StylesheetInvalidationSet {\n        self.stylist.flush(&StylesheetGuards::same(guard))\n    }\n\n    /// Get the default computed values for this document.\n    pub fn default_computed_values(&self) -> &Arc<ComputedValues> {\n        self.stylist.device().default_computed_values_arc()\n    }\n\n    /// Measure heap usage.\n    pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        self.stylist.add_size_of(ops, sizes);\n    }\n}\n\n/// The gecko-specific AuthorStyles instantiation.\npub type AuthorStyles = crate::author_styles::AuthorStyles<GeckoStyleSheet>;\n"
  },
  {
    "path": "style/gecko/media_features.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko's media feature list and evaluator.\n\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs;\nuse crate::media_queries::MediaType;\nuse crate::parser::ParserContext;\nuse crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};\nuse crate::queries::values::{Orientation, PrefersColorScheme};\nuse crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution};\nuse crate::values::specified::color::ForcedColors;\nuse app_units::Au;\nuse euclid::default::Size2D;\n\nfn device_size(device: &Device) -> Size2D<Au> {\n    let mut width = 0;\n    let mut height = 0;\n    unsafe {\n        bindings::Gecko_MediaFeatures_GetDeviceSize(device.document(), &mut width, &mut height);\n    }\n    Size2D::new(Au(width), Au(height))\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#width\nfn eval_width(context: &Context) -> CSSPixelLength {\n    CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#device-width\nfn eval_device_width(context: &Context) -> CSSPixelLength {\n    CSSPixelLength::new(device_size(context.device()).width.to_f32_px())\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#height\nfn eval_height(context: &Context) -> CSSPixelLength {\n    CSSPixelLength::new(context.device().au_viewport_size().height.to_f32_px())\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#device-height\nfn eval_device_height(context: &Context) -> CSSPixelLength {\n    CSSPixelLength::new(device_size(context.device()).height.to_f32_px())\n}\n\nfn eval_aspect_ratio_for<F>(context: &Context, get_size: F) -> Ratio\nwhere\n    F: FnOnce(&Device) -> Size2D<Au>,\n{\n    let size = get_size(context.device());\n    Ratio::new(size.width.0 as f32, size.height.0 as f32)\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#aspect-ratio\nfn eval_aspect_ratio(context: &Context) -> Ratio {\n    eval_aspect_ratio_for(context, Device::au_viewport_size)\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio\nfn eval_device_aspect_ratio(context: &Context) -> Ratio {\n    eval_aspect_ratio_for(context, device_size)\n}\n\n/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio\nfn eval_device_pixel_ratio(context: &Context) -> f32 {\n    eval_resolution(context).dppx()\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#orientation\nfn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {\n    Orientation::eval(context.device().au_viewport_size(), value)\n}\n\n/// FIXME: There's no spec for `-moz-device-orientation`.\nfn eval_device_orientation(context: &Context, value: Option<Orientation>) -> bool {\n    Orientation::eval(device_size(context.device()), value)\n}\n\nfn document_picture_in_picture_enabled(context: &ParserContext) -> bool {\n    static_prefs::pref!(\"dom.documentpip.enabled\") || context.chrome_rules_enabled()\n}\n\n/// Values for the display-mode media feature.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum DisplayMode {\n    Browser = 0,\n    MinimalUi,\n    Standalone,\n    Fullscreen,\n    #[parse(condition = \"document_picture_in_picture_enabled\")]\n    PictureInPicture,\n}\n\n/// https://w3c.github.io/manifest/#the-display-mode-media-feature\nfn eval_display_mode(context: &Context, query_value: Option<DisplayMode>) -> bool {\n    match query_value {\n        Some(v) => {\n            v == unsafe {\n                bindings::Gecko_MediaFeatures_GetDisplayMode(context.device().document())\n            }\n        },\n        None => true,\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#grid\nfn eval_grid(_: &Context) -> bool {\n    // Gecko doesn't support grid devices (e.g., ttys), so the 'grid' feature\n    // is always 0.\n    false\n}\n\n/// https://compat.spec.whatwg.org/#css-media-queries-webkit-transform-3d\nfn eval_transform_3d(_: &Context) -> bool {\n    true\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum Scan {\n    Progressive,\n    Interlace,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#scan\nfn eval_scan(_: &Context, _: Option<Scan>) -> bool {\n    // Since Gecko doesn't support the 'tv' media type, the 'scan' feature never\n    // matches.\n    false\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#color\nfn eval_color(context: &Context) -> i32 {\n    unsafe { bindings::Gecko_MediaFeatures_GetColorDepth(context.device().document()) }\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#color-index\nfn eval_color_index(_: &Context) -> i32 {\n    // We should return zero if the device does not use a color lookup table.\n    0\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#monochrome\nfn eval_monochrome(context: &Context) -> i32 {\n    // For color devices we should return 0.\n    unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(context.device().document()) }\n}\n\n/// Values for the color-gamut media feature.\n/// This implements PartialOrd so that lower values will correctly match\n/// higher capabilities.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]\n#[repr(u8)]\npub enum ColorGamut {\n    /// The sRGB gamut.\n    Srgb,\n    /// The gamut specified by the Display P3 Color Space.\n    P3,\n    /// The gamut specified by the ITU-R Recommendation BT.2020 Color Space.\n    Rec2020,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#color-gamut\nfn eval_color_gamut(context: &Context, query_value: Option<ColorGamut>) -> bool {\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return false,\n    };\n    let color_gamut =\n        unsafe { bindings::Gecko_MediaFeatures_ColorGamut(context.device().document()) };\n    // Match if our color gamut is at least as wide as the query value\n    query_value <= color_gamut\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#resolution\nfn eval_resolution(context: &Context) -> Resolution {\n    let resolution_dppx =\n        unsafe { bindings::Gecko_MediaFeatures_GetResolution(context.device().document()) };\n    Resolution::from_dppx(resolution_dppx)\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum PrefersReducedMotion {\n    NoPreference,\n    Reduce,\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum PrefersReducedTransparency {\n    NoPreference,\n    Reduce,\n}\n\n/// Values for the dynamic-range and video-dynamic-range media features.\n/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range\n/// This implements PartialOrd so that lower values will correctly match\n/// higher capabilities.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, PartialOrd, ToCss)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum DynamicRange {\n    Standard,\n    High,\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-motion\nfn eval_prefers_reduced_motion(\n    context: &Context,\n    query_value: Option<PrefersReducedMotion>,\n) -> bool {\n    let prefers_reduced =\n        unsafe { bindings::Gecko_MediaFeatures_PrefersReducedMotion(context.device().document()) };\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return prefers_reduced,\n    };\n\n    match query_value {\n        PrefersReducedMotion::NoPreference => !prefers_reduced,\n        PrefersReducedMotion::Reduce => prefers_reduced,\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency\nfn eval_prefers_reduced_transparency(\n    context: &Context,\n    query_value: Option<PrefersReducedTransparency>,\n) -> bool {\n    let prefers_reduced = unsafe {\n        bindings::Gecko_MediaFeatures_PrefersReducedTransparency(context.device().document())\n    };\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return prefers_reduced,\n    };\n\n    match query_value {\n        PrefersReducedTransparency::NoPreference => !prefers_reduced,\n        PrefersReducedTransparency::Reduce => prefers_reduced,\n    }\n}\n\n/// Possible values for prefers-contrast media query.\n/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\npub enum PrefersContrast {\n    /// More contrast is preferred.\n    More,\n    /// Low contrast is preferred.\n    Less,\n    /// Custom (not more, not less).\n    Custom,\n    /// The default value if neither high or low contrast is enabled.\n    NoPreference,\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast\nfn eval_prefers_contrast(context: &Context, query_value: Option<PrefersContrast>) -> bool {\n    let prefers_contrast =\n        unsafe { bindings::Gecko_MediaFeatures_PrefersContrast(context.device().document()) };\n    match query_value {\n        Some(v) => v == prefers_contrast,\n        None => prefers_contrast != PrefersContrast::NoPreference,\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#forced-colors\nfn eval_forced_colors(context: &Context, query_value: Option<ForcedColors>) -> bool {\n    let forced = context.device().forced_colors();\n    match query_value {\n        Some(query_value) => query_value == forced,\n        None => forced != ForcedColors::None,\n    }\n}\n\n/// Possible values for the inverted-colors media query.\n/// https://drafts.csswg.org/mediaqueries-5/#inverted\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum InvertedColors {\n    /// Colors are displayed normally.\n    None,\n    /// All pixels within the displayed area have been inverted.\n    Inverted,\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#inverted\nfn eval_inverted_colors(context: &Context, query_value: Option<InvertedColors>) -> bool {\n    let inverted_colors =\n        unsafe { bindings::Gecko_MediaFeatures_InvertedColors(context.device().document()) };\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return inverted_colors,\n    };\n\n    match query_value {\n        InvertedColors::None => !inverted_colors,\n        InvertedColors::Inverted => inverted_colors,\n    }\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum OverflowBlock {\n    None,\n    Scroll,\n    Paged,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-block\nfn eval_overflow_block(context: &Context, query_value: Option<OverflowBlock>) -> bool {\n    // For the time being, assume that printing (including previews)\n    // is the only time when we paginate, and we are otherwise always\n    // scrolling. This is true at the moment in Firefox, but may need\n    // updating in the future (e.g., ebook readers built with Stylo, a\n    // billboard mode that doesn't support overflow at all).\n    //\n    // If this ever changes, don't forget to change eval_overflow_inline too.\n    let scrolling = context.device().media_type() != MediaType::print();\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return true,\n    };\n\n    match query_value {\n        OverflowBlock::None => false,\n        OverflowBlock::Scroll => scrolling,\n        OverflowBlock::Paged => !scrolling,\n    }\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum OverflowInline {\n    None,\n    Scroll,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#mf-overflow-inline\nfn eval_overflow_inline(context: &Context, query_value: Option<OverflowInline>) -> bool {\n    // See the note in eval_overflow_block.\n    let scrolling = context.device().media_type() != MediaType::print();\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return scrolling,\n    };\n\n    match query_value {\n        OverflowInline::None => !scrolling,\n        OverflowInline::Scroll => scrolling,\n    }\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum Update {\n    None,\n    Slow,\n    Fast,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#update\nfn eval_update(context: &Context, query_value: Option<Update>) -> bool {\n    // This has similar caveats to those described in eval_overflow_block.\n    // For now, we report that print (incl. print media simulation,\n    // which can in fact update but is limited to the developer tools)\n    // is `update: none` and that all other contexts are `update: fast`,\n    // which may not be true for future platforms, like e-ink devices.\n    let can_update = context.device().media_type() != MediaType::print();\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return can_update,\n    };\n\n    match query_value {\n        Update::None => !can_update,\n        Update::Slow => false,\n        Update::Fast => can_update,\n    }\n}\n\nfn do_eval_prefers_color_scheme(\n    context: &Context,\n    use_content: bool,\n    query_value: Option<PrefersColorScheme>,\n) -> bool {\n    let prefers_color_scheme = unsafe {\n        bindings::Gecko_MediaFeatures_PrefersColorScheme(context.device().document(), use_content)\n    };\n    match query_value {\n        Some(v) => prefers_color_scheme == v,\n        None => true,\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme\nfn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {\n    do_eval_prefers_color_scheme(context, /* use_content = */ false, query_value)\n}\n\nfn eval_content_prefers_color_scheme(\n    context: &Context,\n    query_value: Option<PrefersColorScheme>,\n) -> bool {\n    do_eval_prefers_color_scheme(context, /* use_content = */ true, query_value)\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#dynamic-range\nfn eval_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {\n    let dynamic_range =\n        unsafe { bindings::Gecko_MediaFeatures_DynamicRange(context.device().document()) };\n    match query_value {\n        Some(v) => dynamic_range >= v,\n        None => false,\n    }\n}\n/// https://drafts.csswg.org/mediaqueries-5/#video-dynamic-range\nfn eval_video_dynamic_range(context: &Context, query_value: Option<DynamicRange>) -> bool {\n    let dynamic_range =\n        unsafe { bindings::Gecko_MediaFeatures_VideoDynamicRange(context.device().document()) };\n    match query_value {\n        Some(v) => dynamic_range >= v,\n        None => false,\n    }\n}\n\nbitflags! {\n    /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction\n    struct PointerCapabilities: u8 {\n        const COARSE = structs::PointerCapabilities_Coarse;\n        const FINE = structs::PointerCapabilities_Fine;\n        const HOVER = structs::PointerCapabilities_Hover;\n    }\n}\n\nfn primary_pointer_capabilities(context: &Context) -> PointerCapabilities {\n    PointerCapabilities::from_bits_truncate(unsafe {\n        bindings::Gecko_MediaFeatures_PrimaryPointerCapabilities(context.device().document())\n    })\n}\n\nfn all_pointer_capabilities(context: &Context) -> PointerCapabilities {\n    PointerCapabilities::from_bits_truncate(unsafe {\n        bindings::Gecko_MediaFeatures_AllPointerCapabilities(context.device().document())\n    })\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum Pointer {\n    None,\n    Coarse,\n    Fine,\n}\n\nfn eval_pointer_capabilities(\n    query_value: Option<Pointer>,\n    pointer_capabilities: PointerCapabilities,\n) -> bool {\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return !pointer_capabilities.is_empty(),\n    };\n\n    match query_value {\n        Pointer::None => pointer_capabilities.is_empty(),\n        Pointer::Coarse => pointer_capabilities.intersects(PointerCapabilities::COARSE),\n        Pointer::Fine => pointer_capabilities.intersects(PointerCapabilities::FINE),\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#pointer\nfn eval_pointer(context: &Context, query_value: Option<Pointer>) -> bool {\n    eval_pointer_capabilities(query_value, primary_pointer_capabilities(context))\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-pointer\nfn eval_any_pointer(context: &Context, query_value: Option<Pointer>) -> bool {\n    eval_pointer_capabilities(query_value, all_pointer_capabilities(context))\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum Hover {\n    None,\n    Hover,\n}\n\nfn eval_hover_capabilities(\n    query_value: Option<Hover>,\n    pointer_capabilities: PointerCapabilities,\n) -> bool {\n    let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER);\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return can_hover,\n    };\n\n    match query_value {\n        Hover::None => !can_hover,\n        Hover::Hover => can_hover,\n    }\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#hover\nfn eval_hover(context: &Context, query_value: Option<Hover>) -> bool {\n    eval_hover_capabilities(query_value, primary_pointer_capabilities(context))\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#descdef-media-any-hover\nfn eval_any_hover(context: &Context, query_value: Option<Hover>) -> bool {\n    eval_hover_capabilities(query_value, all_pointer_capabilities(context))\n}\n\nfn eval_moz_is_glyph(context: &Context) -> bool {\n    context.device().document().mIsSVGGlyphsDocument()\n}\n\nfn eval_moz_in_android_pip_mode(context: &Context) -> bool {\n    unsafe { bindings::Gecko_MediaFeatures_InAndroidPipMode(context.device().document()) }\n}\n\nfn eval_moz_print_preview(context: &Context) -> bool {\n    let is_print_preview = context.device().is_print_preview();\n    if is_print_preview {\n        debug_assert_eq!(context.device().media_type(), MediaType::print());\n    }\n    is_print_preview\n}\n\nfn eval_moz_is_resource_document(context: &Context) -> bool {\n    unsafe { bindings::Gecko_MediaFeatures_IsResourceDocument(context.device().document()) }\n}\n\n/// Allows front-end CSS to discern platform via media queries.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\npub enum Platform {\n    /// Matches any Android version.\n    Android,\n    /// For our purposes here, \"linux\" is just \"gtk\" (so unix-but-not-mac).\n    /// There's no need for our front-end code to differentiate between those\n    /// platforms and they already use the \"linux\" string elsewhere (e.g.,\n    /// toolkit/themes/linux).\n    Linux,\n    /// Matches any iOS version.\n    Ios,\n    /// Matches any macOS version.\n    Macos,\n    /// Matches any Windows version.\n    Windows,\n}\n\nfn eval_moz_platform(_: &Context, query_value: Option<Platform>) -> bool {\n    let query_value = match query_value {\n        Some(v) => v,\n        None => return false,\n    };\n\n    unsafe { bindings::Gecko_MediaFeatures_MatchesPlatform(query_value) }\n}\n\n/// Allows front-end CSS to discern gtk theme via media queries.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\npub enum GtkThemeFamily {\n    /// Unknown theme family.\n    Unknown = 0,\n    /// Adwaita, the default GTK theme.\n    Adwaita,\n    /// Breeze, the default KDE theme.\n    Breeze,\n    /// Yaru, the default Ubuntu theme.\n    Yaru,\n}\n\nfn eval_gtk_theme_family(_: &Context, query_value: Option<GtkThemeFamily>) -> bool {\n    let family = unsafe { bindings::Gecko_MediaFeatures_GtkThemeFamily() };\n    match query_value {\n        Some(v) => v == family,\n        None => return family != GtkThemeFamily::Unknown,\n    }\n}\n\n/// Values for the scripting media feature.\n/// https://drafts.csswg.org/mediaqueries-5/#scripting\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\npub enum Scripting {\n    /// Scripting is not supported or not enabled\n    None,\n    /// Scripting is supported and enabled, but only for initial page load\n    /// We will never match this value as it is intended for non-browser user agents,\n    /// but it is part of the spec so we should still parse it.\n    /// See: https://github.com/w3c/csswg-drafts/issues/8621\n    InitialOnly,\n    /// Scripting is supported and enabled\n    Enabled,\n}\n\n/// https://drafts.csswg.org/mediaqueries-5/#scripting\nfn eval_scripting(context: &Context, query_value: Option<Scripting>) -> bool {\n    let scripting = unsafe { bindings::Gecko_MediaFeatures_Scripting(context.device().document()) };\n    match query_value {\n        Some(v) => v == scripting,\n        None => scripting != Scripting::None,\n    }\n}\n\nfn eval_moz_overlay_scrollbars(context: &Context) -> bool {\n    unsafe { bindings::Gecko_MediaFeatures_UseOverlayScrollbars(context.device().document()) }\n}\n\nfn eval_moz_mac_rtl(context: &Context) -> bool {\n    unsafe { bindings::Gecko_MediaFeatures_MacRTL(context.device().document()) }\n}\n\nfn eval_moz_native_theme(context: &Context) -> bool {\n    if context.device().document().mForceNonNativeTheme() {\n        return false;\n    }\n    static_prefs::pref!(\"browser.theme.native-theme\")\n}\n\nfn get_lnf_int(int_id: i32) -> i32 {\n    unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) }\n}\n\nfn get_lnf_int_as_bool(int_id: i32) -> bool {\n    get_lnf_int(int_id) != 0\n}\n\nmacro_rules! lnf_int_feature {\n    ($feature_name:expr, $int_id:ident, $get_value:ident) => {{\n        fn __eval(_: &Context) -> bool {\n            $get_value(bindings::LookAndFeel_IntID::$int_id as i32)\n        }\n\n        feature!(\n            $feature_name,\n            AllowsRanges::No,\n            Evaluator::BoolInteger(__eval),\n            FeatureFlags::CHROME_AND_UA_ONLY,\n        )\n    }};\n    ($feature_name:expr, $int_id:ident) => {{\n        lnf_int_feature!($feature_name, $int_id, get_lnf_int_as_bool)\n    }};\n}\n\n/// Adding new media features requires (1) adding the new feature to this\n/// array, with appropriate entries (and potentially any new code needed\n/// to support new types in these entries and (2) ensuring that either\n/// nsPresContext::MediaFeatureValuesChanged is called when the value that\n/// would be returned by the evaluator function could change.\npub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [\n    feature!(\n        atom!(\"width\"),\n        AllowsRanges::Yes,\n        Evaluator::Length(eval_width),\n        FeatureFlags::VIEWPORT_DEPENDENT,\n    ),\n    feature!(\n        atom!(\"height\"),\n        AllowsRanges::Yes,\n        Evaluator::Length(eval_height),\n        FeatureFlags::VIEWPORT_DEPENDENT,\n    ),\n    feature!(\n        atom!(\"aspect-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::NumberRatio(eval_aspect_ratio),\n        FeatureFlags::VIEWPORT_DEPENDENT,\n    ),\n    feature!(\n        atom!(\"orientation\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_orientation, Orientation),\n        FeatureFlags::VIEWPORT_DEPENDENT,\n    ),\n    feature!(\n        atom!(\"device-width\"),\n        AllowsRanges::Yes,\n        Evaluator::Length(eval_device_width),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"device-height\"),\n        AllowsRanges::Yes,\n        Evaluator::Length(eval_device_height),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"device-aspect-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::NumberRatio(eval_device_aspect_ratio),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"-moz-device-orientation\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_device_orientation, Orientation),\n        FeatureFlags::empty(),\n    ),\n    // Webkit extensions that we support for de-facto web compatibility.\n    // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref):\n    feature!(\n        atom!(\"device-pixel-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::Float(eval_device_pixel_ratio),\n        FeatureFlags::WEBKIT_PREFIX,\n    ),\n    // -webkit-transform-3d.\n    feature!(\n        atom!(\"transform-3d\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_transform_3d),\n        FeatureFlags::WEBKIT_PREFIX,\n    ),\n    feature!(\n        atom!(\"-moz-device-pixel-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::Float(eval_device_pixel_ratio),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"resolution\"),\n        AllowsRanges::Yes,\n        Evaluator::Resolution(eval_resolution),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"display-mode\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_display_mode, DisplayMode),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"grid\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_grid),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"scan\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_scan, Scan),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"color\"),\n        AllowsRanges::Yes,\n        Evaluator::Integer(eval_color),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"color-index\"),\n        AllowsRanges::Yes,\n        Evaluator::Integer(eval_color_index),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"monochrome\"),\n        AllowsRanges::Yes,\n        Evaluator::Integer(eval_monochrome),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"color-gamut\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_color_gamut, ColorGamut),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"prefers-reduced-motion\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"prefers-reduced-transparency\"),\n        AllowsRanges::No,\n        keyword_evaluator!(\n            eval_prefers_reduced_transparency,\n            PrefersReducedTransparency\n        ),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"prefers-contrast\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_prefers_contrast, PrefersContrast),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"forced-colors\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_forced_colors, ForcedColors),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"inverted-colors\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_inverted_colors, InvertedColors),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"overflow-block\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_overflow_block, OverflowBlock),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"overflow-inline\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_overflow_inline, OverflowInline),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"update\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_update, Update),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"prefers-color-scheme\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"dynamic-range\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_dynamic_range, DynamicRange),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"video-dynamic-range\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_video_dynamic_range, DynamicRange),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"scripting\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_scripting, Scripting),\n        FeatureFlags::empty(),\n    ),\n    // Evaluates to the preferred color scheme for content. Only useful in\n    // chrome context, where the chrome color-scheme and the content\n    // color-scheme might differ.\n    feature!(\n        atom!(\"-moz-content-prefers-color-scheme\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"pointer\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_pointer, Pointer),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"any-pointer\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_any_pointer, Pointer),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"hover\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_hover, Hover),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"any-hover\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_any_hover, Hover),\n        FeatureFlags::empty(),\n    ),\n    // Internal -moz-is-glyph media feature: applies only inside SVG glyphs.\n    // Internal because it is really only useful in the user agent anyway\n    // and therefore not worth standardizing.\n    feature!(\n        atom!(\"-moz-is-glyph\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_is_glyph),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-in-android-pip-mode\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_in_android_pip_mode),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-is-resource-document\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_is_resource_document),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-platform\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_moz_platform, Platform),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-gtk-theme-family\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_gtk_theme_family, GtkThemeFamily),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-print-preview\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_print_preview),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-overlay-scrollbars\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_overlay_scrollbars),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    lnf_int_feature!(atom!(\"-moz-menubar-drag\"), MenuBarDrag),\n    lnf_int_feature!(atom!(\"-moz-mac-big-sur-theme\"), MacBigSurTheme),\n    lnf_int_feature!(atom!(\"-moz-mac-tahoe-theme\"), MacTahoeTheme),\n    feature!(\n        atom!(\"-moz-mac-rtl\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_mac_rtl),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    feature!(\n        atom!(\"-moz-native-theme\"),\n        AllowsRanges::No,\n        Evaluator::BoolInteger(eval_moz_native_theme),\n        FeatureFlags::CHROME_AND_UA_ONLY,\n    ),\n    lnf_int_feature!(\n        atom!(\"-moz-windows-accent-color-in-titlebar\"),\n        WindowsAccentColorInTitlebar\n    ),\n    lnf_int_feature!(atom!(\"-moz-windows-mica\"), WindowsMica),\n    lnf_int_feature!(atom!(\"-moz-windows-mica-popups\"), WindowsMicaPopups),\n    lnf_int_feature!(atom!(\"-moz-swipe-animation-enabled\"), SwipeAnimationEnabled),\n    lnf_int_feature!(atom!(\"-moz-gtk-csd-available\"), GTKCSDAvailable),\n    lnf_int_feature!(\n        atom!(\"-moz-gtk-csd-transparency-available\"),\n        GTKCSDTransparencyAvailable\n    ),\n    lnf_int_feature!(atom!(\"-moz-gtk-csd-minimize-button\"), GTKCSDMinimizeButton),\n    lnf_int_feature!(atom!(\"-moz-gtk-csd-maximize-button\"), GTKCSDMaximizeButton),\n    lnf_int_feature!(atom!(\"-moz-gtk-csd-close-button\"), GTKCSDCloseButton),\n    lnf_int_feature!(\n        atom!(\"-moz-gtk-csd-reversed-placement\"),\n        GTKCSDReversedPlacement\n    ),\n    lnf_int_feature!(atom!(\"-moz-system-dark-theme\"), SystemUsesDarkTheme),\n    lnf_int_feature!(atom!(\"-moz-panel-animations\"), PanelAnimations),\n];\n"
  },
  {
    "path": "style/gecko/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko-specific style-system bits.\n\n#[macro_use]\nmod non_ts_pseudo_class_list;\n\npub mod arc_types;\npub mod conversions;\npub mod data;\npub mod media_features;\npub mod pseudo_element;\npub mod restyle_damage;\npub mod selector_parser;\npub mod snapshot;\npub mod snapshot_helpers;\npub mod traversal;\npub mod url;\npub mod wrapper;\n"
  },
  {
    "path": "style/gecko/non_ts_pseudo_class_list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n/*\n * This file contains a helper macro includes all supported non-tree-structural\n * pseudo-classes.\n *\n * FIXME: Find a way to autogenerate this file.\n *\n * Expected usage is as follows:\n * ```\n * macro_rules! pseudo_class_macro{\n *     ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n *         // do stuff\n *     }\n * }\n * apply_non_ts_list!(pseudo_class_macro)\n * ```\n *\n * $state can be either \"_\" or an expression of type ElementState.  If present,\n *        the semantics are that the pseudo-class matches if any of the bits in\n *        $state are set on the element.\n * $flags can be either \"_\" or an expression of type NonTSPseudoClassFlag,\n * see selector_parser.rs for more details.\n */\n\nmacro_rules! apply_non_ts_list {\n    ($apply_macro:ident) => {\n        $apply_macro! {\n            [\n                (\"-moz-table-border-nonzero\", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-select-list-box\", MozSelectListBox, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),\n                (\"link\", Link, UNVISITED, _),\n                (\"any-link\", AnyLink, VISITED_OR_UNVISITED, _),\n                (\"visited\", Visited, VISITED, _),\n                (\"active\", Active, ACTIVE, _),\n                (\"autofill\", Autofill, AUTOFILL, _),\n                (\"checked\", Checked, CHECKED, _),\n                (\"defined\", Defined, DEFINED, _),\n                (\"disabled\", Disabled, DISABLED, _),\n                (\"enabled\", Enabled, ENABLED, _),\n                (\"focus\", Focus, FOCUS, _),\n                (\"focus-within\", FocusWithin, FOCUS_WITHIN, _),\n                (\"focus-visible\", FocusVisible, FOCUSRING, _),\n                (\"has-slotted\", HasSlotted, HAS_SLOTTED, _),\n                (\"hover\", Hover, HOVER, _),\n                (\"active-view-transition\", ActiveViewTransition, ACTIVE_VIEW_TRANSITION, _),\n                (\"-moz-drag-over\", MozDragOver, DRAGOVER, _),\n                (\"target\", Target, URLTARGET, _),\n                (\"indeterminate\", Indeterminate, INDETERMINATE, _),\n                (\"-moz-inert\", MozInert, INERT, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-devtools-highlighted\", MozDevtoolsHighlighted, DEVTOOLS_HIGHLIGHTED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-styleeditor-transitioning\", MozStyleeditorTransitioning, STYLEEDITOR_TRANSITIONING, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"fullscreen\", Fullscreen, FULLSCREEN, _),\n                (\"modal\", Modal, MODAL, _),\n                (\"open\", Open, OPEN, _),\n                (\"-moz-topmost-modal\", MozTopmostModal, TOPMOST_MODAL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-broken\", MozBroken, BROKEN, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),\n                (\"-moz-has-dir-attr\", MozHasDirAttr, HAS_DIR_ATTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-dir-attr-ltr\", MozDirAttrLTR, HAS_DIR_ATTR_LTR, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-dir-attr-rtl\", MozDirAttrRTL, HAS_DIR_ATTR_RTL, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-dir-attr-like-auto\", MozDirAttrLikeAuto, HAS_DIR_ATTR_LIKE_AUTO, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n\n                (\"-moz-autofill-preview\", MozAutofillPreview, AUTOFILL_PREVIEW, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),\n                (\"-moz-value-empty\", MozValueEmpty, VALUE_EMPTY, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-revealed\", MozRevealed, REVEALED, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-suppress-for-print-selection\", MozSuppressForPrintSelection, SUPPRESS_FOR_PRINT_SELECTION, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n\n                (\"-moz-math-increment-script-level\", MozMathIncrementScriptLevel, INCREMENT_SCRIPT_LEVEL, _),\n\n                (\"required\", Required, REQUIRED, _),\n                (\"popover-open\", PopoverOpen, POPOVER_OPEN, _),\n                (\"optional\", Optional, OPTIONAL_, _),\n                (\"valid\", Valid, VALID, _),\n                (\"invalid\", Invalid, INVALID, _),\n                (\"in-range\", InRange, INRANGE, _),\n                (\"out-of-range\", OutOfRange, OUTOFRANGE, _),\n                (\"default\", Default, DEFAULT, _),\n                (\"placeholder-shown\", PlaceholderShown, PLACEHOLDER_SHOWN, _),\n                (\"read-only\", ReadOnly, READONLY, _),\n                (\"read-write\", ReadWrite, READWRITE, _),\n                (\"user-valid\", UserValid, USER_VALID, _),\n                (\"user-invalid\", UserInvalid, USER_INVALID, _),\n                (\"-moz-meter-optimum\", MozMeterOptimum, OPTIMUM, _),\n                (\"-moz-meter-sub-optimum\", MozMeterSubOptimum, SUB_OPTIMUM, _),\n                (\"-moz-meter-sub-sub-optimum\", MozMeterSubSubOptimum, SUB_SUB_OPTIMUM, _),\n\n                (\"-moz-first-node\", MozFirstNode, _, _),\n                (\"-moz-last-node\", MozLastNode, _, _),\n                (\"-moz-only-whitespace\", MozOnlyWhitespace, _, _),\n                (\"-moz-native-anonymous\", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-placeholder\", MozPlaceholder, _, _),\n\n                // Media element pseudo classes\n                (\"paused\", Paused, PAUSED, _),\n                (\"playing\", Playing, PAUSED, _),\n                (\"seeking\", Seeking, SEEKING, _),\n                (\"buffering\", Buffering, BUFFERING, _),\n                (\"stalled\", Stalled, STALLED, _),\n                (\"muted\", Muted, MUTED, _),\n                (\"volume-locked\", VolumeLocked, _, _),\n\n\n                // NOTE(emilio): Pseudo-classes below only depend on document state, and thus\n                // conceptually they should probably be media queries instead.\n                //\n                // However that has a set of trade-offs that might not be worth making. In\n                // particular, such media queries would prevent documents that match them from\n                // sharing user-agent stylesheets with documents that don't. Also, changes between\n                // media query results are more expensive than document state changes. So for now\n                // making them pseudo-classes is probably the right trade-off.\n                (\"-moz-is-html\", MozIsHTML, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),\n                (\"-moz-window-inactive\", MozWindowInactive, _, _),\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko/pseudo_element.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko's definition of a pseudo-element.\n//!\n//! Note that a few autogenerated bits of this live in\n//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably\n//! need to update the checked-in files for Servo.\n\nuse crate::gecko_bindings::structs::PseudoStyleType;\nuse crate::properties::longhands::display::computed_value::T as Display;\nuse crate::properties::{ComputedValues, PropertyFlags};\nuse crate::selector_parser::{PseudoElementCascadeType, SelectorImpl};\nuse crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};\nuse crate::string_cache::Atom;\nuse crate::values::serialize_atom_identifier;\nuse crate::values::AtomIdent;\nuse cssparser::{Parser, ToCss};\nuse selectors::parser::PseudoElement as PseudoElementTrait;\nuse static_prefs::pref;\nuse std::fmt;\nuse style_traits::ParseError;\n\nbitflags! {\n    /// Various pseudo-element flags, see pseudo_elements.toml and anonymous_boxes.toml for the\n    /// meaning.\n    #[derive(Clone, Copy, Default)]\n    pub struct PseudoStyleTypeFlags : u16 {\n        /// No flags\n        const NONE = 0;\n        /// Whether we're enabled in UA sheets.\n        const ENABLED_IN_UA = 1 << 0;\n        /// Whether we're enabled in chrome sheets.\n        const ENABLED_IN_CHROME = 1 << 1;\n        /// Whether we're enabled by a pref.\n        const ENABLED_BY_PREF = 1 << 2;\n        /// Whether we're an anonymous box.\n        const IS_PSEUDO_ELEMENT = 1 << 3;\n        /// Whether we're a CSS2 pseudo-element.\n        const IS_CSS2 = 1 << 4;\n        /// Whether we're an eagerly-cascaded pseudo-element.\n        const IS_EAGER = 1 << 5;\n        /// Whether we can be created by JS\n        const IS_JS_CREATED_NAC = 1 << 6;\n        /// Whether we can be a flex or grid item.\n        const IS_FLEX_OR_GRID_ITEM = 1 << 7;\n        /// Whether we're backed by a real element.\n        const IS_ELEMENT_BACKED = 1 << 8;\n        /// Whether we're a tree-abiding pseudo as per\n        /// https://drafts.csswg.org/css-pseudo-4/#treelike\n        const IS_TREE_ABIDING = 1 << 9;\n        /// Whether we support user-action state pseudo-classes after the pseudo-element.\n        const SUPPORTS_USER_ACTION_STATE = 1 << 10;\n        /// Whether we are an inheriting anon-box.\n        const IS_INHERITING_ANON_BOX = 1 << 11;\n        /// Whether we are a non-inheriting anon box.\n        const IS_NON_INHERITING_ANON_BOX = 1 << 12;\n        /// Combo of the above to cover all anon boxes.\n        const IS_ANON_BOX = Self::IS_INHERITING_ANON_BOX.bits() |\n                            Self::IS_NON_INHERITING_ANON_BOX.bits();\n        /// Whether we're a wrapping anon box.\n        const IS_WRAPPER_ANON_BOX = 1 << 13;\n        /// Whether we parse as an element-backed pseudo-element.\n        const PARSES_AS_ELEMENT_BACKED = 1 << 14;\n    }\n}\n\ninclude!(concat!(\n    env!(\"OUT_DIR\"),\n    \"/gecko/pseudo_element_definition.rs\"\n));\n\n/// The target we are using for parsing pseudo-elements.\npub enum Target {\n    /// When parsing a selector, we want to use the full syntax.\n    Selector,\n    /// When parsing the pseudo-element string (from CSSOM), we only accept CusomIdent for named\n    /// view transition pseudo-elements.\n    Cssom,\n}\n\n/// The type to hold the value of `<pt-name-and-class-selector>`.\n///\n/// `<pt-name-and-class-selector> = <pt-name-selector> <pt-class-selector>? | <pt-class-selector>`\n/// `<pt-name-selector> = '*' | <custom-ident>`\n/// `<pt-class-selector> = ['.' <custom-ident>]+`\n///\n/// This type should have at least one element.\n/// If there is no <pt-name-selector>, the first element would be the universal symbol, i.e. '*'.\n/// In other words, when we match it, \".abc\" is the same as \"*.abc\".\n/// Note that we also serialize \".abc\" as \"*.abc\".\n///\n/// We use a single ThinVec<> to represent this structure to avoid allocating too much memory for a\n/// single selectors::parser::Component (max: 24 bytes) and PseudoElement (max: 16 bytes).\n///\n/// https://drafts.csswg.org/css-view-transitions-2/#typedef-pt-name-and-class-selector\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]\npub struct PtNameAndClassSelector(thin_vec::ThinVec<Atom>);\n\nimpl PtNameAndClassSelector {\n    /// Constructs a new one from a name.\n    pub fn from_name(name: Atom) -> Self {\n        Self(thin_vec::thin_vec![name])\n    }\n\n    /// Returns the name component.\n    pub fn name(&self) -> &Atom {\n        debug_assert!(!self.0.is_empty());\n        self.0.first().expect(\"Shouldn't be empty\")\n    }\n\n    /// Returns the classes component.\n    pub fn classes(&self) -> &[Atom] {\n        debug_assert!(!self.0.is_empty());\n        &self.0[1..]\n    }\n\n    /// Returns the vector we store.\n    pub fn name_and_classes(&self) -> &thin_vec::ThinVec<Atom> {\n        &self.0\n    }\n\n    /// Parse the pseudo-element tree name and/or class.\n    /// |for_selector| is true if we are parsing the CSS selectors and so need to check the\n    /// universal symbol, i.e. '*', and classes.\n    // Note: We share the same type for both pseudo-element and pseudo-element selector. The\n    // universal symbol (i.e. '*') and `<pt-class-selector>` are used only in the selector (for\n    // matching).\n    pub fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        target: Target,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::CustomIdent;\n        use cssparser::Token;\n        use style_traits::StyleParseErrorKind;\n\n        // <pt-name-selector> = '*' | <custom-ident>\n        let parse_pt_name = |input: &mut Parser<'i, '_>| {\n            // For pseudo-element string, we don't accept '*'.\n            if matches!(target, Target::Selector)\n                && input.try_parse(|i| i.expect_delim('*')).is_ok()\n            {\n                Ok(atom!(\"*\"))\n            } else {\n                CustomIdent::parse(input, &[]).map(|c| c.0)\n            }\n        };\n        let name = input.try_parse(parse_pt_name);\n\n        // Skip <pt-class-selector> for pseudo-element string.\n        if matches!(target, Target::Cssom) {\n            return name.map(Self::from_name);\n        }\n\n        // <pt-class-selector> = ['.' <custom-ident>]+\n        let parse_pt_class = |input: &mut Parser<'i, '_>| {\n            // The white space is forbidden:\n            // 1. Between <pt-name-selector> and <pt-class-selector>\n            // 2. Between any of the components of <pt-class-selector>.\n            let location = input.current_source_location();\n            match input.next_including_whitespace()? {\n                Token::Delim('.') => (),\n                t => return Err(location.new_unexpected_token_error(t.clone())),\n            }\n            // Whitespace is not allowed between '.' and the class name.\n            if let Ok(token) = input.try_parse(|i| i.expect_whitespace()) {\n                return Err(input.new_unexpected_token_error(Token::WhiteSpace(token)));\n            }\n            CustomIdent::parse(input, &[]).map(|c| c.0)\n        };\n        // If there is no `<pt-name-selector>`, it's fine to have whitespaces before the first '.'.\n        if name.is_err() {\n            input.skip_whitespace();\n        }\n        let mut classes = thin_vec::ThinVec::new();\n        while let Ok(class) = input.try_parse(parse_pt_class) {\n            classes.push(class);\n        }\n\n        // If we don't have `<pt-name-selector>`, we must have `<pt-class-selector>`, per the\n        // syntax: `<pt-name-selector> <pt-class-selector>? | <pt-class-selector>`.\n        if name.is_err() && classes.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // Use the universal symbol as the first element to present the part of\n        // `<pt-name-selector>` because they are equivalent (and the serialization is the same).\n        let mut result = thin_vec::thin_vec![name.unwrap_or(atom!(\"*\"))];\n        result.append(&mut classes);\n\n        Ok(Self(result))\n    }\n}\n\nimpl ToCss for PtNameAndClassSelector {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        let name = self.name();\n        if name == &atom!(\"*\") {\n            // serialize_atom_identifier() may serialize \"*\" as \"\\*\", so we handle it separately.\n            dest.write_char('*')?;\n        } else {\n            serialize_atom_identifier(name, dest)?;\n        }\n\n        for class in self.classes() {\n            dest.write_char('.')?;\n            serialize_atom_identifier(class, dest)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl PseudoElementTrait for PseudoElement {\n    type Impl = SelectorImpl;\n\n    // ::slotted() should support all tree-abiding pseudo-elements, see\n    // https://drafts.csswg.org/css-scoping/#slotted-pseudo\n    // https://drafts.csswg.org/css-pseudo-4/#treelike\n    #[inline]\n    fn valid_after_slotted(&self) -> bool {\n        self.flags()\n            .intersects(PseudoStyleTypeFlags::IS_TREE_ABIDING)\n    }\n\n    // ::before/::after should support ::marker, but no others.\n    // https://drafts.csswg.org/css-pseudo-4/#marker-pseudo\n    #[inline]\n    fn valid_after_before_or_after(&self) -> bool {\n        matches!(*self, Self::Marker)\n    }\n\n    #[inline]\n    fn accepts_state_pseudo_classes(&self) -> bool {\n        // Note: if the pseudo element is a descendants of a pseudo element, `only-child` should be\n        // allowed after it.\n        self.supports_user_action_state() || self.is_in_pseudo_element_tree()\n    }\n\n    #[inline]\n    fn specificity_count(&self) -> u32 {\n        self.specificity_count()\n    }\n\n    #[inline]\n    fn is_in_pseudo_element_tree(&self) -> bool {\n        // All the named view transition pseudo-elements are the descendants of a pseudo-element\n        // root.\n        self.is_named_view_transition()\n    }\n\n    fn parses_as_element_backed(&self) -> bool {\n        self.flags()\n            .intersects(PseudoStyleTypeFlags::PARSES_AS_ELEMENT_BACKED)\n    }\n\n    /// Whether the current pseudo element is ::before or ::after.\n    #[inline]\n    fn is_before_or_after(&self) -> bool {\n        matches!(*self, PseudoElement::Before | PseudoElement::After)\n    }\n}\n\nimpl PseudoElement {\n    /// Whether this pseudo-element is \"element-backed\", which means that it inherits from its regular\n    /// flat tree parent, which might not be the originating element.\n    #[inline]\n    pub fn is_element_backed(&self) -> bool {\n        self.flags()\n            .intersects(PseudoStyleTypeFlags::IS_ELEMENT_BACKED)\n    }\n\n    /// Returns the kind of cascade type that a given pseudo is going to use.\n    ///\n    /// In Gecko we only compute ::before and ::after eagerly. We save the rules\n    /// for anonymous boxes separately, so we resolve them as precomputed\n    /// pseudos.\n    ///\n    /// We resolve the others lazily, see `Servo_ResolvePseudoStyle`.\n    pub fn cascade_type(&self) -> PseudoElementCascadeType {\n        if self.is_eager() {\n            debug_assert!(!self.is_anon_box());\n            return PseudoElementCascadeType::Eager;\n        }\n\n        if self.is_precomputed() {\n            return PseudoElementCascadeType::Precomputed;\n        }\n\n        PseudoElementCascadeType::Lazy\n    }\n\n    /// Returns an index of the pseudo-element.\n    #[inline]\n    pub fn index(&self) -> usize {\n        self.discriminant() as usize\n    }\n\n    #[inline]\n    fn discriminant(&self) -> u8 {\n        // SAFETY: #[repr(u8) guarantees this, see comments in\n        // https://doc.rust-lang.org/std/mem/fn.discriminant.html\n        unsafe { *(self as *const _ as *const u8) }\n    }\n\n    /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element.\n    #[inline]\n    pub fn is_unknown_webkit_pseudo_element(&self) -> bool {\n        matches!(*self, PseudoElement::UnknownWebkit(..))\n    }\n\n    /// Whether this pseudo-element is an anonymous box.\n    #[inline]\n    pub fn is_anon_box(&self) -> bool {\n        self.flags().intersects(PseudoStyleTypeFlags::IS_ANON_BOX)\n    }\n\n    /// Whether this pseudo-element is eagerly-cascaded.\n    #[inline]\n    pub fn is_eager(&self) -> bool {\n        self.flags().intersects(PseudoStyleTypeFlags::IS_EAGER)\n    }\n\n    /// Gets the canonical index of this eagerly-cascaded pseudo-element.\n    #[inline]\n    pub fn eager_index(&self) -> usize {\n        EAGER_PSEUDOS\n            .iter()\n            .position(|p| p == self)\n            .expect(\"Not an eager pseudo\")\n    }\n\n    /// Creates a pseudo-element from an eager index.\n    #[inline]\n    pub fn from_eager_index(i: usize) -> Self {\n        EAGER_PSEUDOS[i].clone()\n    }\n\n    /// Whether animations for the current pseudo element are stored in the\n    /// parent element.\n    #[inline]\n    pub fn animations_stored_in_parent(&self) -> bool {\n        matches!(\n            *self,\n            Self::Before | Self::After | Self::Marker | Self::Backdrop\n        )\n    }\n\n    /// Whether this pseudo-element is the ::before pseudo.\n    #[inline]\n    pub fn is_before(&self) -> bool {\n        *self == PseudoElement::Before\n    }\n\n    /// Whether this pseudo-element is the ::after pseudo.\n    #[inline]\n    pub fn is_after(&self) -> bool {\n        *self == PseudoElement::After\n    }\n\n    /// Whether this pseudo-element is the ::marker pseudo.\n    #[inline]\n    pub fn is_marker(&self) -> bool {\n        *self == PseudoElement::Marker\n    }\n\n    /// Whether this pseudo-element is the ::selection pseudo.\n    #[inline]\n    pub fn is_selection(&self) -> bool {\n        *self == PseudoElement::Selection\n    }\n\n    /// Whether this pseudo-element is ::first-letter.\n    #[inline]\n    pub fn is_first_letter(&self) -> bool {\n        *self == PseudoElement::FirstLetter\n    }\n\n    /// Whether this pseudo-element is ::first-line.\n    #[inline]\n    pub fn is_first_line(&self) -> bool {\n        *self == PseudoElement::FirstLine\n    }\n\n    /// Whether this pseudo-element is lazily-cascaded.\n    #[inline]\n    pub fn is_lazy(&self) -> bool {\n        !self.is_eager() && !self.is_precomputed()\n    }\n\n    /// The identifier of the highlight this pseudo-element represents.\n    pub fn highlight_name(&self) -> Option<&AtomIdent> {\n        match *self {\n            Self::Highlight(ref name) => Some(name),\n            _ => None,\n        }\n    }\n\n    /// Whether this pseudo-element is the ::highlight pseudo.\n    pub fn is_highlight(&self) -> bool {\n        matches!(*self, Self::Highlight(_))\n    }\n\n    /// Whether this pseudo-element is the ::target-text pseudo.\n    #[inline]\n    pub fn is_target_text(&self) -> bool {\n        *self == PseudoElement::TargetText\n    }\n\n    /// Whether this is a highlight pseudo-element that is styled lazily during\n    /// painting rather than during the restyle traversal. These pseudos need\n    /// explicit repaint triggering when their styles change.\n    #[inline]\n    pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {\n        self.is_selection() || self.is_highlight() || self.is_target_text()\n    }\n\n    /// Whether this pseudo-element is a named view transition pseudo-element.\n    pub fn is_named_view_transition(&self) -> bool {\n        matches!(\n            *self,\n            Self::ViewTransitionGroup(..)\n                | Self::ViewTransitionImagePair(..)\n                | Self::ViewTransitionOld(..)\n                | Self::ViewTransitionNew(..)\n        )\n    }\n\n    /// The count we contribute to the specificity from this pseudo-element.\n    pub fn specificity_count(&self) -> u32 {\n        match *self {\n            Self::ViewTransitionGroup(ref name_and_class)\n            | Self::ViewTransitionImagePair(ref name_and_class)\n            | Self::ViewTransitionOld(ref name_and_class)\n            | Self::ViewTransitionNew(ref name_and_class) => {\n                // The specificity of a named view transition pseudo-element selector with either:\n                // 1. a <pt-name-selector> with a <custom-ident>; or\n                // 2. a <pt-class-selector> with at least one <custom-ident>,\n                // is equivalent to a type selector.\n                //\n                // The specificity of a named view transition pseudo-element selector with a `*`\n                // argument and with an empty <pt-class-selector> is zero.\n                // https://drafts.csswg.org/css-view-transitions-2/#pseudo-element-class-additions\n                (name_and_class.name() != &atom!(\"*\") || !name_and_class.classes().is_empty())\n                    as u32\n            },\n            _ => 1,\n        }\n    }\n\n    /// Whether this pseudo-element supports user action selectors.\n    pub fn supports_user_action_state(&self) -> bool {\n        self.flags()\n            .intersects(PseudoStyleTypeFlags::SUPPORTS_USER_ACTION_STATE)\n    }\n\n    /// Whether this pseudo-element is enabled for all content.\n    pub fn enabled_in_content(&self) -> bool {\n        Self::type_enabled_in_content(self.pseudo_type())\n    }\n\n    /// Whether this pseudo is enabled explicitly in UA sheets.\n    pub fn enabled_in_ua_sheets(&self) -> bool {\n        self.flags().intersects(PseudoStyleTypeFlags::ENABLED_IN_UA)\n    }\n\n    /// Whether this pseudo is enabled explicitly in chrome sheets.\n    pub fn enabled_in_chrome(&self) -> bool {\n        self.flags()\n            .intersects(PseudoStyleTypeFlags::ENABLED_IN_CHROME)\n    }\n\n    /// Whether this pseudo-element skips flex/grid container display-based\n    /// fixup.\n    #[inline]\n    pub fn skip_item_display_fixup(&self) -> bool {\n        !self\n            .flags()\n            .intersects(PseudoStyleTypeFlags::IS_FLEX_OR_GRID_ITEM)\n    }\n\n    /// Whether this pseudo-element is precomputed.\n    #[inline]\n    pub fn is_precomputed(&self) -> bool {\n        self.is_anon_box()\n    }\n\n    /// Property flag that properties must have to apply to this pseudo-element.\n    #[inline]\n    pub fn property_restriction(&self) -> Option<PropertyFlags> {\n        Some(match *self {\n            PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,\n            PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE,\n            PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,\n            PseudoElement::Cue => PropertyFlags::APPLIES_TO_CUE,\n            PseudoElement::Marker => PropertyFlags::APPLIES_TO_MARKER,\n            _ => return None,\n        })\n    }\n\n    /// Whether this pseudo-element should actually exist if it has\n    /// the given styles.\n    pub fn should_exist(&self, style: &ComputedValues) -> bool {\n        debug_assert!(self.is_eager());\n\n        if style.get_box().clone_display() == Display::None {\n            return false;\n        }\n\n        if self.is_before_or_after() && style.ineffective_content_property() {\n            return false;\n        }\n\n        true\n    }\n\n    /// Parse the pseudo-element string without the check of enabled state. This may includes\n    /// all possible PseudoElement, including tree pseudo-elements and anonymous box.\n    // TODO: Bug 1845712. Merge this with the pseudo element part in parse_one_simple_selector().\n    pub fn parse_ignore_enabled_state<'i, 't>(\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::gecko::selector_parser;\n        use cssparser::Token;\n        use selectors::parser::{is_css2_pseudo_element, SelectorParseErrorKind};\n        use style_traits::StyleParseErrorKind;\n\n        // The pseudo-element string should start with ':'.\n        input.expect_colon()?;\n\n        let location = input.current_source_location();\n        let next = input.next_including_whitespace()?;\n        if !matches!(next, Token::Colon) {\n            // Parse a CSS2 pseudo-element.\n            let name = match next {\n                Token::Ident(name) if is_css2_pseudo_element(&name) => name,\n                _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n            };\n            return PseudoElement::from_slice(&name, false).ok_or(location.new_custom_error(\n                SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()),\n            ));\n        }\n\n        // Now we have double colons, so check the following tokens.\n        match input.next_including_whitespace()?.clone() {\n            Token::Ident(name) => {\n                // We don't need to parse unknown ::-webkit-* pseudo-elements in this function.\n                PseudoElement::from_slice(&name, false).ok_or(input.new_custom_error(\n                    SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),\n                ))\n            },\n            Token::Function(name) => {\n                // Note: ::slotted() and ::part() are not accepted in getComputedStyle().\n                // https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle\n                input.parse_nested_block(|input| {\n                    selector_parser::parse_functional_pseudo_element_with_name(\n                        &name,\n                        input,\n                        Target::Cssom,\n                    )\n                })\n            },\n            t => return Err(input.new_unexpected_token_error(t)),\n        }\n    }\n\n    /// Returns true if this pseudo-element matches its selector.\n    pub fn matches_named_view_transition_pseudo_element(\n        &self,\n        selector: &Self,\n        element: &super::wrapper::GeckoElement,\n    ) -> bool {\n        use crate::gecko_bindings::bindings;\n\n        match (self, selector) {\n            (\n                &Self::ViewTransitionGroup(ref name),\n                &Self::ViewTransitionGroup(ref s_name_class),\n            )\n            | (\n                &Self::ViewTransitionImagePair(ref name),\n                &Self::ViewTransitionImagePair(ref s_name_class),\n            )\n            | (&Self::ViewTransitionOld(ref name), &Self::ViewTransitionOld(ref s_name_class))\n            | (&Self::ViewTransitionNew(ref name), &Self::ViewTransitionNew(ref s_name_class)) => {\n                // Named view transition pseudos accept the universal selector as the name, so we\n                // check it first.\n                // https://drafts.csswg.org/css-view-transitions-1/#named-view-transition-pseudo\n                let s_name = s_name_class.name();\n                if s_name != name.name() && s_name != &atom!(\"*\") {\n                    return false;\n                }\n\n                // We have to check class list only when the name is matched and there are one or\n                // more <pt-class-selector>s.\n                s_name_class.classes().is_empty()\n                    || unsafe {\n                        bindings::Gecko_MatchViewTransitionClass(\n                            element.0,\n                            s_name_class.name_and_classes(),\n                        )\n                    }\n            },\n            _ => false,\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko/pseudo_element_definition.mako.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::derives::*;\n\ntype AtomThinVec = thin_vec::ThinVec<Atom>;\n\n/// Gecko's pseudo-element definition.\n///\n/// We intentionally double-box legacy ::-moz-tree pseudo-elements to keep the\n/// size of PseudoElement (and thus selector components) small.\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]\n#[repr(u8)]\npub enum PseudoElement {\n    % for pseudo in PSEUDOS:\n    /// ${pseudo.name}\n    % if pseudo.argument:\n    ${pseudo.capitalized}(${pseudo.argument}),\n    % else:\n    ${pseudo.capitalized},\n    % endif\n    % endfor\n    /// ::-webkit-* that we don't recognize\n    /// https://github.com/whatwg/compat/issues/103\n    UnknownWebkit(Atom),\n}\n\n<% EAGER_PSEUDOS = list(filter(lambda p: getattr(p, 'is_eager', False), PSEUDOS)) %>\n\n/// The number of eager pseudo-elements.\npub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)};\n\n/// The number of all pseudo-elements.\npub const PSEUDO_COUNT: usize = ${len(PSEUDOS)};\n\n/// The list of eager pseudos.\npub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [\n    % for eager_pseudo in EAGER_PSEUDOS:\n    PseudoElement::${eager_pseudo.capitalized},\n    % endfor\n];\n\n<%def name=\"pseudo_element_variant(pseudo, arg='..')\">\\\nPseudoElement::${pseudo.capitalized}${\"({})\".format(arg) if pseudo.argument else \"\"}\\\n</%def>\n\nimpl PseudoElement {\n    /// Whether this pseudo-element is tree pseudo-element.\n    #[inline]\n    pub fn is_tree_pseudo_element(&self) -> bool {\n        match *self {\n            % for pseudo in PSEUDOS:\n            % if pseudo.name.startswith(\"-moz-tree-\"):\n            ${pseudo_element_variant(pseudo)} => true,\n            % endif\n            % endfor\n            _ => false,\n        }\n    }\n\n    #[inline]\n    fn flags_for_index(i: usize) -> PseudoStyleTypeFlags {\n        static FLAGS: [PseudoStyleTypeFlags; PSEUDO_COUNT + 1] = [\n        % for pseudo in PSEUDOS:\n        PseudoStyleTypeFlags::from_bits_truncate(${' | '.join(f\"PseudoStyleTypeFlags::{flag}.bits()\" for flag in pseudo.flags())}),\n        % endfor\n        PseudoStyleTypeFlags::NONE, // :-webkit-*\n        ];\n        FLAGS[i]\n    }\n\n    /// Gets the flags associated to this pseudo-element or anon box.\n    #[inline]\n    pub fn flags(&self) -> PseudoStyleTypeFlags {\n        Self::flags_for_index(self.index())\n    }\n\n    /// If this pseudo is pref-gated, returns the pref value, otherwise false.\n    pub fn type_enabled_in_content(ty: PseudoStyleType) -> bool {\n        let flags = Self::flags_for_index(ty as usize);\n        // Common path, not enabled explicitly in UA sheets (note that chrome implies UA), and not\n        // pref-gated.\n        if !flags.intersects(PseudoStyleTypeFlags::ENABLED_IN_UA | PseudoStyleTypeFlags::ENABLED_BY_PREF) {\n            return true;\n        }\n        if !flags.intersects(PseudoStyleTypeFlags::ENABLED_BY_PREF) {\n            return false;\n        }\n        match ty {\n        % for pseudo in PSEUDOS:\n        % if pseudo.pref:\n            PseudoStyleType::${pseudo.capitalized} => pref!(\"${pseudo.pref}\"),\n        % endif\n        % endfor\n            _ => false,\n        }\n    }\n\n    /// Construct a pseudo-element from a `PseudoStyleType`.\n    #[inline]\n    pub fn from_pseudo_type(type_: PseudoStyleType, functional_pseudo_parameter: Option<AtomIdent>) -> Option<Self> {\n        match type_ {\n            % for pseudo in PSEUDOS:\n            % if not pseudo.argument:\n            PseudoStyleType::${pseudo.capitalized} => {\n                debug_assert!(functional_pseudo_parameter.is_none());\n                Some(${pseudo_element_variant(pseudo)})\n            },\n            % elif pseudo.argument == \"AtomThinVec\":\n            PseudoStyleType::${pseudo.capitalized} => {\n                debug_assert!(functional_pseudo_parameter.is_none());\n                Some(${pseudo_element_variant(pseudo, \"Default::default()\")})\n            },\n            % elif pseudo.argument == \"PtNameAndClassSelector\":\n            PseudoStyleType::${pseudo.capitalized} => functional_pseudo_parameter.map(|p| {\n                PseudoElement::${pseudo.capitalized}(PtNameAndClassSelector::from_name(p.0))\n            }),\n            % else:\n            <% assert pseudo.argument == \"AtomIdent\", f\"Unhandled argument type {pseudo.argument}\" %>\n            PseudoStyleType::${pseudo.capitalized} => {\n                functional_pseudo_parameter.map(PseudoElement::${pseudo.capitalized})\n            },\n            % endif\n            % endfor\n            _ => None,\n        }\n    }\n\n    /// Construct a `PseudoStyleType`.\n    #[inline]\n    pub fn pseudo_type(&self) -> PseudoStyleType {\n        // SAFETY: PseudoStyleType has the same variants as PseudoElement\n        unsafe { std::mem::transmute::<u8, PseudoStyleType>(self.discriminant()) }\n    }\n\n    /// Returns the relevant PseudoStyleType, and an atom as an argument, if any.\n    /// FIXME: we probably have to return the arguments of -moz-tree. However, they are multiple\n    /// names, so we skip them for now (until we really need them).\n    #[inline]\n    pub fn pseudo_type_and_argument(&self) -> (PseudoStyleType, Option<&Atom>) {\n        let ty = self.pseudo_type();\n        let arg = match *self {\n            % for pseudo in PSEUDOS:\n            % if pseudo.argument == \"PtNameAndClassSelector\":\n            PseudoElement::${pseudo.capitalized}(ref val) => Some(val.name()),\n            % elif pseudo.argument == \"AtomIdent\":\n            PseudoElement::${pseudo.capitalized}(ref val) => Some(&val.0),\n            % endif\n            % endfor\n            _ => None,\n        };\n        (ty, arg)\n    }\n\n    /// Get the argument list of a tree pseudo-element.\n    #[inline]\n    pub fn tree_pseudo_args(&self) -> &[Atom] {\n        match *self {\n            % for pseudo in PSEUDOS:\n            % if pseudo.name.startswith(\"-moz-tree-\"):\n            PseudoElement::${pseudo.capitalized}(ref args) => &args,\n            % endif\n            % endfor\n            _ => &[],\n        }\n    }\n\n    /// Constructs a pseudo-element from a string of text.\n    ///\n    /// Returns `None` if the pseudo-element is not recognised.\n    #[inline]\n    pub fn from_slice(name: &str, allow_unkown_webkit: bool) -> Option<Self> {\n        // We don't need to support tree pseudos because functional\n        // pseudo-elements needs arguments, and thus should be created\n        // via other methods.\n        cssparser::ascii_case_insensitive_phf_map! {\n            pseudo -> PseudoElement = {\n                % for pseudo in PSEUDOS:\n                % if not pseudo.argument:\n                \"${pseudo.name}\" => ${pseudo_element_variant(pseudo)},\n                % endif\n                % endfor\n                // Alias some legacy prefixed pseudos to their standardized name at parse time:\n                \"-moz-selection\" => PseudoElement::Selection,\n                \"-moz-placeholder\" => PseudoElement::Placeholder,\n                \"-moz-list-bullet\" => PseudoElement::Marker,\n                \"-moz-list-number\" => PseudoElement::Marker,\n            }\n        }\n        if let Some(p) = pseudo::get(name) {\n            return Some(p.clone());\n        }\n        if starts_with_ignore_ascii_case(name, \"-moz-tree-\") {\n            return PseudoElement::tree_pseudo_element(name, Default::default())\n        }\n        const WEBKIT_PREFIX: &str = \"-webkit-\";\n        if allow_unkown_webkit && starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {\n            let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);\n            return Some(PseudoElement::UnknownWebkit(part.into()));\n        }\n        None\n    }\n\n    /// Constructs a tree pseudo-element from the given name and arguments.\n    /// \"name\" must start with \"-moz-tree-\".\n    ///\n    /// Returns `None` if the pseudo-element is not recognized.\n    #[inline]\n    pub fn tree_pseudo_element(name: &str, args: thin_vec::ThinVec<Atom>) -> Option<Self> {\n        debug_assert!(starts_with_ignore_ascii_case(name, \"-moz-tree-\"));\n        let tree_part = &name[10..];\n        % for pseudo in PSEUDOS:\n        % if pseudo.name.startswith(\"-moz-tree-\"):\n        if tree_part.eq_ignore_ascii_case(\"${pseudo.name[10:]}\") {\n            return Some(${pseudo_element_variant(pseudo, \"args\")});\n        }\n        % endif\n        % endfor\n        None\n    }\n\n    /// Returns true if this pseudo-element matches the given selector.\n    pub fn matches(\n        &self,\n        pseudo_selector: &PseudoElement,\n        element: &super::wrapper::GeckoElement,\n    ) -> bool {\n        if *self == *pseudo_selector {\n            return true;\n        }\n\n        if std::mem::discriminant(self) != std::mem::discriminant(pseudo_selector) {\n            return false;\n        }\n\n        // Check named view transition pseudo-elements.\n        self.matches_named_view_transition_pseudo_element(pseudo_selector, element)\n    }\n}\n\nimpl ToCss for PseudoElement {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {\n        match *self {\n            % for pseudo in PSEUDOS:\n            % if pseudo.argument == \"AtomThinVec\":\n            ${pseudo_element_variant(pseudo, \"ref args\")} => {\n                dest.write_str(\"::${pseudo.name}\")?;\n                let mut iter = args.iter();\n                if let Some(first) = iter.next() {\n                    dest.write_char('(')?;\n                    serialize_atom_identifier(&first, dest)?;\n                    for item in iter {\n                        dest.write_str(\", \")?;\n                        serialize_atom_identifier(item, dest)?;\n                    }\n                    dest.write_char(')')?;\n                }\n                Ok(())\n            },\n            % elif pseudo.argument:\n                PseudoElement::${pseudo.capitalized}(ref arg) => {\n                    dest.write_str(\"::${pseudo.name}(\")?;\n                    arg.to_css(dest)?;\n                    dest.write_char(')')\n                }\n            % else:\n                ${pseudo_element_variant(pseudo)} => dest.write_str(\"::${pseudo.name}\"),\n            % endif\n            % endfor\n            PseudoElement::UnknownWebkit(ref atom) => {\n                dest.write_str(\"::-webkit-\")?;\n                serialize_atom_identifier(atom, dest)\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko/pseudo_elements.py",
    "content": "#!/usr/bin/env python\n\n# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nimport os\nimport sys\n\nimport toml\n\nTHIS_DIR = os.path.dirname(__file__)\nsys.path.insert(0, os.path.join(os.path.dirname(THIS_DIR), \"properties\"))\n\nimport data\n\n\nclass Pseudo(object):\n    def __init__(self, name, argument, enabled_in, pref):\n        self.name = name\n        self.camel = data.to_camel_case(name)\n        self.capitalized = self.camel[0].upper() + self.camel[1:]\n        self.argument = argument\n        self.enabled_in = enabled_in\n        self.pref = pref\n\n    def is_pseudo_element(self):\n        return isinstance(self, PseudoElement)\n\n    def is_anon_box(self):\n        return isinstance(self, AnonBox)\n\n    def is_non_inheriting_anon_box(self):\n        return isinstance(self, AnonBox) and not self.inherits\n\n    def is_inheriting_anon_box(self):\n        return isinstance(self, AnonBox) and self.inherits\n\n    def flags(self):\n        flags = []\n        if self.enabled_in == \"ua\":\n            flags.append(\"ENABLED_IN_UA\")\n        elif self.enabled_in == \"chrome\":\n            flags.append(\"ENABLED_IN_UA\")\n            flags.append(\"ENABLED_IN_CHROME\")\n        if self.pref:\n            flags.append(\"ENABLED_BY_PREF\")\n        if isinstance(self, PseudoElement):\n            flags.append(\"IS_PSEUDO_ELEMENT\")\n            if self.is_css2:\n                flags.append(\"IS_CSS2\")\n            if self.is_eager:\n                flags.append(\"IS_EAGER\")\n            if self.is_js_created_nac:\n                flags.append(\"IS_JS_CREATED_NAC\")\n            if self.is_flex_or_grid_item:\n                flags.append(\"IS_FLEX_OR_GRID_ITEM\")\n            if self.is_element_backed:\n                flags.append(\"IS_ELEMENT_BACKED\")\n            if self.is_tree_abiding:\n                flags.append(\"IS_TREE_ABIDING\")\n            if self.parses_as_element_backed:\n                flags.append(\"PARSES_AS_ELEMENT_BACKED\")\n            if self.supports_user_action_state:\n                flags.append(\"SUPPORTS_USER_ACTION_STATE\")\n        if isinstance(self, AnonBox):\n            if self.inherits:\n                flags.append(\"IS_INHERITING_ANON_BOX\")\n            else:\n                flags.append(\"IS_NON_INHERITING_ANON_BOX\")\n            if self.wrapper:\n                flags.append(\"IS_WRAPPER_ANON_BOX\")\n        return flags\n\n\nclass AnonBox(Pseudo):\n    def __init__(self, name, wrapper=False, inherits=True):\n        super().__init__(name, argument=None, enabled_in=\"ua\", pref=None)\n        self.wrapper = wrapper\n        self.inherits = inherits\n\n\nclass PseudoElement(Pseudo):\n    def __init__(\n        self,\n        name,\n        enabled_in=\"content\",\n        is_css2=False,\n        is_eager=False,\n        is_js_created_nac=False,\n        is_flex_or_grid_item=False,\n        is_element_backed=False,\n        is_tree_abiding=False,\n        parses_as_element_backed=None,\n        supports_user_action_state=False,\n        pref=None,\n        argument=None,\n    ):\n        super().__init__(name, argument=argument, enabled_in=enabled_in, pref=pref)\n        self.is_css2 = is_css2\n        self.is_eager = is_eager\n        self.is_js_created_nac = is_js_created_nac\n        self.is_flex_or_grid_item = is_flex_or_grid_item\n        self.is_element_backed = is_element_backed\n        self.is_tree_abiding = is_element_backed or is_tree_abiding\n        self.parses_as_element_backed = (\n            parses_as_element_backed\n            if parses_as_element_backed is not None\n            else is_element_backed\n        )\n        self.supports_user_action_state = supports_user_action_state\n\n\nclass PseudoElementData:\n    def __init__(self):\n        this_dir = os.path.dirname(__file__)\n        pseudo_elements_toml = os.path.join(this_dir, \"pseudo_elements.toml\")\n        anon_boxes_toml = os.path.join(this_dir, \"anon_boxes.toml\")\n        self.anon_boxes = sorted(\n            (\n                AnonBox(name, **val)\n                for name, val in toml.loads(open(anon_boxes_toml).read()).items()\n            ),\n            key=lambda n: n.inherits,\n        )\n        self.pseudo_elements = [\n            PseudoElement(name, **val)\n            for name, val in toml.loads(open(pseudo_elements_toml).read()).items()\n        ]\n        self.path_dependencies = [pseudo_elements_toml, anon_boxes_toml]\n\n    def all_pseudos(self):\n        return self.anon_boxes + self.pseudo_elements\n"
  },
  {
    "path": "style/gecko/pseudo_elements.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n#\n# This file contains the list of support CSS pseudo-elements and some flags.\n#\n# Each pseudo element has its own section, with the following keys:\n#\n#   enabled_in - Same semantics as longhands.toml\n#   is_css2 - Default false, whether this is a css2 pseudo-element, and can be\n#             parsed only with one colon.\n#   is_eager - Whether the pseudo-element is eagerly cascaded by the style\n#              system during traversal. This should generally only be used for\n#              pseudo-elements that apply to most elements.\n#   is_js_created_nac - True if this pseudo-element can be created by JS.\n#   is_flex_or_grid_item - True if display item fixup for flex / grid should\n#                          happen for this pseudo\n#   is_element_backed - whether this is an element-backed pseudo-element\n#                       https://drafts.csswg.org/css-pseudo-4/#element-like\n#   supports_user_action_state - true if this pseudo supports :hover etc.\n#   argument - If provided, the argument this functional pseudo takes.\n#   pref - Same semantics as gecko_pref in longhands.toml\n#\n# When adding a pseudo-element, you also need to add the relevant atom to\n# StaticAtoms.py, with the camel-case name of the pseudo-element.\n[after]\nis_css2 = true\nis_eager = true\nis_flex_or_grid_item = true\nis_tree_abiding = true\n[before]\nis_css2 = true\nis_eager = true\nis_flex_or_grid_item = true\nis_tree_abiding = true\n[marker]\nis_tree_abiding = true\n[backdrop]\n[cue]\nis_js_created_nac = true\n[first-letter]\nis_css2 = true\nis_eager = true\n[first-line]\nis_css2 = true\nis_eager = true\n[highlight]\nargument = \"AtomIdent\"\n[selection]\n[target-text]\n[view-transition]\nenabled_in = \"ua\"\npref = \"dom.viewTransitions.enabled\"\n[view-transition-group]\nenabled_in = \"ua\"\npref = \"dom.viewTransitions.enabled\"\nargument = \"PtNameAndClassSelector\"\nis_element_backed = true\n[view-transition-image-pair]\nenabled_in = \"ua\"\npref = \"dom.viewTransitions.enabled\"\nis_element_backed = true\nargument = \"PtNameAndClassSelector\"\n[view-transition-old]\nenabled_in = \"ua\"\npref = \"dom.viewTransitions.enabled\"\nis_element_backed = true\nargument = \"PtNameAndClassSelector\"\n[view-transition-new]\nenabled_in = \"ua\"\npref = \"dom.viewTransitions.enabled\"\nis_element_backed = true\nargument = \"PtNameAndClassSelector\"\n# The internal implementation usage for View transition to create the snapshot\n# containing block concept.\n[-moz-snapshot-containing-block]\nenabled_in = \"ua\"\n[picker]\nenabled_in = \"ua\"\npref = \"dom.select.customizable_select.enabled\"\nargument = \"AtomIdent\"\nis_element_backed = true\n\n# HTML5 Forms pseudo elements\n[-moz-number-spin-box]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\n[-moz-number-spin-down]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\n[-moz-number-spin-up]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\n[-moz-search-clear-button]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\n# The selected label of a <select>\n[-moz-select-content]\nenabled_in = \"chrome\"\nis_element_backed = true\n# The menulist button of a <select>\n[picker-icon]\nenabled_in = \"chrome\"\npref = \"dom.select.customizable_select.enabled\"\nis_element_backed = true\n# We represent ::picker-icon with a real element, but per spec it should parse\n# as a regular generated content pseudo-element.\nparses_as_element_backed = false\n[-moz-progress-bar]\nsupports_user_action_state = true\n[-moz-range-track]\nsupports_user_action_state = true\n[-moz-range-progress]\nsupports_user_action_state = true\n[-moz-range-thumb]\nsupports_user_action_state = true\n[-moz-meter-bar]\nsupports_user_action_state = true\n[placeholder]\nsupports_user_action_state = true\nis_element_backed = true\nparses_as_element_backed = false\n[-moz-color-swatch]\nsupports_user_action_state = true\n# The root of the text value anonymous content inside an <input> or <textarea>.\n[-moz-text-control-editing-root]\nenabled_in = \"ua\"\nis_element_backed = true\n# The element that shows the autofill value.\n[-moz-text-control-preview]\nenabled_in = \"ua\"\nis_element_backed = true\n# The Reveal Password button for <input type=password>.\n[-moz-reveal]\nsupports_user_action_state = true\nenabled_in = \"ua\"\n# The button in an <input type=file>\n[file-selector-button]\nsupports_user_action_state = true\nis_tree_abiding = true\n# The cropped label in a file input.\n[-moz-file-content]\nenabled_in = \"chrome\"\n# Standard progress/meter/range pseudo-elements.\n[slider-track]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\npref = \"layout.css.modern-range-pseudos.enabled\"\n[slider-thumb]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\npref = \"layout.css.modern-range-pseudos.enabled\"\n[slider-fill]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\npref = \"layout.css.modern-range-pseudos.enabled\"\n# The content in a <details> element that is shown when the element is open.\n[details-content]\nsupports_user_action_state = true\nenabled_in = \"chrome\"\npref = \"layout.css.details-content.enabled\"\nis_element_backed = true\n[-webkit-scrollbar]\npref = \"layout.css.fake-webkit-scrollbar.enabled\"\n\n# Tree pseudo-elements\n[-moz-tree-column]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-row]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-separator]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-cell]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-indentation]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-line]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-twisty]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-image]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-cell-text]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-checkbox]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n[-moz-tree-drop-feedback]\nenabled_in = \"chrome\"\nargument = \"AtomThinVec\"\n"
  },
  {
    "path": "style/gecko/regen_atoms.py",
    "content": "#!/usr/bin/env python\n\n# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nimport re\nimport os\nimport sys\n\nfrom io import BytesIO\nfrom pseudo_elements import PseudoElementData\n\nimport build\n\n\n# Matches lines like `GK_ATOM(foo, \"foo\", 0x12345678, true)`.\nPATTERN = re.compile(\n    r'^GK_ATOM\\(([^,]*),[^\"]*\"([^\"]*)\",\\s*(0x[0-9a-f]+),\\s*[^,]*\\)',\n    re.MULTILINE,\n)\nFILE = \"include/nsGkAtomList.h\"\n\n\ndef map_atom(ident):\n    if ident in {\n        \"box\",\n        \"loop\",\n        \"match\",\n        \"mod\",\n        \"ref\",\n        \"self\",\n        \"type\",\n        \"use\",\n        \"where\",\n        \"in\",\n    }:\n        return ident + \"_\"\n    return ident\n\n\nclass Atom:\n    def __init__(self, ident, value, hash):\n        self.ident = \"nsGkAtoms_{}\".format(ident)\n        self.original_ident = ident\n        self.value = value\n        self.hash = hash\n\n\ndef collect_atoms(objdir):\n    atoms = []\n    path = os.path.abspath(os.path.join(objdir, FILE))\n    print(\"cargo:rerun-if-changed={}\".format(path))\n    with open(path) as f:\n        content = f.read()\n        for result in PATTERN.finditer(content):\n            atoms.append(\n                Atom(\n                    result.group(1),\n                    result.group(2),\n                    result.group(3),\n                )\n            )\n    return atoms\n\n\nclass FileAvoidWrite(BytesIO):\n    \"\"\"File-like object that buffers output and only writes if content changed.\"\"\"\n\n    def __init__(self, filename):\n        BytesIO.__init__(self)\n        self.name = filename\n\n    def write(self, buf):\n        if isinstance(buf, str):\n            buf = buf.encode(\"utf-8\")\n        BytesIO.write(self, buf)\n\n    def close(self):\n        buf = self.getvalue()\n        BytesIO.close(self)\n        try:\n            with open(self.name, \"rb\") as f:\n                old_content = f.read()\n                if old_content == buf:\n                    print(\"{} is not changed, skip\".format(self.name))\n                    return\n        except IOError:\n            pass\n        with open(self.name, \"wb\") as f:\n            f.write(buf)\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, type, value, traceback):\n        if not self.closed:\n            self.close()\n\n\nPRELUDE = \"\"\"\n/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n// Autogenerated file created by components/style/gecko/regen_atoms.py.\n// DO NOT EDIT DIRECTLY\n\"\"\"[\n    1:\n]\n\nRULE_TEMPLATE = \"\"\"\n    (\"{atom}\") => {{{{\n        #[allow(unsafe_code)] #[allow(unused_unsafe)]\n        unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}\n    }}}};\n\"\"\"[\n    1:\n]\n\nMACRO_TEMPLATE = \"\"\"\n/// Returns a static atom by passing the literal string it represents.\n#[macro_export]\nmacro_rules! atom {{\n{body}\\\n}}\n\"\"\"\n\n\ndef write_atom_macro(atoms, file_name):\n    with FileAvoidWrite(file_name) as f:\n        f.write(PRELUDE)\n        macro_rules = [\n            RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i)\n            for (i, atom) in enumerate(atoms)\n        ]\n        f.write(MACRO_TEMPLATE.format(body=\"\".join(macro_rules)))\n\n\ndef generate_atoms(dist, out):\n    atoms = collect_atoms(dist)\n    write_atom_macro(atoms, os.path.join(out, \"atom_macro.rs\"))\n\n\ndef generate_pseudo_elements(dist, out):\n    data = PseudoElementData()\n    pseudo_definition_template = os.path.join(\n        os.path.dirname(__file__), \"pseudo_element_definition.mako.rs\"\n    )\n\n    print(f\"cargo:rerun-if-changed={pseudo_definition_template}\")\n    for f in data.path_dependencies:\n        print(f\"cargo:rerun-if-changed={f}\")\n\n    target = os.path.join(out, \"pseudo_element_definition.rs\")\n    with FileAvoidWrite(target) as f:\n        f.write(build.render(pseudo_definition_template, PSEUDOS=data.all_pseudos()))\n\n\nif __name__ == \"__main__\":\n    if len(sys.argv) != 3:\n        print(\"Usage: {} dist out\".format(sys.argv[0]))\n        exit(2)\n    dist = sys.argv[1]\n    out = sys.argv[2]\n    generate_atoms(dist, out)\n    generate_pseudo_elements(dist, out)\n"
  },
  {
    "path": "style/gecko/restyle_damage.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko's restyle damage computation (aka change hints, aka `nsChangeHint`).\n\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs;\nuse crate::gecko_bindings::structs::nsChangeHint;\nuse crate::matching::{StyleChange, StyleDifference};\nuse crate::properties::ComputedValues;\nuse std::ops::{BitAnd, BitOr, BitOrAssign, Not};\n\n/// The representation of Gecko's restyle damage is just a wrapper over\n/// `nsChangeHint`.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub struct GeckoRestyleDamage(nsChangeHint);\n\nimpl GeckoRestyleDamage {\n    /// Trivially construct a new `GeckoRestyleDamage`.\n    #[inline]\n    pub fn new(raw: nsChangeHint) -> Self {\n        GeckoRestyleDamage(raw)\n    }\n\n    /// Get the inner change hint for this damage.\n    #[inline]\n    pub fn as_change_hint(&self) -> nsChangeHint {\n        self.0\n    }\n\n    /// Get an empty change hint, that is (`nsChangeHint(0)`).\n    #[inline]\n    pub fn empty() -> Self {\n        GeckoRestyleDamage(nsChangeHint(0))\n    }\n\n    /// Returns whether this restyle damage represents the empty damage.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.0 == nsChangeHint(0)\n    }\n\n    /// Computes the `StyleDifference` (including the appropriate change hint)\n    /// given an old and a new style.\n    pub fn compute_style_difference(\n        old_style: &ComputedValues,\n        new_style: &ComputedValues,\n    ) -> StyleDifference {\n        let mut any_style_changed = false;\n        let mut reset_only = false;\n        let hint = unsafe {\n            bindings::Gecko_CalcStyleDifference(\n                old_style.as_gecko_computed_style(),\n                new_style.as_gecko_computed_style(),\n                &mut any_style_changed,\n                &mut reset_only,\n            )\n        };\n        // Gecko_CalcStyleDamage only deals with non-custom properties.\n        let old_custom_props = old_style.custom_properties();\n        let new_custom_props = new_style.custom_properties();\n        let mut custom_properties_changed = false;\n        if old_custom_props.inherited != new_custom_props.inherited {\n            any_style_changed = true;\n            custom_properties_changed = true;\n            reset_only = false;\n        } else if old_custom_props.non_inherited != new_custom_props.non_inherited {\n            any_style_changed = true;\n            custom_properties_changed = true;\n        };\n        let change = if any_style_changed {\n            StyleChange::Changed {\n                reset_only,\n                custom_properties_changed,\n            }\n        } else {\n            StyleChange::Unchanged\n        };\n        let damage = GeckoRestyleDamage(nsChangeHint(hint));\n        StyleDifference { damage, change }\n    }\n\n    /// Returns true if this restyle damage contains all the damage of |other|.\n    pub fn contains(self, other: Self) -> bool {\n        self & other == other\n    }\n\n    /// Gets restyle damage to reconstruct the entire frame, subsuming all\n    /// other damage.\n    pub fn reconstruct() -> Self {\n        GeckoRestyleDamage(structs::nsChangeHint::nsChangeHint_ReconstructFrame)\n    }\n}\n\nimpl Default for GeckoRestyleDamage {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nimpl BitOr for GeckoRestyleDamage {\n    type Output = Self;\n    fn bitor(self, other: Self) -> Self {\n        GeckoRestyleDamage(self.0 | other.0)\n    }\n}\n\nimpl BitOrAssign for GeckoRestyleDamage {\n    fn bitor_assign(&mut self, other: Self) {\n        *self = *self | other;\n    }\n}\n\nimpl BitAnd for GeckoRestyleDamage {\n    type Output = Self;\n    fn bitand(self, other: Self) -> Self {\n        GeckoRestyleDamage(nsChangeHint((self.0).0 & (other.0).0))\n    }\n}\n\nimpl Not for GeckoRestyleDamage {\n    type Output = Self;\n    fn not(self) -> Self {\n        GeckoRestyleDamage(nsChangeHint(!(self.0).0))\n    }\n}\n"
  },
  {
    "path": "style/gecko/selector_parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko-specific bits for selector-parsing.\n\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::derives::*;\nuse crate::invalidation::element::document_state::InvalidationMatchingData;\nuse crate::properties::ComputedValues;\nuse crate::selector_parser::{Direction, HorizontalDirection, SelectorParser};\nuse crate::str::starts_with_ignore_ascii_case;\nuse crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};\nuse crate::values::{AtomIdent, AtomString, CSSInteger, CustomIdent};\nuse cssparser::{match_ignore_ascii_case, CowRcStr, SourceLocation, ToCss, Token};\nuse cssparser::{BasicParseError, BasicParseErrorKind, Parser};\nuse dom::{DocumentState, ElementState, HEADING_LEVEL_OFFSET};\nuse selectors::parser::SelectorParseErrorKind;\nuse std::fmt;\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};\nuse thin_vec::ThinVec;\n\npub use crate::gecko::pseudo_element::{\n    PseudoElement, Target, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,\n};\npub use crate::gecko::snapshot::SnapshotMap;\n\nbitflags! {\n    // See NonTSPseudoClass::is_enabled_in()\n    #[derive(Copy, Clone)]\n    struct NonTSPseudoClassFlag: u8 {\n        const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS = 1 << 0;\n        const PSEUDO_CLASS_ENABLED_IN_CHROME = 1 << 1;\n        const PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME =\n            NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS.bits() |\n            NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME.bits();\n    }\n}\n\n/// The type used to store the language argument to the `:lang` pseudo-class.\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[css(comma)]\npub struct Lang(#[css(iterable)] pub ThinVec<AtomIdent>);\n\n/// The type used to store the state argument to the `:state` pseudo-class.\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub struct CustomState(pub AtomIdent);\n\n/// The properties that comprise a :heading() pseudoclass (e.g. a list of An+Bs).\n/// https://drafts.csswg.org/selectors-5/#headings\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub struct HeadingSelectorData(pub ThinVec<CSSInteger>);\n\nimpl HeadingSelectorData {\n    /// Matches the heading level from the given state against the list of\n    /// heading level selectors. If AnPlusBs intersect with the level packed in\n    /// ElementState then this will return true.\n    pub fn matches_state(&self, state: ElementState) -> bool {\n        let bits = state.intersection(ElementState::HEADING_LEVEL_BITS).bits();\n        // If none of the HEADING_LEVEL_BITS are set on ElementState,\n        // then this is not a heading level, so return false.\n        if bits == 0 {\n            return false;\n        }\n        // :heading selector will provide an empty levels list. It matches against any\n        // heading level, so we can return true here.\n        if self.0.is_empty() {\n            return true;\n        }\n        let level = (bits >> HEADING_LEVEL_OFFSET) as i32;\n        debug_assert!(level > 0 && level < 16);\n        self.0.iter().any(|item| *item == level)\n    }\n}\n\nmacro_rules! pseudo_class_name {\n    ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n        /// Our representation of a non tree-structural pseudo-class.\n        #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\n        pub enum NonTSPseudoClass {\n            $(\n                #[doc = $css]\n                $name,\n            )*\n            /// The `:lang` pseudo-class.\n            Lang(Lang),\n            /// The `:dir` pseudo-class.\n            Dir(Direction),\n            /// The :state` pseudo-class.\n            CustomState(CustomState),\n            /// The `:heading` & `:heading()` pseudo-classes.\n            Heading(HeadingSelectorData),\n            /// The :active-view-transition-type() pseudo-class:\n            /// https://drafts.csswg.org/css-view-transitions-2/#the-active-view-transition-type-pseudo\n            ActiveViewTransitionType(ThinVec<CustomIdent>),\n            /// The non-standard `:-moz-locale-dir` pseudo-class.\n            MozLocaleDir(Direction),\n        }\n    }\n}\napply_non_ts_list!(pseudo_class_name);\n\nimpl ToCss for NonTSPseudoClass {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        macro_rules! pseudo_class_serialize {\n            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n                match *self {\n                    $(NonTSPseudoClass::$name => concat!(\":\", $css),)*\n                    NonTSPseudoClass::Lang(ref lang) => {\n                        dest.write_str(\":lang(\")?;\n                        lang.to_css(&mut CssWriter::new(dest))?;\n                        return dest.write_char(')');\n                    },\n                    NonTSPseudoClass::CustomState(ref state) => {\n                        dest.write_str(\":state(\")?;\n                        state.to_css(&mut CssWriter::new(dest))?;\n                        return dest.write_char(')');\n                    },\n                    NonTSPseudoClass::MozLocaleDir(ref dir) => {\n                        dest.write_str(\":-moz-locale-dir(\")?;\n                        dir.to_css(&mut CssWriter::new(dest))?;\n                        return dest.write_char(')')\n                    },\n                    NonTSPseudoClass::Dir(ref dir) => {\n                        dest.write_str(\":dir(\")?;\n                        dir.to_css(&mut CssWriter::new(dest))?;\n                        return dest.write_char(')')\n                    },\n                    NonTSPseudoClass::ActiveViewTransitionType(ref types) => {\n                        dest.write_str(\":active-view-transition-type(\")?;\n                        let mut first = true;\n                        for ty in types.iter() {\n                            if !first {\n                                dest.write_str(\", \")?;\n                            }\n                            first = false;\n                            ty.to_css(&mut CssWriter::new(dest))?;\n                        }\n                        return dest.write_char(')')\n                    },\n                    NonTSPseudoClass::Heading(ref levels) => {\n                        dest.write_str(\":heading\")?;\n                        if levels.0.is_empty() {\n                            return Ok(());\n                        }\n                        dest.write_str(\"(\")?;\n                        let mut first = true;\n                        for item in levels.0.iter() {\n                            if !first {\n                                dest.write_str(\", \")?;\n                            }\n                            first = false;\n                            ToCss::to_css(item, dest)?;\n                        }\n                        return dest.write_str(\")\");\n                    },\n                }\n            }\n        }\n        let ser = apply_non_ts_list!(pseudo_class_serialize);\n        dest.write_str(ser)\n    }\n}\n\nimpl NonTSPseudoClass {\n    /// Parses the name and returns a non-ts-pseudo-class if succeeds.\n    /// None otherwise. It doesn't check whether the pseudo-class is enabled\n    /// in a particular state.\n    pub fn parse_non_functional(name: &str) -> Option<Self> {\n        macro_rules! pseudo_class_parse {\n            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n                match_ignore_ascii_case! { &name,\n                    $($css => Some(NonTSPseudoClass::$name),)*\n                    \"heading\" => Some(NonTSPseudoClass::Heading(HeadingSelectorData([].into()))),\n                    \"-moz-full-screen\" => Some(NonTSPseudoClass::Fullscreen),\n                    \"-moz-read-only\" => Some(NonTSPseudoClass::ReadOnly),\n                    \"-moz-read-write\" => Some(NonTSPseudoClass::ReadWrite),\n                    \"-moz-focusring\" => Some(NonTSPseudoClass::FocusVisible),\n                    \"-moz-ui-valid\" => Some(NonTSPseudoClass::UserValid),\n                    \"-moz-ui-invalid\" => Some(NonTSPseudoClass::UserInvalid),\n                    \"-webkit-autofill\" => Some(NonTSPseudoClass::Autofill),\n                    _ => None,\n                }\n            }\n        }\n        apply_non_ts_list!(pseudo_class_parse)\n    }\n\n    /// Returns true if this pseudo-class has any of the given flags set.\n    fn has_any_flag(&self, flags: NonTSPseudoClassFlag) -> bool {\n        macro_rules! check_flag {\n            (_) => {\n                false\n            };\n            ($flags:ident) => {\n                NonTSPseudoClassFlag::$flags.intersects(flags)\n            };\n        }\n        macro_rules! pseudo_class_check_is_enabled_in {\n            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n                match *self {\n                    $(NonTSPseudoClass::$name => check_flag!($flags),)*\n                    NonTSPseudoClass::MozLocaleDir(_) => check_flag!(PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),\n                    NonTSPseudoClass::CustomState(_) |\n                    NonTSPseudoClass::Heading(_) |\n                    NonTSPseudoClass::Lang(_) |\n                    NonTSPseudoClass::ActiveViewTransitionType(_) |\n                    NonTSPseudoClass::Dir(_) => false,\n                }\n            }\n        }\n        apply_non_ts_list!(pseudo_class_check_is_enabled_in)\n    }\n\n    /// Returns whether the pseudo-class is enabled in content sheets.\n    #[inline]\n    fn is_enabled_in_content(&self) -> bool {\n        if matches!(\n            *self,\n            Self::ActiveViewTransition | Self::ActiveViewTransitionType(..)\n        ) {\n            return static_prefs::pref!(\"dom.viewTransitions.enabled\");\n        }\n        if matches!(*self, Self::Heading(..)) {\n            return static_prefs::pref!(\"layout.css.heading-selector.enabled\");\n        }\n        if matches!(\n            *self,\n            Self::Playing\n                | Self::Paused\n                | Self::Seeking\n                | Self::Buffering\n                | Self::Stalled\n                | Self::Muted\n                | Self::VolumeLocked\n        ) {\n            return static_prefs::pref!(\"dom.media.pseudo-classes.enabled\");\n        }\n        !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)\n    }\n\n    /// Get the state flag associated with a pseudo-class, if any.\n    pub fn state_flag(&self) -> ElementState {\n        macro_rules! flag {\n            (_) => {\n                ElementState::empty()\n            };\n            ($state:ident) => {\n                ElementState::$state\n            };\n        }\n        macro_rules! pseudo_class_state {\n            ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {\n                match *self {\n                    $(NonTSPseudoClass::$name => flag!($state),)*\n                    NonTSPseudoClass::Dir(ref dir) => dir.element_state(),\n                    NonTSPseudoClass::Heading(..) => ElementState::HEADING_LEVEL_BITS,\n                    NonTSPseudoClass::ActiveViewTransitionType(..) => ElementState::ACTIVE_VIEW_TRANSITION,\n                    NonTSPseudoClass::MozLocaleDir(..) |\n                    NonTSPseudoClass::CustomState(..) |\n                    NonTSPseudoClass::Lang(..) => ElementState::empty(),\n                }\n            }\n        }\n        apply_non_ts_list!(pseudo_class_state)\n    }\n\n    /// Get the document state flag associated with a pseudo-class, if any.\n    pub fn document_state_flag(&self) -> DocumentState {\n        match *self {\n            NonTSPseudoClass::MozLocaleDir(ref dir) => match dir.as_horizontal_direction() {\n                Some(HorizontalDirection::Ltr) => DocumentState::LTR_LOCALE,\n                Some(HorizontalDirection::Rtl) => DocumentState::RTL_LOCALE,\n                None => DocumentState::empty(),\n            },\n            NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE,\n            _ => DocumentState::empty(),\n        }\n    }\n\n    /// Returns true if the given pseudoclass should trigger style sharing cache\n    /// revalidation.\n    pub fn needs_cache_revalidation(&self) -> bool {\n        self.state_flag().is_empty()\n            && !matches!(\n                *self,\n                // :dir() depends on state only, but may have an empty state_flag for invalid\n                // arguments.\n                NonTSPseudoClass::Dir(_) |\n                      // We prevent style sharing for NAC.\n                      NonTSPseudoClass::MozNativeAnonymous |\n                      // :-moz-placeholder is parsed but never matches.\n                      NonTSPseudoClass::MozPlaceholder |\n                      // :-moz-is-html, :-moz-locale-dir and :-moz-window-inactive\n                      // depend only on the state of the document, which is invariant across all\n                      // elements involved in a given style cache.\n                      NonTSPseudoClass::MozIsHTML |\n                      NonTSPseudoClass::MozLocaleDir(_) |\n                      NonTSPseudoClass::MozWindowInactive\n            )\n    }\n}\n\nimpl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {\n    type Impl = SelectorImpl;\n\n    #[inline]\n    fn is_active_or_hover(&self) -> bool {\n        matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)\n    }\n\n    /// We intentionally skip the link-related ones.\n    #[inline]\n    fn is_user_action_state(&self) -> bool {\n        matches!(\n            *self,\n            NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus\n        )\n    }\n}\n\n/// The dummy struct we use to implement our selector parsing.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct SelectorImpl;\n\n/// A set of extra data to carry along with the matching context, either for\n/// selector-matching or invalidation.\n#[derive(Default)]\npub struct ExtraMatchingData<'a> {\n    /// The invalidation data to invalidate doc-state pseudo-classes correctly.\n    pub invalidation_data: InvalidationMatchingData,\n\n    /// The invalidation bits from matching container queries. These are here\n    /// just for convenience mostly.\n    pub cascade_input_flags: ComputedValueFlags,\n\n    /// The style of the originating element in order to evaluate @container\n    /// size queries affecting pseudo-elements.\n    pub originating_element_style: Option<&'a ComputedValues>,\n}\n\nimpl ::selectors::SelectorImpl for SelectorImpl {\n    type ExtraMatchingData<'a> = ExtraMatchingData<'a>;\n    type AttrValue = AtomString;\n    type Identifier = AtomIdent;\n    type LocalName = AtomIdent;\n    type NamespacePrefix = AtomIdent;\n    type NamespaceUrl = Namespace;\n    type BorrowedNamespaceUrl = WeakNamespace;\n    type BorrowedLocalName = WeakAtom;\n\n    type PseudoElement = PseudoElement;\n    type NonTSPseudoClass = NonTSPseudoClass;\n\n    fn should_collect_attr_hash(name: &AtomIdent) -> bool {\n        !crate::bloom::is_attr_name_excluded_from_filter(name)\n    }\n}\n\nimpl<'a> SelectorParser<'a> {\n    fn is_pseudo_class_enabled(&self, pseudo_class: &NonTSPseudoClass) -> bool {\n        if pseudo_class.is_enabled_in_content() {\n            return true;\n        }\n\n        if self.in_user_agent_stylesheet()\n            && pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS)\n        {\n            return true;\n        }\n\n        if self.chrome_rules_enabled()\n            && pseudo_class.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_CHROME)\n        {\n            return true;\n        }\n\n        if matches!(*pseudo_class, NonTSPseudoClass::MozBroken) {\n            return static_prefs::pref!(\"layout.css.moz-broken.content.enabled\");\n        }\n\n        return false;\n    }\n\n    fn is_pseudo_element_enabled(&self, pseudo_element: &PseudoElement) -> bool {\n        if pseudo_element.enabled_in_content() {\n            return true;\n        }\n\n        if self.in_user_agent_stylesheet() && pseudo_element.enabled_in_ua_sheets() {\n            return true;\n        }\n\n        if self.chrome_rules_enabled() && pseudo_element.enabled_in_chrome() {\n            return true;\n        }\n\n        return false;\n    }\n}\n\n/// Parse the functional pseudo-element with the function name.\npub fn parse_functional_pseudo_element_with_name<'i, 't>(\n    name: &CowRcStr<'i>,\n    parser: &mut Parser<'i, 't>,\n    target: Target,\n) -> Result<PseudoElement, ParseError<'i>> {\n    use crate::gecko::pseudo_element::PtNameAndClassSelector;\n\n    if matches!(target, Target::Selector)\n        && starts_with_ignore_ascii_case(name.as_ref(), \"-moz-tree-\")\n    {\n        // Tree pseudo-elements can have zero or more arguments, separated\n        // by either comma or space.\n        let mut args = ThinVec::new();\n        loop {\n            let location = parser.current_source_location();\n            match parser.next() {\n                Ok(&Token::Ident(ref ident)) => args.push(Atom::from(ident.as_ref())),\n                Ok(&Token::Comma) => {},\n                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),\n                Err(BasicParseError {\n                    kind: BasicParseErrorKind::EndOfInput,\n                    ..\n                }) => break,\n                _ => unreachable!(\"Parser::next() shouldn't return any other error\"),\n            }\n        }\n        return PseudoElement::tree_pseudo_element(name.as_ref(), args).ok_or(\n            parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name.clone(),\n            )),\n        );\n    }\n\n    Ok(match_ignore_ascii_case! { &name,\n        \"highlight\" => PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref())),\n        \"picker\" => {\n            let picker_element = parser.expect_ident()?.as_ref();\n            if !picker_element.eq_ignore_ascii_case(\"select\") {\n                return Err(parser.new_custom_error(\n                    SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()),\n                ));\n            }\n            // Don't use the actual ident, because it is not always all lowercase.\n            PseudoElement::Picker(AtomIdent(atom!(\"select\")))\n        },\n        \"view-transition-group\" => {\n            PseudoElement::ViewTransitionGroup(PtNameAndClassSelector::parse(parser, target)?)\n        },\n        \"view-transition-image-pair\" => {\n            PseudoElement::ViewTransitionImagePair(PtNameAndClassSelector::parse(parser, target)?)\n        },\n        \"view-transition-old\" => {\n            PseudoElement::ViewTransitionOld(PtNameAndClassSelector::parse(parser, target)?)\n        },\n        \"view-transition-new\" => {\n            PseudoElement::ViewTransitionNew(PtNameAndClassSelector::parse(parser, target)?)\n        },\n        _ => {\n            return Err(parser.new_custom_error(\n                SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()),\n            ));\n        },\n    })\n}\n\nimpl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {\n    type Impl = SelectorImpl;\n    type Error = StyleParseErrorKind<'i>;\n\n    #[inline]\n    fn parse_parent_selector(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_slotted(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_host(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_nth_child_of(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_is_and_where(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_has(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_part(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn is_is_alias(&self, function: &str) -> bool {\n        function.eq_ignore_ascii_case(\"-moz-any\")\n    }\n\n    #[inline]\n    fn allow_forgiving_selectors(&self) -> bool {\n        !self.for_supports_rule\n    }\n\n    fn parse_non_ts_pseudo_class(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<NonTSPseudoClass, ParseError<'i>> {\n        if let Some(pseudo_class) = NonTSPseudoClass::parse_non_functional(&name) {\n            if self.is_pseudo_class_enabled(&pseudo_class) {\n                return Ok(pseudo_class);\n            }\n        }\n        Err(\n            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn parse_non_ts_functional_pseudo_class<'t>(\n        &self,\n        name: CowRcStr<'i>,\n        parser: &mut Parser<'i, 't>,\n        _after_part: bool,\n    ) -> Result<NonTSPseudoClass, ParseError<'i>> {\n        let pseudo_class = match_ignore_ascii_case! { &name,\n            \"lang\" => {\n                let result = parser.parse_comma_separated(|input| {\n                    Ok(AtomIdent::from(input.expect_ident_or_string()?.as_ref()))\n                })?;\n                if result.is_empty() {\n                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                NonTSPseudoClass::Lang(Lang(result.into()))\n            },\n            \"state\" => {\n                let result = AtomIdent::from(parser.expect_ident()?.as_ref());\n                NonTSPseudoClass::CustomState(CustomState(result))\n            },\n            \"heading\" => {\n                let result = parser.parse_comma_separated(|input| Ok(input.expect_integer()?))?;\n                if result.is_empty() {\n                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                NonTSPseudoClass::Heading(HeadingSelectorData(result.into()))\n            },\n            \"active-view-transition-type\" => {\n                let result = parser.parse_comma_separated(|input| CustomIdent::parse(input, &[]))?;\n                if result.is_empty() {\n                    return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                NonTSPseudoClass::ActiveViewTransitionType(result.into())\n            },\n            \"-moz-locale-dir\" => {\n                NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)\n            },\n            \"dir\" => {\n                NonTSPseudoClass::Dir(Direction::parse(parser)?)\n            },\n            _ => return Err(parser.new_custom_error(\n                SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())\n            ))\n        };\n        if self.is_pseudo_class_enabled(&pseudo_class) {\n            Ok(pseudo_class)\n        } else {\n            Err(\n                parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                    name,\n                )),\n            )\n        }\n    }\n\n    fn parse_pseudo_element(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<PseudoElement, ParseError<'i>> {\n        let allow_unkown_webkit = !self.for_supports_rule;\n        if let Some(pseudo) = PseudoElement::from_slice(&name, allow_unkown_webkit) {\n            if self.is_pseudo_element_enabled(&pseudo) {\n                return Ok(pseudo);\n            }\n        }\n\n        Err(\n            location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn parse_functional_pseudo_element<'t>(\n        &self,\n        name: CowRcStr<'i>,\n        parser: &mut Parser<'i, 't>,\n    ) -> Result<PseudoElement, ParseError<'i>> {\n        let pseudo = parse_functional_pseudo_element_with_name(&name, parser, Target::Selector)?;\n        if self.is_pseudo_element_enabled(&pseudo) {\n            return Ok(pseudo);\n        }\n\n        Err(\n            parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(\n                name,\n            )),\n        )\n    }\n\n    fn default_namespace(&self) -> Option<Namespace> {\n        self.namespaces.default.clone()\n    }\n\n    fn namespace_for_prefix(&self, prefix: &AtomIdent) -> Option<Namespace> {\n        self.namespaces.prefixes.get(prefix).cloned()\n    }\n}\n\nimpl SelectorImpl {\n    /// A helper to traverse each eagerly cascaded pseudo-element, executing\n    /// `fun` on it.\n    #[inline]\n    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)\n    where\n        F: FnMut(PseudoElement),\n    {\n        for pseudo in &EAGER_PSEUDOS {\n            fun(pseudo.clone())\n        }\n    }\n}\n\n// Selector and component sizes are important for matching performance.\nsize_of_test!(selectors::parser::Selector<SelectorImpl>, 8);\nsize_of_test!(selectors::parser::Component<SelectorImpl>, 24);\nsize_of_test!(PseudoElement, 16);\nsize_of_test!(NonTSPseudoClass, 16);\n"
  },
  {
    "path": "style/gecko/snapshot.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A gecko snapshot, that stores the element attributes and state before they\n//! change in order to properly calculate restyle hints.\n\nuse crate::dom::TElement;\nuse crate::gecko::snapshot_helpers;\nuse crate::gecko::wrapper::GeckoElement;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs::ServoElementSnapshot;\nuse crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags;\nuse crate::gecko_bindings::structs::ServoElementSnapshotTable;\nuse crate::invalidation::element::element_wrapper::ElementSnapshot;\nuse crate::selector_parser::AttrValue;\nuse crate::string_cache::{Atom, Namespace};\nuse crate::values::{AtomIdent, AtomString};\nuse crate::LocalName;\nuse crate::WeakAtom;\nuse dom::ElementState;\nuse selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};\n\n/// A snapshot of a Gecko element.\npub type GeckoElementSnapshot = ServoElementSnapshot;\n\n/// A map from elements to snapshots for Gecko's style back-end.\npub type SnapshotMap = ServoElementSnapshotTable;\n\nimpl SnapshotMap {\n    /// Gets the snapshot for this element, if any.\n    ///\n    /// FIXME(emilio): The transmute() business we do here is kind of nasty, but\n    /// it's a consequence of the map being a OpaqueNode -> Snapshot table in\n    /// Servo and an Element -> Snapshot table in Gecko.\n    ///\n    /// We should be able to make this a more type-safe with type annotations by\n    /// making SnapshotMap a trait and moving the implementations outside, but\n    /// that's a pain because it implies parameterizing SharedStyleContext.\n    pub fn get<E: TElement>(&self, element: &E) -> Option<&GeckoElementSnapshot> {\n        debug_assert!(element.has_snapshot());\n\n        unsafe {\n            let element = ::std::mem::transmute::<&E, &GeckoElement>(element);\n            bindings::Gecko_GetElementSnapshot(self, element.0).as_ref()\n        }\n    }\n}\n\nimpl GeckoElementSnapshot {\n    #[inline]\n    fn has_any(&self, flags: Flags) -> bool {\n        (self.mContains as u8 & flags as u8) != 0\n    }\n\n    /// Returns true if the snapshot has stored state for pseudo-classes\n    /// that depend on things other than `ElementState`.\n    #[inline]\n    pub fn has_other_pseudo_class_state(&self) -> bool {\n        self.has_any(Flags::OtherPseudoClassState)\n    }\n\n    /// Returns true if the snapshot recorded an id change.\n    #[inline]\n    pub fn id_changed(&self) -> bool {\n        self.mIdAttributeChanged()\n    }\n\n    /// Returns true if the snapshot recorded a class attribute change.\n    #[inline]\n    pub fn class_changed(&self) -> bool {\n        self.mClassAttributeChanged()\n    }\n\n    /// Executes the callback once for each attribute that changed.\n    #[inline]\n    pub fn each_attr_changed<F>(&self, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        for attr in self.mChangedAttrNames.iter() {\n            unsafe { AtomIdent::with(attr.mRawPtr, &mut callback) }\n        }\n    }\n\n    /// selectors::Element::attr_matches\n    pub fn attr_matches(\n        &self,\n        ns: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        operation: &AttrSelectorOperation<&AttrValue>,\n    ) -> bool {\n        snapshot_helpers::attr_matches(&self.mAttrs, ns, local_name, operation)\n    }\n}\n\nimpl ElementSnapshot for GeckoElementSnapshot {\n    fn debug_list_attributes(&self) -> String {\n        use nsstring::nsCString;\n        let mut string = nsCString::new();\n        unsafe {\n            bindings::Gecko_Snapshot_DebugListAttributes(self, &mut string);\n        }\n        String::from_utf8_lossy(&*string).into_owned()\n    }\n\n    fn state(&self) -> Option<ElementState> {\n        if self.has_any(Flags::State) {\n            Some(ElementState::from_bits_retain(self.mState))\n        } else {\n            None\n        }\n    }\n\n    #[inline]\n    fn has_attrs(&self) -> bool {\n        self.has_any(Flags::Attributes)\n    }\n\n    #[inline]\n    fn id_attr(&self) -> Option<&WeakAtom> {\n        if !self.has_any(Flags::Id) {\n            return None;\n        }\n\n        snapshot_helpers::get_id(&*self.mAttrs)\n    }\n\n    #[inline]\n    fn is_part(&self, name: &AtomIdent) -> bool {\n        let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!(\"part\")) {\n            Some(attr) => attr,\n            None => return false,\n        };\n\n        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)\n    }\n\n    #[inline]\n    fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {\n        snapshot_helpers::imported_part(&*self.mAttrs, name)\n    }\n\n    #[inline]\n    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        if !self.has_any(Flags::MaybeClass) {\n            return false;\n        }\n\n        snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass)\n    }\n\n    #[inline]\n    fn each_class<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        if !self.has_any(Flags::MaybeClass) {\n            return;\n        }\n\n        snapshot_helpers::each_class_or_part(&self.mClass, callback)\n    }\n\n    #[inline]\n    fn lang_attr(&self) -> Option<AtomString> {\n        let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };\n        if ptr.is_null() {\n            None\n        } else {\n            Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))\n        }\n    }\n\n    /// Returns true if the snapshot has stored state for custom states\n    #[inline]\n    fn has_custom_states(&self) -> bool {\n        self.has_any(Flags::CustomState)\n    }\n\n    /// Returns true if the snapshot has a given CustomState\n    #[inline]\n    fn has_custom_state(&self, state: &AtomIdent) -> bool {\n        unsafe {\n            self.mCustomStates\n                .iter()\n                .any(|setstate| AtomIdent::with(setstate.mRawPtr, |setstate| state == setstate))\n        }\n    }\n\n    #[inline]\n    fn each_custom_state<F>(&self, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        unsafe {\n            for atom in self.mCustomStates.iter() {\n                AtomIdent::with(atom.mRawPtr, &mut callback)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko/snapshot_helpers.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Element an snapshot common logic.\n\nuse crate::dom::TElement;\nuse crate::gecko::wrapper::namespace_id_to_atom;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs::{self, nsAtom};\nuse crate::invalidation::element::element_wrapper::ElementSnapshot;\nuse crate::selector_parser::{AttrValue, SnapshotMap};\nuse crate::string_cache::WeakAtom;\nuse crate::values::AtomIdent;\nuse crate::{Atom, CaseSensitivityExt, LocalName, Namespace};\nuse selectors::attr::{\n    AttrSelectorOperation, AttrSelectorOperator, CaseSensitivity, NamespaceConstraint,\n};\nuse smallvec::SmallVec;\n\n/// A function that, given an element of type `T`, allows you to get a single\n/// class or a class list.\nenum Class<'a> {\n    None,\n    One(*const nsAtom),\n    More(&'a [structs::RefPtr<nsAtom>]),\n}\n\n#[inline(always)]\nfn base_type(attr: &structs::nsAttrValue) -> structs::nsAttrValue_ValueBaseType {\n    (attr.mBits & structs::NS_ATTRVALUE_BASETYPE_MASK) as structs::nsAttrValue_ValueBaseType\n}\n\n#[inline(always)]\nunsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T {\n    (attr.mBits & !structs::NS_ATTRVALUE_BASETYPE_MASK) as *const T\n}\n\n#[inline(always)]\nunsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class<'_> {\n    debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr));\n    let base_type = base_type(attr);\n    if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {\n        return Class::One(ptr::<nsAtom>(attr));\n    }\n    if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase {\n        let container = ptr::<structs::MiscContainer>(attr);\n        debug_assert_eq!(\n            (*container).mType,\n            structs::nsAttrValue_ValueType_eAtomArray\n        );\n        // NOTE: Bindgen doesn't deal with AutoTArray, so cast it below.\n        let attr_array: *const _ = *(*container)\n            .__bindgen_anon_1\n            .mValue\n            .as_ref()\n            .__bindgen_anon_1\n            .mAtomArray\n            .as_ref();\n        let array =\n            (*attr_array).mArray.0.as_ptr() as *const structs::nsTArray<structs::RefPtr<nsAtom>>;\n        return Class::More(&**array);\n    }\n    debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eStringBase);\n    Class::None\n}\n\n#[inline(always)]\nunsafe fn get_id_from_attr(attr: &structs::nsAttrValue) -> &WeakAtom {\n    debug_assert_eq!(\n        base_type(attr),\n        structs::nsAttrValue_ValueBaseType_eAtomBase\n    );\n    WeakAtom::new(ptr::<nsAtom>(attr))\n}\n\nimpl structs::nsAttrName {\n    #[inline]\n    fn is_nodeinfo(&self) -> bool {\n        self.mBits & 1 != 0\n    }\n\n    #[inline]\n    unsafe fn as_nodeinfo(&self) -> &structs::NodeInfo {\n        debug_assert!(self.is_nodeinfo());\n        &*((self.mBits & !1) as *const structs::NodeInfo)\n    }\n\n    #[inline]\n    fn namespace_id(&self) -> i32 {\n        if !self.is_nodeinfo() {\n            return structs::kNameSpaceID_None;\n        }\n        unsafe { self.as_nodeinfo() }.mInner.mNamespaceID\n    }\n\n    /// Returns the attribute name as an atom pointer.\n    #[inline]\n    pub fn name(&self) -> *const nsAtom {\n        if self.is_nodeinfo() {\n            unsafe { self.as_nodeinfo() }.mInner.mName\n        } else {\n            self.mBits as *const nsAtom\n        }\n    }\n}\n\n/// Find an attribute value with a given name and no namespace.\n#[inline(always)]\npub fn find_attr<'a>(\n    attrs: &'a [structs::AttrArray_InternalAttr],\n    name: &Atom,\n) -> Option<&'a structs::nsAttrValue> {\n    attrs\n        .iter()\n        .find(|attr| attr.mName.mBits == name.as_ptr() as usize)\n        .map(|attr| &attr.mValue)\n}\n\n/// Finds the id attribute from a list of attributes.\n#[inline(always)]\npub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {\n    Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!(\"id\"))?) })\n}\n\n#[inline(always)]\npub(super) fn each_exported_part(\n    attrs: &[structs::AttrArray_InternalAttr],\n    name: &AtomIdent,\n    mut callback: impl FnMut(&AtomIdent),\n) {\n    let attr = match find_attr(attrs, &atom!(\"exportparts\")) {\n        Some(attr) => attr,\n        None => return,\n    };\n    let mut length = 0;\n    let atoms = unsafe { bindings::Gecko_Element_ExportedParts(attr, name.as_ptr(), &mut length) };\n    if atoms.is_null() {\n        return;\n    }\n\n    unsafe {\n        for atom in std::slice::from_raw_parts(atoms, length) {\n            AtomIdent::with(*atom, &mut callback)\n        }\n    }\n}\n\n#[inline(always)]\npub(super) fn imported_part(\n    attrs: &[structs::AttrArray_InternalAttr],\n    name: &AtomIdent,\n) -> Option<AtomIdent> {\n    let attr = find_attr(attrs, &atom!(\"exportparts\"))?;\n    let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };\n    if atom.is_null() {\n        return None;\n    }\n    Some(AtomIdent(unsafe { Atom::from_raw(atom) }))\n}\n\n/// Given a class or part name, a case sensitivity, and an array of attributes,\n/// returns whether the attribute has that name.\n#[inline(always)]\npub fn has_class_or_part(\n    name: &AtomIdent,\n    case_sensitivity: CaseSensitivity,\n    attr: &structs::nsAttrValue,\n) -> bool {\n    match unsafe { get_class_or_part_from_attr(attr) } {\n        Class::None => false,\n        Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) },\n        Class::More(atoms) => match case_sensitivity {\n            CaseSensitivity::CaseSensitive => {\n                let name_ptr = name.as_ptr();\n                atoms.iter().any(|atom| atom.mRawPtr == name_ptr)\n            },\n            CaseSensitivity::AsciiCaseInsensitive => unsafe {\n                atoms\n                    .iter()\n                    .any(|atom| WeakAtom::new(atom.mRawPtr).eq_ignore_ascii_case(name))\n            },\n        },\n    }\n}\n\n/// Given an item, a callback, and a getter, execute `callback` for each class\n/// or part name this `item` has.\n#[inline(always)]\npub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F)\nwhere\n    F: FnMut(&AtomIdent),\n{\n    unsafe {\n        match get_class_or_part_from_attr(attr) {\n            Class::None => {},\n            Class::One(atom) => AtomIdent::with(atom, callback),\n            Class::More(atoms) => {\n                for atom in atoms {\n                    AtomIdent::with(atom.mRawPtr, &mut callback)\n                }\n            },\n        }\n    }\n}\n\n/// Returns a list of classes that were either added to or removed from the\n/// element since the snapshot.\npub fn classes_changed<E: TElement>(element: &E, snapshots: &SnapshotMap) -> SmallVec<[Atom; 8]> {\n    debug_assert!(element.has_snapshot(), \"Why bothering?\");\n    let snapshot = snapshots.get(element).expect(\"has_snapshot lied\");\n    if !snapshot.class_changed() {\n        return SmallVec::new();\n    }\n\n    let mut classes_changed = SmallVec::<[Atom; 8]>::new();\n    snapshot.each_class(|c| {\n        if !element.has_class(c, CaseSensitivity::CaseSensitive) {\n            classes_changed.push(c.0.clone());\n        }\n    });\n    element.each_class(|c| {\n        if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {\n            classes_changed.push(c.0.clone());\n        }\n    });\n\n    classes_changed\n}\n\n/// Returns whether a given attribute selector matches given the internal attrs.\n#[inline(always)]\npub(crate) fn attr_matches(\n    attrs: &[structs::AttrArray_InternalAttr],\n    ns: &NamespaceConstraint<&Namespace>,\n    local_name: &LocalName,\n    operation: &AttrSelectorOperation<&AttrValue>,\n) -> bool {\n    let name_ptr = local_name.as_ptr();\n    for attr in attrs {\n        if attr.mName.name() != name_ptr {\n            continue;\n        }\n\n        if attr_matches_checked_name(attr, ns, operation) {\n            return true;\n        }\n\n        // The name matched but the value or namespace didn't. The only reason to check the other\n        // attributes now would be to find one with the same name but a different namespace.\n        if *ns != NamespaceConstraint::Any {\n            // We don't want to look for other namespaces, so we're done.\n            return false;\n        }\n    }\n    false\n}\n\n/// Returns whether a given attribute selector matches given a single attribute,\n/// for the case where the caller has already found an attribute with the right name.\nfn attr_matches_checked_name(\n    attr: &structs::AttrArray_InternalAttr,\n    ns: &NamespaceConstraint<&Namespace>,\n    operation: &AttrSelectorOperation<&AttrValue>,\n) -> bool {\n    let ns_matches = match *ns {\n        NamespaceConstraint::Any => true,\n        NamespaceConstraint::Specific(ns) => {\n            if *ns == ns!() {\n                !attr.mName.is_nodeinfo()\n            } else {\n                ns.as_ptr() == unsafe { namespace_id_to_atom(attr.mName.namespace_id()) }\n            }\n        },\n    };\n\n    if !ns_matches {\n        return false;\n    }\n\n    let (operator, case_sensitivity, value) = match *operation {\n        AttrSelectorOperation::Exists => return true,\n        AttrSelectorOperation::WithValue {\n            operator,\n            case_sensitivity,\n            value,\n        } => (operator, case_sensitivity, value),\n    };\n    let ignore_case = match case_sensitivity {\n        CaseSensitivity::CaseSensitive => false,\n        CaseSensitivity::AsciiCaseInsensitive => true,\n    };\n    let value = value.as_ptr();\n    unsafe {\n        match operator {\n            AttrSelectorOperator::Equal => {\n                bindings::Gecko_AttrEquals(&attr.mValue, value, ignore_case)\n            },\n            AttrSelectorOperator::Includes => {\n                bindings::Gecko_AttrIncludes(&attr.mValue, value, ignore_case)\n            },\n            AttrSelectorOperator::DashMatch => {\n                bindings::Gecko_AttrDashEquals(&attr.mValue, value, ignore_case)\n            },\n            AttrSelectorOperator::Prefix => {\n                bindings::Gecko_AttrHasPrefix(&attr.mValue, value, ignore_case)\n            },\n            AttrSelectorOperator::Suffix => {\n                bindings::Gecko_AttrHasSuffix(&attr.mValue, value, ignore_case)\n            },\n            AttrSelectorOperator::Substring => {\n                bindings::Gecko_AttrHasSubstring(&attr.mValue, value, ignore_case)\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko/traversal.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko-specific bits for the styling DOM traversal.\n\nuse crate::context::{SharedStyleContext, StyleContext};\nuse crate::dom::{TElement, TNode};\nuse crate::gecko::wrapper::{GeckoElement, GeckoNode};\nuse crate::traversal::{recalc_style_at, DomTraversal, PerLevelTraversalData};\n\n/// This is the simple struct that Gecko uses to encapsulate a DOM traversal for\n/// styling.\npub struct RecalcStyleOnly<'a> {\n    shared: SharedStyleContext<'a>,\n}\n\nimpl<'a> RecalcStyleOnly<'a> {\n    /// Create a `RecalcStyleOnly` traversal from a `SharedStyleContext`.\n    pub fn new(shared: SharedStyleContext<'a>) -> Self {\n        RecalcStyleOnly { shared: shared }\n    }\n}\n\nimpl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc> {\n    fn process_preorder<F>(\n        &self,\n        traversal_data: &PerLevelTraversalData,\n        context: &mut StyleContext<GeckoElement<'le>>,\n        node: GeckoNode<'le>,\n        note_child: F,\n    ) where\n        F: FnMut(GeckoNode<'le>),\n    {\n        if let Some(el) = node.as_element() {\n            let mut data = unsafe { el.ensure_data() };\n            recalc_style_at(self, traversal_data, context, el, &mut data, note_child);\n        }\n    }\n\n    fn process_postorder(&self, _: &mut StyleContext<GeckoElement<'le>>, _: GeckoNode<'le>) {\n        unreachable!();\n    }\n\n    /// We don't use the post-order traversal for anything.\n    fn needs_postorder_traversal() -> bool {\n        false\n    }\n\n    fn shared_context(&self) -> &SharedStyleContext<'recalc> {\n        &self.shared\n    }\n}\n"
  },
  {
    "path": "style/gecko/url.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common handling for the specified value CSS url() values.\n\nuse crate::derives::*;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::structs;\nuse crate::parser::{Parse, ParserContext};\nuse crate::stylesheets::{CorsMode, UrlExtraData};\nuse crate::values::computed::{Context, ToComputedValue};\nuse cssparser::{Parser, SourceLocation};\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps};\nuse nsstring::nsCString;\nuse servo_arc::Arc;\nuse std::collections::HashMap;\nuse std::fmt::{self, Write};\nuse std::mem::ManuallyDrop;\nuse std::sync::{LazyLock, RwLock};\nuse style_traits::{CssWriter, ParseError, ToCss};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\n/// A CSS url() value for gecko.\n#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\n#[css(function = \"url\")]\n#[repr(C)]\npub struct CssUrl(pub Arc<CssUrlData>);\n\n/// Data shared between CssUrls.\n///\n/// cbindgen:derive-eq=false\n/// cbindgen:derive-neq=false\n#[derive(Debug, SpecifiedValueInfo, ToCss, ToShmem)]\n#[repr(C)]\npub struct CssUrlData {\n    /// The URL in unresolved string form.\n    serialization: crate::OwnedStr,\n\n    /// The URL extra data.\n    #[css(skip)]\n    pub extra_data: UrlExtraData,\n\n    /// The CORS mode that will be used for the load.\n    #[css(skip)]\n    cors_mode: CorsMode,\n\n    /// Data to trigger a load from Gecko. This is mutable in C++.\n    ///\n    /// TODO(emilio): Maybe we can eagerly resolve URLs and make this immutable?\n    #[css(skip)]\n    load_data: LoadDataSource,\n}\n\nimpl PartialEq for CssUrlData {\n    fn eq(&self, other: &Self) -> bool {\n        self.serialization == other.serialization\n            && self.extra_data == other.extra_data\n            && self.cors_mode == other.cors_mode\n    }\n}\n\n/// How an URI might depend on our base URI.\n///\n/// TODO(emilio): See if necko can provide this, but for our case local refs or empty URIs are\n/// totally fine even though they wouldn't be in general...\n#[repr(u8)]\n#[derive(PartialOrd, PartialEq)]\npub enum NonLocalUriDependency {\n    /// No non-local URI dependencies.\n    No = 0,\n    /// URI is absolute or not dependent on the base uri otherwise.\n    Absolute,\n    /// We might depend on our path depth. E.g. `https://example.com/foo` and\n    /// `https://example.com/bar` both resolve a relative URI like `baz.css` as\n    /// `https://example.com/baz.css`.\n    Path,\n    /// We might depend on our full URI. This is the case for query strings (and, in the general\n    /// case, local refs and empty URIs, but that's not the case for CSS as described below.\n    Full,\n}\n\nimpl NonLocalUriDependency {\n    fn scan(specified: &str) -> Self {\n        if specified.is_empty() || specified.starts_with('#') || specified.starts_with(\"data:\") {\n            // In CSS the empty URL is special / invalid. Local and data uris are also fair game.\n            // https://drafts.csswg.org/css-values-4/#url-empty\n            return Self::No;\n        }\n        if specified.starts_with('/')\n            || specified.starts_with(\"http:\")\n            || specified.starts_with(\"https:\")\n        {\n            return Self::Absolute;\n        }\n        if specified.starts_with('?') {\n            // Query string resolves differently for any two different base URIs\n            return Self::Full;\n        }\n        // Might be a relative URI, play it safe.\n        Self::Path\n    }\n}\n\nimpl CssUrl {\n    /// Parse a URL with a particular CORS mode.\n    pub fn parse_with_cors_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n    ) -> Result<Self, ParseError<'i>> {\n        let start = input.position().byte_index();\n        let location = input.current_source_location();\n        let url = input.expect_url()?;\n        let end = input.position().byte_index();\n        Self::parse_from_string(\n            url.as_ref().to_owned(),\n            start,\n            end,\n            context,\n            cors_mode,\n            location,\n        )\n    }\n\n    /// Parse a URL from a string value that is a valid CSS token for a URL,\n    /// enforcing attr()-tainting constraints if applicable.\n    /// https://drafts.csswg.org/css-values-5/#attr-security\n    pub fn parse_from_string<'i>(\n        url: String,\n        url_start: usize,\n        url_end: usize,\n        context: &ParserContext,\n        cors_mode: CorsMode,\n        location: SourceLocation,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::custom_properties::AttrTaintedRange;\n        use style_traits::StyleParseErrorKind;\n        let range = AttrTaintedRange::new(url_start, url_end);\n        if context.disallow_urls_in_range(&range) {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(Self::new_from_string(url, context, cors_mode))\n    }\n\n    /// Create a new CSS URL that is attr()-untainted given a valid CSS token for a URL.\n    /// Be cautious when calling `expect_url()` to not bypass attr()-tainting checks. If\n    /// it's possible attr()'s were substituted into the `url`, DO NOT use this method.\n    /// https://drafts.csswg.org/css-values-5/#attr-security\n    pub fn new_from_untainted_string(\n        url: String,\n        context: &ParserContext,\n        cors_mode: CorsMode,\n    ) -> Self {\n        debug_assert!(context.attr_tainted_regions.is_empty());\n        Self::new_from_string(url, context, cors_mode)\n    }\n\n    fn new_from_string(url: String, context: &ParserContext, cors_mode: CorsMode) -> Self {\n        use crate::use_counters::CustomUseCounter;\n        if let Some(counters) = context.use_counters {\n            if !counters\n                .custom\n                .recorded(CustomUseCounter::MaybeHasFullBaseUriDependency)\n            {\n                let dep = NonLocalUriDependency::scan(&url);\n                if dep >= NonLocalUriDependency::Absolute {\n                    counters\n                        .custom\n                        .record(CustomUseCounter::HasNonLocalUriDependency);\n                }\n                if dep >= NonLocalUriDependency::Path {\n                    counters\n                        .custom\n                        .record(CustomUseCounter::MaybeHasPathBaseUriDependency);\n                }\n                if dep >= NonLocalUriDependency::Full {\n                    counters\n                        .custom\n                        .record(CustomUseCounter::MaybeHasFullBaseUriDependency);\n                }\n            }\n        }\n        CssUrl(Arc::new(CssUrlData {\n            serialization: url.into(),\n            extra_data: context.url_data.clone(),\n            cors_mode,\n            load_data: LoadDataSource::Owned(LoadData::default()),\n        }))\n    }\n\n    /// Returns true if the URL is definitely invalid. We don't eagerly resolve\n    /// URLs in gecko, so we just return false here.\n    /// use its |resolved| status.\n    pub fn is_invalid(&self) -> bool {\n        false\n    }\n\n    /// Returns true if this URL looks like a fragment.\n    /// See https://drafts.csswg.org/css-values/#local-urls\n    #[inline]\n    pub fn is_fragment(&self) -> bool {\n        self.0.is_fragment()\n    }\n\n    /// Return the unresolved url as string, or the empty string if it's\n    /// invalid.\n    #[inline]\n    pub fn as_str(&self) -> &str {\n        self.0.as_str()\n    }\n}\n\nimpl CssUrlData {\n    /// Returns true if this URL looks like a fragment.\n    /// See https://drafts.csswg.org/css-values/#local-urls\n    pub fn is_fragment(&self) -> bool {\n        self.as_str()\n            .as_bytes()\n            .iter()\n            .next()\n            .map_or(false, |b| *b == b'#')\n    }\n\n    /// Return the unresolved url as string, or the empty string if it's\n    /// invalid.\n    pub fn as_str(&self) -> &str {\n        &*self.serialization\n    }\n}\n\nimpl Parse for CssUrl {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_cors_mode(context, input, CorsMode::None)\n    }\n}\n\nimpl Eq for CssUrl {}\n\nimpl MallocSizeOf for CssUrl {\n    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {\n        // XXX: measure `serialization` once bug 1397971 lands\n\n        // We ignore `extra_data`, because RefPtr is tricky, and there aren't\n        // many of them in practise (sharing is common).\n\n        0\n    }\n}\n\n/// A key type for LOAD_DATA_TABLE.\n#[derive(Eq, Hash, PartialEq)]\nstruct LoadDataKey(*const LoadDataSource);\n\nunsafe impl Sync for LoadDataKey {}\nunsafe impl Send for LoadDataKey {}\n\nbitflags! {\n    /// Various bits of mutable state that are kept for image loads.\n    #[derive(Debug)]\n    #[repr(C)]\n    pub struct LoadDataFlags: u8 {\n        /// Whether we tried to resolve the uri at least once.\n        const TRIED_TO_RESOLVE_URI = 1 << 0;\n        /// Whether we tried to resolve the image at least once.\n        const TRIED_TO_RESOLVE_IMAGE = 1 << 1;\n    }\n}\n\n/// This is usable and movable from multiple threads just fine, as long as it's\n/// not cloned (it is not clonable), and the methods that mutate it run only on\n/// the main thread (when all the other threads we care about are paused).\nunsafe impl Sync for LoadData {}\nunsafe impl Send for LoadData {}\n\n/// The load data for a given URL. This is mutable from C++, and shouldn't be\n/// accessed from rust for anything.\n#[repr(C)]\n#[derive(Debug)]\npub struct LoadData {\n    /// A strong reference to the imgRequestProxy, if any, that should be\n    /// released on drop.\n    ///\n    /// These are raw pointers because they are not safe to reference-count off\n    /// the main thread.\n    resolved_image: *mut structs::imgRequestProxy,\n    /// A strong reference to the resolved URI of this image.\n    resolved_uri: *mut structs::nsIURI,\n    /// A few flags that are set when resolving the image or such.\n    flags: LoadDataFlags,\n}\n\nimpl Drop for LoadData {\n    fn drop(&mut self) {\n        unsafe { bindings::Gecko_LoadData_Drop(self) }\n    }\n}\n\nimpl Default for LoadData {\n    fn default() -> Self {\n        Self {\n            resolved_image: std::ptr::null_mut(),\n            resolved_uri: std::ptr::null_mut(),\n            flags: LoadDataFlags::empty(),\n        }\n    }\n}\n\n/// The data for a load, or a lazy-loaded, static member that will be stored in\n/// LOAD_DATA_TABLE, keyed by the memory location of this object, which is\n/// always in the heap because it's inside the CssUrlData object.\n///\n/// This type is meant not to be used from C++ so we don't derive helper\n/// methods.\n///\n/// cbindgen:derive-helper-methods=false\n#[derive(Debug)]\n#[repr(u8, C)]\npub enum LoadDataSource {\n    /// An owned copy of the load data.\n    Owned(LoadData),\n    /// A lazily-resolved copy of it.\n    Lazy,\n}\n\nimpl LoadDataSource {\n    /// Gets the load data associated with the source.\n    ///\n    /// This relies on the source on being in a stable location if lazy.\n    #[inline]\n    pub unsafe fn get(&self) -> *const LoadData {\n        match *self {\n            LoadDataSource::Owned(ref d) => return d,\n            LoadDataSource::Lazy => {},\n        }\n\n        let key = LoadDataKey(self);\n\n        {\n            let guard = LOAD_DATA_TABLE.read().unwrap();\n            if let Some(r) = guard.get(&key) {\n                return &**r;\n            }\n        }\n        let mut guard = LOAD_DATA_TABLE.write().unwrap();\n        let r = guard.entry(key).or_insert_with(Default::default);\n        &**r\n    }\n}\n\nimpl ToShmem for LoadDataSource {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        Ok(ManuallyDrop::new(match self {\n            LoadDataSource::Owned(..) => LoadDataSource::Lazy,\n            LoadDataSource::Lazy => LoadDataSource::Lazy,\n        }))\n    }\n}\n\n/// A specified non-image `url()` value.\npub type SpecifiedUrl = CssUrl;\n\n/// Clears LOAD_DATA_TABLE.  Entries in this table, which are for specified URL\n/// values that come from shared memory style sheets, would otherwise persist\n/// until the end of the process and be reported as leaks.\npub fn shutdown() {\n    LOAD_DATA_TABLE.write().unwrap().clear();\n}\n\nimpl ToComputedValue for SpecifiedUrl {\n    type ComputedValue = ComputedUrl;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        ComputedUrl(self.clone())\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.0.clone()\n    }\n}\n\n/// The computed value of a CSS non-image `url()`.\n///\n/// The only difference between specified and computed URLs is the\n/// serialization.\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]\n#[repr(C)]\npub struct ComputedUrl(pub SpecifiedUrl);\n\nimpl ComputedUrl {\n    fn serialize_with<W>(\n        &self,\n        function: unsafe extern \"C\" fn(*const Self, *mut nsCString),\n        dest: &mut CssWriter<W>,\n    ) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"url(\")?;\n        unsafe {\n            let mut string = nsCString::new();\n            function(self, &mut string);\n            string.as_str_unchecked().to_css(dest)?;\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl ToCss for ComputedUrl {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.serialize_with(bindings::Gecko_GetComputedURLSpec, dest)\n    }\n}\n\n/// A table mapping CssUrlData objects to their lazily created LoadData\n/// objects.\nstatic LOAD_DATA_TABLE: LazyLock<RwLock<HashMap<LoadDataKey, Box<LoadData>>>> =\n    LazyLock::new(|| Default::default());\n"
  },
  {
    "path": "style/gecko/wrapper.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n\n//! Wrapper definitions on top of Gecko types in order to be used in the style\n//! system.\n//!\n//! This really follows the Servo pattern in\n//! `components/script/layout_wrapper.rs`.\n//!\n//! This theoretically should live in its own crate, but now it lives in the\n//! style system it's kind of pointless in the Stylo case, and only Servo forces\n//! the separation between the style system implementation and everything else.\n\nuse crate::applicable_declarations::ApplicableDeclarationBlock;\nuse crate::bloom::each_relevant_element_hash;\nuse crate::context::{QuirksMode, SharedStyleContext, UpdateAnimationsTasks};\nuse crate::data::{ElementDataMut, ElementDataRef, ElementDataWrapper};\nuse crate::device::Device;\nuse crate::dom::{\n    AttributeProvider, LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode,\n    TShadowRoot,\n};\nuse crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};\nuse crate::gecko::snapshot_helpers;\nuse crate::gecko_bindings::bindings;\nuse crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;\nuse crate::gecko_bindings::bindings::Gecko_ElementHasCSSAnimations;\nuse crate::gecko_bindings::bindings::Gecko_ElementHasCSSTransitions;\nuse crate::gecko_bindings::bindings::Gecko_ElementState;\nuse crate::gecko_bindings::bindings::Gecko_GetActiveLinkAttrDeclarationBlock;\nuse crate::gecko_bindings::bindings::Gecko_GetAnimationEffectCount;\nuse crate::gecko_bindings::bindings::Gecko_GetAnimationRule;\nuse crate::gecko_bindings::bindings::Gecko_GetExtraContentStyleDeclarations;\nuse crate::gecko_bindings::bindings::Gecko_GetHTMLPresentationAttrDeclarationBlock;\nuse crate::gecko_bindings::bindings::Gecko_GetStyleAttrDeclarationBlock;\nuse crate::gecko_bindings::bindings::Gecko_GetUnvisitedLinkAttrDeclarationBlock;\nuse crate::gecko_bindings::bindings::Gecko_GetVisitedLinkAttrDeclarationBlock;\nuse crate::gecko_bindings::bindings::Gecko_IsSignificantChild;\nuse crate::gecko_bindings::bindings::Gecko_MatchLang;\nuse crate::gecko_bindings::bindings::Gecko_UnsetDirtyStyleAttr;\nuse crate::gecko_bindings::bindings::Gecko_UpdateAnimations;\nuse crate::gecko_bindings::structs;\nuse crate::gecko_bindings::structs::nsChangeHint;\nuse crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;\nuse crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;\nuse crate::gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;\nuse crate::gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;\nuse crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;\nuse crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES;\nuse crate::gecko_bindings::structs::NODE_NEEDS_FRAME;\nuse crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag};\nuse crate::gecko_bindings::structs::{nsINode as RawGeckoNode, Element as RawGeckoElement};\nuse crate::global_style_data::GLOBAL_STYLE_DATA;\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::properties::{\n    animated_properties::{AnimationValue, AnimationValueMap},\n    ComputedValues, Importance, OwnedPropertyDeclarationId, PropertyDeclaration,\n    PropertyDeclarationBlock, PropertyDeclarationId, PropertyDeclarationIdSet,\n};\nuse crate::rule_tree::CascadeLevel as ServoCascadeLevel;\nuse crate::rule_tree::CascadeOrigin as ServoCascadeOrigin;\nuse crate::selector_parser::{AttrValue, Lang};\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};\nuse crate::stylesheets::scope_rule::ImplicitScopeRoot;\nuse crate::stylist::CascadeData;\nuse crate::values::computed::Display;\nuse crate::values::{AtomIdent, AtomString};\nuse crate::CaseSensitivityExt;\nuse crate::LocalName;\nuse app_units::Au;\nuse dom::{DocumentState, ElementState};\nuse euclid::default::Size2D;\nuse nsstring::nsString;\nuse rustc_hash::FxHashMap;\nuse selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};\nuse selectors::bloom::{BloomFilter, BLOOM_HASH_MASK};\nuse selectors::matching::VisitedHandlingMode;\nuse selectors::matching::{ElementSelectorFlags, MatchingContext};\nuse selectors::sink::Push;\nuse selectors::{Element, OpaqueElement};\nuse servo_arc::{Arc, ArcBorrow};\nuse std::cell::Cell;\nuse std::hash::{Hash, Hasher};\nuse std::sync::atomic::{AtomicU32, Ordering};\nuse std::{fmt, mem, ptr};\n\n#[inline]\nfn elements_with_id<'a, 'le>(\n    array: structs::RustSpan<*const RawGeckoElement>,\n) -> &'a [GeckoElement<'le>] {\n    unsafe {\n        let elements: &[*const RawGeckoElement] =\n            std::slice::from_raw_parts(array.begin, array.length);\n\n        // NOTE(emilio): We rely on the in-memory representation of\n        // GeckoElement<'ld> and *const RawGeckoElement being the same.\n        #[allow(dead_code)]\n        unsafe fn static_assert() {\n            mem::transmute::<*mut RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *mut _);\n        }\n\n        mem::transmute(elements)\n    }\n}\n\n/// A simple wrapper over `Document`.\n#[derive(Clone, Copy)]\npub struct GeckoDocument<'ld>(pub &'ld structs::Document);\n\nimpl<'ld> TDocument for GeckoDocument<'ld> {\n    type ConcreteNode = GeckoNode<'ld>;\n\n    #[inline]\n    fn as_node(&self) -> Self::ConcreteNode {\n        GeckoNode(&self.0._base)\n    }\n\n    #[inline]\n    fn is_html_document(&self) -> bool {\n        self.0.mType == structs::Document_Type::eHTML\n    }\n\n    #[inline]\n    fn quirks_mode(&self) -> QuirksMode {\n        self.0.mCompatMode.into()\n    }\n\n    #[inline]\n    fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()>\n    where\n        Self: 'a,\n    {\n        Ok(elements_with_id(unsafe {\n            bindings::Gecko_Document_GetElementsWithId(self.0, id.as_ptr())\n        }))\n    }\n\n    fn shared_lock(&self) -> &SharedRwLock {\n        &GLOBAL_STYLE_DATA.shared_lock\n    }\n}\n\n/// A simple wrapper over `ShadowRoot`.\n#[derive(Clone, Copy)]\npub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);\n\nimpl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        // TODO(emilio): Maybe print the host or something?\n        write!(f, \"<shadow-root> ({:#x})\", self.as_node().opaque().0)\n    }\n}\n\nimpl<'lr> PartialEq for GeckoShadowRoot<'lr> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.0 as *const _ == other.0 as *const _\n    }\n}\n\nimpl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {\n    type ConcreteNode = GeckoNode<'lr>;\n\n    #[inline]\n    fn as_node(&self) -> Self::ConcreteNode {\n        GeckoNode(&self.0._base._base._base._base)\n    }\n\n    #[inline]\n    fn host(&self) -> GeckoElement<'lr> {\n        GeckoElement(unsafe { &*self.0._base.mHost.mRawPtr })\n    }\n\n    #[inline]\n    fn style_data<'a>(&self) -> Option<&'a CascadeData>\n    where\n        Self: 'a,\n    {\n        let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };\n        Some(&author_styles.data)\n    }\n\n    #[inline]\n    fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()>\n    where\n        Self: 'a,\n    {\n        Ok(elements_with_id(unsafe {\n            bindings::Gecko_ShadowRoot_GetElementsWithId(self.0, id.as_ptr())\n        }))\n    }\n\n    #[inline]\n    fn parts<'a>(&self) -> &[<Self::ConcreteNode as TNode>::ConcreteElement]\n    where\n        Self: 'a,\n    {\n        let slice: &[*const RawGeckoElement] = &*self.0.mParts;\n\n        #[allow(dead_code)]\n        unsafe fn static_assert() {\n            mem::transmute::<*const RawGeckoElement, GeckoElement<'static>>(0xbadc0de as *const _);\n        }\n\n        unsafe { mem::transmute(slice) }\n    }\n\n    #[inline]\n    fn implicit_scope_for_sheet(&self, sheet_index: usize) -> Option<ImplicitScopeRoot> {\n        use crate::stylesheets::StylesheetInDocument;\n\n        let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? };\n        let sheet = author_styles.stylesheets.get(sheet_index)?;\n        sheet.implicit_scope_root()\n    }\n}\n\n/// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.\n///\n/// Important: We don't currently refcount the DOM, because the wrapper lifetime\n/// magic guarantees that our LayoutFoo references won't outlive the root, and\n/// we don't mutate any of the references on the Gecko side during restyle.\n///\n/// We could implement refcounting if need be (at a potentially non-trivial\n/// performance cost) by implementing Drop and making LayoutFoo non-Copy.\n#[derive(Clone, Copy)]\npub struct GeckoNode<'ln>(pub &'ln RawGeckoNode);\n\nimpl<'ln> PartialEq for GeckoNode<'ln> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.0 as *const _ == other.0 as *const _\n    }\n}\n\nimpl<'ln> fmt::Debug for GeckoNode<'ln> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        if let Some(el) = self.as_element() {\n            return el.fmt(f);\n        }\n\n        if self.is_text_node() {\n            return write!(f, \"<text node> ({:#x})\", self.opaque().0);\n        }\n\n        if self.is_document() {\n            return write!(f, \"<document> ({:#x})\", self.opaque().0);\n        }\n\n        if let Some(sr) = self.as_shadow_root() {\n            return sr.fmt(f);\n        }\n\n        write!(f, \"<non-text node> ({:#x})\", self.opaque().0)\n    }\n}\n\nimpl<'ln> GeckoNode<'ln> {\n    #[inline]\n    fn is_document(&self) -> bool {\n        // This is a DOM constant that isn't going to change.\n        const DOCUMENT_NODE: u16 = 9;\n        self.node_info().mInner.mNodeType == DOCUMENT_NODE\n    }\n\n    #[inline]\n    fn is_shadow_root(&self) -> bool {\n        self.is_in_shadow_tree() && self.parent_node().is_none()\n    }\n\n    #[inline]\n    fn from_content(content: &'ln nsIContent) -> Self {\n        GeckoNode(&content._base)\n    }\n\n    #[inline]\n    fn set_flags(&self, flags: u32) {\n        self.flags_atomic().fetch_or(flags, Ordering::Relaxed);\n    }\n\n    fn flags_atomic_for(flags: &Cell<u32>) -> &AtomicU32 {\n        const_assert!(mem::size_of::<Cell<u32>>() == mem::size_of::<AtomicU32>());\n        const_assert!(mem::align_of::<Cell<u32>>() == mem::align_of::<AtomicU32>());\n\n        // Rust doesn't provide standalone atomic functions like GCC/clang do\n        // (via the atomic intrinsics) or via std::atomic_ref, but it guarantees\n        // that the memory representation of u32 and AtomicU32 matches:\n        // https://doc.rust-lang.org/std/sync/atomic/struct.AtomicU32.html\n        unsafe { mem::transmute::<&Cell<u32>, &AtomicU32>(flags) }\n    }\n\n    #[inline]\n    fn flags_atomic(&self) -> &AtomicU32 {\n        Self::flags_atomic_for(&self.0._base._base_1.mFlags)\n    }\n\n    #[inline]\n    fn flags(&self) -> u32 {\n        self.flags_atomic().load(Ordering::Relaxed)\n    }\n\n    #[inline]\n    fn may_have_element_children(&self) -> bool {\n        self.flags() & structs::NODE_MAY_HAVE_ELEMENT_CHILDREN != 0\n    }\n\n    #[inline]\n    fn selector_flags_atomic(&self) -> &AtomicU32 {\n        Self::flags_atomic_for(&self.0.mSelectorFlags)\n    }\n\n    #[inline]\n    fn selector_flags(&self) -> u32 {\n        self.selector_flags_atomic().load(Ordering::Relaxed)\n    }\n\n    #[inline]\n    fn set_selector_flags(&self, flags: u32) {\n        self.selector_flags_atomic()\n            .fetch_or(flags, Ordering::Relaxed);\n    }\n\n    #[inline]\n    fn node_info(&self) -> &structs::NodeInfo {\n        debug_assert!(!self.0.mNodeInfo.mRawPtr.is_null());\n        unsafe { &*self.0.mNodeInfo.mRawPtr }\n    }\n\n    // These live in different locations depending on processor architecture.\n    #[cfg(target_pointer_width = \"64\")]\n    #[inline]\n    fn bool_flags(&self) -> u32 {\n        (self.0)._base._base_1.mBoolFlags\n    }\n\n    #[cfg(target_pointer_width = \"32\")]\n    #[inline]\n    fn bool_flags(&self) -> u32 {\n        (self.0).mBoolFlags\n    }\n\n    #[inline]\n    fn get_bool_flag(&self, flag: nsINode_BooleanFlag) -> bool {\n        self.bool_flags() & (1u32 << flag as u32) != 0\n    }\n\n    /// This logic is duplicate in Gecko's nsINode::IsInShadowTree().\n    #[inline]\n    fn is_in_shadow_tree(&self) -> bool {\n        use crate::gecko_bindings::structs::NODE_IS_IN_SHADOW_TREE;\n        self.flags() & NODE_IS_IN_SHADOW_TREE != 0\n    }\n\n    /// Returns true if we know for sure that `flattened_tree_parent` and `parent_node` return the\n    /// same thing.\n    ///\n    /// TODO(emilio): Measure and consider not doing this fast-path, it's only a function call and\n    /// from profiles it seems that keeping this fast path makes the compiler not inline\n    /// `flattened_tree_parent` as a whole, so we're not gaining much either.\n    #[inline]\n    fn flattened_tree_parent_is_parent(&self) -> bool {\n        use crate::gecko_bindings::structs::*;\n        let flags = self.flags();\n\n        let parent = match self.parent_node() {\n            Some(p) => p,\n            None => return true,\n        };\n\n        if parent.is_shadow_root() {\n            return false;\n        }\n\n        if let Some(parent) = parent.as_element() {\n            if flags & NODE_IS_NATIVE_ANONYMOUS_ROOT != 0 && parent.is_root() {\n                return false;\n            }\n            if parent.shadow_root().is_some() || parent.is_html_slot_element() {\n                return false;\n            }\n        }\n\n        true\n    }\n\n    #[inline]\n    fn flattened_tree_parent(&self) -> Option<Self> {\n        if self.flattened_tree_parent_is_parent() {\n            debug_assert_eq!(\n                unsafe {\n                    bindings::Gecko_GetFlattenedTreeParentNode(self.0)\n                        .as_ref()\n                        .map(GeckoNode)\n                },\n                self.parent_node(),\n                \"Fast path stopped holding!\"\n            );\n            return self.parent_node();\n        }\n\n        // NOTE(emilio): If this call is too expensive, we could manually inline more aggressively.\n        unsafe {\n            bindings::Gecko_GetFlattenedTreeParentNode(self.0)\n                .as_ref()\n                .map(GeckoNode)\n        }\n    }\n\n    #[inline]\n    fn contains_non_whitespace_content(&self) -> bool {\n        unsafe { Gecko_IsSignificantChild(self.0, false) }\n    }\n\n    /// Returns the previous sibling of this node that is an element.\n    #[inline]\n    pub fn prev_sibling_element(&self) -> Option<GeckoElement<'ln>> {\n        if !self.parent_node()?.may_have_element_children() {\n            return None;\n        }\n        let mut prev = self.prev_sibling();\n        while let Some(p) = prev {\n            if let Some(e) = p.as_element() {\n                return Some(e);\n            }\n            prev = p.prev_sibling();\n        }\n        None\n    }\n\n    /// Returns the next sibling of this node that is an element.\n    #[inline]\n    pub fn next_sibling_element(&self) -> Option<GeckoElement<'ln>> {\n        if !self.parent_node()?.may_have_element_children() {\n            return None;\n        }\n        let mut next = self.next_sibling();\n        while let Some(n) = next {\n            if let Some(e) = n.as_element() {\n                return Some(e);\n            }\n            next = n.next_sibling();\n        }\n        None\n    }\n\n    /// Returns last child sibling of this node that is an element.\n    #[inline]\n    pub fn last_child_element(&self) -> Option<GeckoElement<'ln>> {\n        let last = match self.last_child() {\n            Some(n) => n,\n            None => return None,\n        };\n        if let Some(e) = last.as_element() {\n            return Some(e);\n        }\n        None\n    }\n}\n\nimpl<'ln> NodeInfo for GeckoNode<'ln> {\n    #[inline]\n    fn is_element(&self) -> bool {\n        self.get_bool_flag(nsINode_BooleanFlag::NodeIsElement)\n    }\n\n    fn is_text_node(&self) -> bool {\n        // This is a DOM constant that isn't going to change.\n        const TEXT_NODE: u16 = 3;\n        self.node_info().mInner.mNodeType == TEXT_NODE\n    }\n}\n\nimpl<'ln> TNode for GeckoNode<'ln> {\n    type ConcreteDocument = GeckoDocument<'ln>;\n    type ConcreteShadowRoot = GeckoShadowRoot<'ln>;\n    type ConcreteElement = GeckoElement<'ln>;\n\n    #[inline]\n    fn parent_node(&self) -> Option<Self> {\n        unsafe { self.0.mParent.as_ref().map(GeckoNode) }\n    }\n\n    #[inline]\n    fn first_child(&self) -> Option<Self> {\n        unsafe {\n            self.0\n                .mFirstChild\n                .raw()\n                .as_ref()\n                .map(GeckoNode::from_content)\n        }\n    }\n\n    #[inline]\n    fn last_child(&self) -> Option<Self> {\n        unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) }\n    }\n\n    #[inline]\n    fn prev_sibling(&self) -> Option<Self> {\n        unsafe {\n            let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?);\n            if prev_or_last.0.mNextSibling.raw().is_null() {\n                return None;\n            }\n            Some(prev_or_last)\n        }\n    }\n\n    #[inline]\n    fn next_sibling(&self) -> Option<Self> {\n        unsafe {\n            self.0\n                .mNextSibling\n                .raw()\n                .as_ref()\n                .map(GeckoNode::from_content)\n        }\n    }\n\n    #[inline]\n    fn owner_doc(&self) -> Self::ConcreteDocument {\n        debug_assert!(!self.node_info().mDocument.is_null());\n        GeckoDocument(unsafe { &*self.node_info().mDocument })\n    }\n\n    #[inline]\n    fn is_in_document(&self) -> bool {\n        self.get_bool_flag(nsINode_BooleanFlag::IsInDocument)\n    }\n\n    fn traversal_parent(&self) -> Option<GeckoElement<'ln>> {\n        self.flattened_tree_parent().and_then(|n| n.as_element())\n    }\n\n    #[inline]\n    fn opaque(&self) -> OpaqueNode {\n        let ptr: usize = self.0 as *const _ as usize;\n        OpaqueNode(ptr)\n    }\n\n    fn debug_id(self) -> usize {\n        unimplemented!()\n    }\n\n    #[inline]\n    fn as_element(&self) -> Option<GeckoElement<'ln>> {\n        if !self.is_element() {\n            return None;\n        }\n\n        Some(GeckoElement(unsafe {\n            &*(self.0 as *const _ as *const RawGeckoElement)\n        }))\n    }\n\n    #[inline]\n    fn as_document(&self) -> Option<Self::ConcreteDocument> {\n        if !self.is_document() {\n            return None;\n        }\n\n        debug_assert_eq!(self.owner_doc().as_node(), *self, \"How?\");\n        Some(self.owner_doc())\n    }\n\n    #[inline]\n    fn as_shadow_root(&self) -> Option<Self::ConcreteShadowRoot> {\n        if !self.is_shadow_root() {\n            return None;\n        }\n\n        Some(GeckoShadowRoot(unsafe {\n            &*(self.0 as *const _ as *const structs::ShadowRoot)\n        }))\n    }\n}\n\n/// A wrapper on top of two kind of iterators, depending on the parent being\n/// iterated.\n///\n/// We generally iterate children by traversing the light-tree siblings of the\n/// first child like Servo does.\n///\n/// However, for nodes with anonymous children, we use a custom (heavier-weight)\n/// Gecko-implemented iterator.\n///\n/// FIXME(emilio): If we take into account shadow DOM, we're going to need the\n/// flat tree pretty much always. We can try to optimize the case where there's\n/// no shadow root sibling, probably.\npub enum GeckoChildrenIterator<'a> {\n    /// A simple iterator that tracks the current node being iterated and\n    /// replaces it with the next sibling when requested.\n    Current(Option<GeckoNode<'a>>),\n    /// A Gecko-implemented iterator we need to drop appropriately.\n    GeckoIterator(mem::ManuallyDrop<structs::StyleChildrenIterator>),\n}\n\nimpl<'a> Drop for GeckoChildrenIterator<'a> {\n    fn drop(&mut self) {\n        if let GeckoChildrenIterator::GeckoIterator(ref mut it) = *self {\n            unsafe {\n                bindings::Gecko_DestroyStyleChildrenIterator(&mut **it);\n            }\n        }\n    }\n}\n\nimpl<'a> Iterator for GeckoChildrenIterator<'a> {\n    type Item = GeckoNode<'a>;\n    fn next(&mut self) -> Option<GeckoNode<'a>> {\n        match *self {\n            GeckoChildrenIterator::Current(curr) => {\n                let next = curr.and_then(|node| node.next_sibling());\n                *self = GeckoChildrenIterator::Current(next);\n                curr\n            },\n            GeckoChildrenIterator::GeckoIterator(ref mut it) => unsafe {\n                // We do this unsafe lengthening of the lifetime here because\n                // structs::StyleChildrenIterator is actually StyleChildrenIterator<'a>,\n                // however we can't express this easily with bindgen, and it would\n                // introduce functions with two input lifetimes into bindgen,\n                // which would be out of scope for elision.\n                bindings::Gecko_GetNextStyleChild(&mut **it)\n                    .as_ref()\n                    .map(GeckoNode)\n            },\n        }\n    }\n}\n\n/// A simple wrapper over a non-null Gecko `Element` pointer.\n#[derive(Clone, Copy)]\npub struct GeckoElement<'le>(pub &'le RawGeckoElement);\n\nimpl<'le> fmt::Debug for GeckoElement<'le> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        use nsstring::nsCString;\n\n        write!(f, \"<{}\", self.local_name())?;\n\n        let mut attrs = nsCString::new();\n        unsafe {\n            bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs);\n        }\n        write!(f, \"{}\", attrs)?;\n        write!(f, \"> ({:#x})\", self.as_node().opaque().0)\n    }\n}\n\nimpl<'le> GeckoElement<'le> {\n    /// Gets the raw `ElementDataWrapper` for the element.\n    #[inline(always)]\n    pub fn get_data(&self) -> Option<&ElementDataWrapper> {\n        unsafe { self.0.mServoData.get().as_ref() }\n    }\n\n    /// Returns whether any animation applies to this element.\n    #[inline]\n    pub fn has_any_animation(&self) -> bool {\n        self.may_have_animations() && unsafe { Gecko_ElementHasAnimations(self.0) }\n    }\n\n    /// Check if mImpl contains a real pointer (not a bloom filter).\n    #[inline(always)]\n    fn has_attr_impl(&self) -> bool {\n        let ptr = self.0.mAttrs.mImpl.mPtr as usize;\n        ptr != 0 && (ptr & 1) == 0\n    }\n\n    #[inline(always)]\n    fn attrs(&self) -> &[structs::AttrArray_InternalAttr] {\n        unsafe {\n            if !self.has_attr_impl() {\n                return &[];\n            }\n            match self.0.mAttrs.mImpl.mPtr.as_ref() {\n                Some(attrs) => attrs.mBuffer.as_slice(attrs.mAttrCount as usize),\n                None => &[],\n            }\n        }\n    }\n\n    #[inline(always)]\n    fn get_part_attr(&self) -> Option<&structs::nsAttrValue> {\n        if !self.has_part_attr() {\n            return None;\n        }\n        snapshot_helpers::find_attr(self.attrs(), &atom!(\"part\"))\n    }\n\n    #[inline(always)]\n    fn get_class_attr(&self) -> Option<&structs::nsAttrValue> {\n        if !self.may_have_class() {\n            return None;\n        }\n\n        if self.is_svg_element() {\n            let svg_class = unsafe { bindings::Gecko_GetSVGAnimatedClass(self.0).as_ref() };\n            if let Some(c) = svg_class {\n                return Some(c);\n            }\n        }\n\n        snapshot_helpers::find_attr(self.attrs(), &atom!(\"class\"))\n    }\n\n    #[inline]\n    fn may_have_anonymous_children(&self) -> bool {\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveAnonymousChildren)\n    }\n\n    #[inline]\n    fn flags(&self) -> u32 {\n        self.as_node().flags()\n    }\n\n    #[inline]\n    fn set_flags(&self, flags: u32) {\n        self.as_node().set_flags(flags);\n    }\n\n    #[inline]\n    unsafe fn unset_flags(&self, flags: u32) {\n        self.as_node()\n            .flags_atomic()\n            .fetch_and(!flags, Ordering::Relaxed);\n    }\n\n    /// Returns true if this element has descendants for lazy frame construction.\n    #[inline]\n    pub fn descendants_need_frames(&self) -> bool {\n        self.flags() & NODE_DESCENDANTS_NEED_FRAMES != 0\n    }\n\n    /// Returns true if this element needs lazy frame construction.\n    #[inline]\n    pub fn needs_frame(&self) -> bool {\n        self.flags() & NODE_NEEDS_FRAME != 0\n    }\n\n    /// Returns a reference to the DOM slots for this Element, if they exist.\n    #[inline]\n    fn dom_slots(&self) -> Option<&structs::FragmentOrElement_nsDOMSlots> {\n        let slots = self.as_node().0.mSlots as *const structs::FragmentOrElement_nsDOMSlots;\n        unsafe { slots.as_ref() }\n    }\n\n    /// Returns a reference to the extended DOM slots for this Element.\n    #[inline]\n    fn extended_slots(&self) -> Option<&structs::FragmentOrElement_nsExtendedDOMSlots> {\n        self.dom_slots().and_then(|s| unsafe {\n            // For the bit usage, see nsContentSlots::GetExtendedSlots.\n            let e_slots = s._base.mExtendedSlots\n                & !structs::nsIContent_nsContentSlots_sNonOwningExtendedSlotsFlag;\n            (e_slots as *const structs::FragmentOrElement_nsExtendedDOMSlots).as_ref()\n        })\n    }\n\n    #[inline]\n    fn namespace_id(&self) -> i32 {\n        self.as_node().node_info().mInner.mNamespaceID\n    }\n\n    #[inline]\n    fn has_id(&self) -> bool {\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementHasID)\n    }\n\n    #[inline]\n    fn state_internal(&self) -> u64 {\n        if !self\n            .as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementHasLockedStyleStates)\n        {\n            return self.0.mState.bits;\n        }\n        unsafe { Gecko_ElementState(self.0) }\n    }\n\n    #[inline]\n    fn document_state(&self) -> DocumentState {\n        DocumentState::from_bits_retain(self.as_node().owner_doc().0.mState.bits)\n    }\n\n    #[inline]\n    fn may_have_class(&self) -> bool {\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveClass)\n    }\n\n    #[inline]\n    fn has_properties(&self) -> bool {\n        use crate::gecko_bindings::structs::NODE_HAS_PROPERTIES;\n\n        self.flags() & NODE_HAS_PROPERTIES != 0\n    }\n\n    #[inline]\n    fn may_have_style_attribute(&self) -> bool {\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementMayHaveStyle)\n    }\n\n    /// Only safe to call on the main thread, with exclusive access to the\n    /// element and its ancestors.\n    ///\n    /// This function is also called after display property changed for SMIL\n    /// animation.\n    ///\n    /// Also this function schedules style flush.\n    pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) {\n        use crate::gecko::restyle_damage::GeckoRestyleDamage;\n\n        let damage = GeckoRestyleDamage::new(change_hint);\n        debug!(\n            \"note_explicit_hints: {:?}, restyle_hint={:?}, change_hint={:?}\",\n            self, restyle_hint, change_hint\n        );\n        debug_assert!(bindings::Gecko_IsMainThread());\n        debug_assert!(\n            !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()),\n            \"Animation restyle hints should not appear with non-animation restyle hints\"\n        );\n\n        let Some(mut data) = self.mutate_data() else {\n            debug!(\"(Element not styled, discarding hints)\");\n            return;\n        };\n\n        // Propagate the bit up the chain.\n        if restyle_hint.has_animation_hint() {\n            bindings::Gecko_NoteAnimationOnlyDirtyElement(self.0);\n        } else {\n            bindings::Gecko_NoteDirtyElement(self.0);\n        }\n\n        data.hint.insert(restyle_hint);\n        data.damage |= damage;\n    }\n\n    /// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.\n    #[inline]\n    fn is_root_of_native_anonymous_subtree(&self) -> bool {\n        return self.flags() & structs::NODE_IS_NATIVE_ANONYMOUS_ROOT != 0;\n    }\n\n    /// Whether the element is in an anonymous subtree. Note that this includes UA widgets!\n    #[inline]\n    fn in_native_anonymous_subtree(&self) -> bool {\n        (self.flags() & structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) != 0\n    }\n\n    /// Whether the element has been in a UA widget\n    #[inline]\n    fn in_ua_widget(&self) -> bool {\n        (self.flags() & structs::NODE_HAS_BEEN_IN_UA_WIDGET) != 0\n    }\n\n    fn css_transitions_info(&self) -> FxHashMap<OwnedPropertyDeclarationId, Arc<AnimationValue>> {\n        use crate::gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt;\n        use crate::gecko_bindings::bindings::Gecko_ElementTransitions_Length;\n\n        let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize;\n        let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default());\n\n        for i in 0..collection_length {\n            let end_value =\n                unsafe { Arc::from_raw_addrefed(Gecko_ElementTransitions_EndValueAt(self.0, i)) };\n            let property = end_value.id();\n            debug_assert!(!property.is_logical());\n            map.insert(property.to_owned(), end_value);\n        }\n        map\n    }\n\n    fn needs_transitions_update_per_property(\n        &self,\n        property_declaration_id: PropertyDeclarationId,\n        combined_duration_seconds: f32,\n        before_change_style: &ComputedValues,\n        after_change_style: &ComputedValues,\n        existing_transitions: &FxHashMap<OwnedPropertyDeclarationId, Arc<AnimationValue>>,\n    ) -> bool {\n        debug_assert!(!property_declaration_id.is_logical());\n\n        // If there is an existing transition, update only if the end value\n        // differs.\n        //\n        // If the end value has not changed, we should leave the currently\n        // running transition as-is since we don't want to interrupt its timing\n        // function.\n        if let Some(ref existing) = existing_transitions.get(&property_declaration_id.to_owned()) {\n            let after_value =\n                AnimationValue::from_computed_values(property_declaration_id, after_change_style);\n            debug_assert!(\n                after_value.is_some()\n                    || matches!(property_declaration_id, PropertyDeclarationId::Custom(..))\n            );\n            return after_value.is_none() || ***existing != after_value.unwrap();\n        }\n\n        if combined_duration_seconds <= 0.0f32 {\n            return false;\n        }\n\n        AnimationValue::is_different_for(\n            property_declaration_id,\n            before_change_style,\n            after_change_style,\n        )\n    }\n\n    /// Get slow selector flags required for nth-of invalidation.\n    pub fn slow_selector_flags(&self) -> ElementSelectorFlags {\n        slow_selector_flags_from_node_selector_flags(self.as_node().selector_flags())\n    }\n\n    /// Returns whether this element is an HTML <video> or <audio> element.\n    #[inline]\n    pub fn is_html_media_element(&self) -> bool {\n        self.is_html_element()\n            && (self.local_name().as_ptr() == local_name!(\"video\").as_ptr()\n                || self.local_name().as_ptr() == local_name!(\"audio\").as_ptr())\n    }\n}\n\n/// Convert slow selector flags from the raw `NodeSelectorFlags`.\npub fn slow_selector_flags_from_node_selector_flags(flags: u32) -> ElementSelectorFlags {\n    use crate::gecko_bindings::structs::NodeSelectorFlags;\n    let mut result = ElementSelectorFlags::empty();\n    if flags & NodeSelectorFlags::HasSlowSelector.0 != 0 {\n        result.insert(ElementSelectorFlags::HAS_SLOW_SELECTOR);\n    }\n    if flags & NodeSelectorFlags::HasSlowSelectorLaterSiblings.0 != 0 {\n        result.insert(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS);\n    }\n    result\n}\n\n/// Converts flags from the layout used by rust-selectors to the layout used\n/// by Gecko. We could align these and then do this without conditionals, but\n/// it's probably not worth the trouble.\nfn selector_flags_to_node_flags(flags: ElementSelectorFlags) -> u32 {\n    use crate::gecko_bindings::structs::NodeSelectorFlags;\n    let mut gecko_flags = 0u32;\n    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR) {\n        gecko_flags |= NodeSelectorFlags::HasSlowSelector.0;\n    }\n    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {\n        gecko_flags |= NodeSelectorFlags::HasSlowSelectorLaterSiblings.0;\n    }\n    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH) {\n        gecko_flags |= NodeSelectorFlags::HasSlowSelectorNth.0;\n    }\n    if flags.contains(ElementSelectorFlags::HAS_SLOW_SELECTOR_NTH_OF) {\n        gecko_flags |= NodeSelectorFlags::HasSlowSelectorNthOf.0;\n    }\n    if flags.contains(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {\n        gecko_flags |= NodeSelectorFlags::HasEdgeChildSelector.0;\n    }\n    if flags.contains(ElementSelectorFlags::HAS_EMPTY_SELECTOR) {\n        gecko_flags |= NodeSelectorFlags::HasEmptySelector.0;\n    }\n    if flags.contains(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR) {\n        gecko_flags |= NodeSelectorFlags::RelativeSelectorAnchor.0;\n    }\n    if flags.contains(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT) {\n        gecko_flags |= NodeSelectorFlags::RelativeSelectorAnchorNonSubject.0;\n    }\n    if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR) {\n        gecko_flags |= NodeSelectorFlags::RelativeSelectorSearchDirectionAncestor.0;\n    }\n    if flags.contains(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING) {\n        gecko_flags |= NodeSelectorFlags::RelativeSelectorSearchDirectionSibling.0;\n    }\n\n    gecko_flags\n}\n\nfn get_animation_rule(\n    element: &GeckoElement,\n    cascade_level: CascadeLevel,\n) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {\n    // There's a very rough correlation between the number of effects\n    // (animations) on an element and the number of properties it is likely to\n    // animate, so we use that as an initial guess for the size of the\n    // AnimationValueMap in order to reduce the number of re-allocations needed.\n    let effect_count = unsafe { Gecko_GetAnimationEffectCount(element.0) };\n    // Also, we should try to reuse the PDB, to avoid creating extra rule nodes.\n    let mut animation_values = AnimationValueMap::with_capacity_and_hasher(\n        effect_count.min(crate::properties::property_counts::ANIMATABLE),\n        Default::default(),\n    );\n    if unsafe { Gecko_GetAnimationRule(element.0, cascade_level, &mut animation_values) } {\n        let shared_lock = &GLOBAL_STYLE_DATA.shared_lock;\n        Some(Arc::new(shared_lock.wrap(\n            PropertyDeclarationBlock::from_animation_value_map(&animation_values),\n        )))\n    } else {\n        None\n    }\n}\n\n/// Turns a gecko namespace id into an atom. Might panic if you pass any random thing that isn't a\n/// namespace id.\n#[inline(always)]\npub unsafe fn namespace_id_to_atom(id: i32) -> *mut nsAtom {\n    unsafe {\n        let namespace_manager = structs::nsNameSpaceManager_sInstance.mRawPtr;\n        (&(*namespace_manager).mURIArray)[id as usize].mRawPtr\n    }\n}\n\nimpl<'le> TElement for GeckoElement<'le> {\n    type ConcreteNode = GeckoNode<'le>;\n    type TraversalChildrenIterator = GeckoChildrenIterator<'le>;\n\n    fn implicit_scope_for_sheet_in_shadow_root(\n        opaque_host: OpaqueElement,\n        sheet_index: usize,\n    ) -> Option<ImplicitScopeRoot> {\n        // As long as this \"unopaqued\" element does not escape this function, we're not leaking\n        // potentially-mutable elements from opaque elements.\n        let e = unsafe {\n            Self(\n                opaque_host\n                    .as_const_ptr::<RawGeckoElement>()\n                    .as_ref()\n                    .unwrap(),\n            )\n        };\n        let shadow_root = match e.shadow_root() {\n            None => return None,\n            Some(r) => r,\n        };\n        shadow_root.implicit_scope_for_sheet(sheet_index)\n    }\n\n    fn inheritance_parent(&self) -> Option<Self> {\n        if let Some(pseudo) = self.implemented_pseudo_element() {\n            if !pseudo.is_element_backed() {\n                return self.pseudo_element_originating_element();\n            }\n        }\n\n        self.as_node()\n            .flattened_tree_parent()\n            .and_then(|n| n.as_element())\n    }\n\n    fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {\n        // This condition is similar to the check that\n        // StyleChildrenIterator::IsNeeded does, except that it might return\n        // true if we used to (but no longer) have anonymous content from\n        // ::before/::after, or nsIAnonymousContentCreators.\n        if self.is_html_slot_element()\n            || self.shadow_root().is_some()\n            || self.may_have_anonymous_children()\n        {\n            unsafe {\n                let mut iter = mem::MaybeUninit::<structs::StyleChildrenIterator>::uninit();\n                bindings::Gecko_ConstructStyleChildrenIterator(self.0, iter.as_mut_ptr());\n                return LayoutIterator(GeckoChildrenIterator::GeckoIterator(\n                    mem::ManuallyDrop::new(iter.assume_init()),\n                ));\n            }\n        }\n\n        LayoutIterator(GeckoChildrenIterator::Current(self.as_node().first_child()))\n    }\n\n    #[inline]\n    fn is_html_element(&self) -> bool {\n        self.namespace_id() == structs::kNameSpaceID_XHTML as i32\n    }\n\n    #[inline]\n    fn is_mathml_element(&self) -> bool {\n        self.namespace_id() == structs::kNameSpaceID_MathML as i32\n    }\n\n    #[inline]\n    fn is_svg_element(&self) -> bool {\n        self.namespace_id() == structs::kNameSpaceID_SVG as i32\n    }\n\n    #[inline]\n    fn is_xul_element(&self) -> bool {\n        self.namespace_id() == structs::root::kNameSpaceID_XUL as i32\n    }\n\n    #[inline]\n    fn subtree_bloom_filter(&self) -> u64 {\n        unsafe { bindings::Gecko_Element_GetSubtreeBloomFilter(self.0) }\n    }\n\n    #[inline]\n    fn local_name(&self) -> &WeakAtom {\n        unsafe { WeakAtom::new(self.as_node().node_info().mInner.mName) }\n    }\n\n    #[inline]\n    fn namespace(&self) -> &WeakNamespace {\n        unsafe { WeakNamespace::new(namespace_id_to_atom(self.namespace_id())) }\n    }\n\n    #[inline]\n    fn query_container_size(&self, display: &Display) -> Size2D<Option<Au>> {\n        // If an element gets 'display: contents' and its nsIFrame has not been removed yet,\n        // Gecko_GetQueryContainerSize will not notice that it can't have size containment.\n        // Other cases like 'display: inline' will be handled once the new nsIFrame is created.\n        if display.is_contents() {\n            return Size2D::new(None, None);\n        }\n\n        let mut width = -1;\n        let mut height = -1;\n        unsafe {\n            bindings::Gecko_GetQueryContainerSize(self.0, &mut width, &mut height);\n        }\n        Size2D::new(\n            if width >= 0 { Some(Au(width)) } else { None },\n            if height >= 0 { Some(Au(height)) } else { None },\n        )\n    }\n\n    /// Return the list of slotted nodes of this node.\n    #[inline]\n    fn slotted_nodes(&self) -> &[Self::ConcreteNode] {\n        if !self.is_html_slot_element() || !self.as_node().is_in_shadow_tree() {\n            return &[];\n        }\n        if cfg!(debug_assertions) {\n            let slot: &structs::HTMLSlotElement = unsafe { mem::transmute(self.0) };\n            let base: &RawGeckoElement = &slot._base._base._base;\n            assert_eq!(base as *const _, self.0 as *const _, \"Bad cast\");\n        }\n        unsafe {\n            let nodes = bindings::Gecko_GetAssignedNodes(self.0);\n            let nodes: &[*const RawGeckoNode] =\n                std::slice::from_raw_parts(nodes.begin, nodes.length);\n            // NOTE(emilio): We rely on the in-memory representation of\n            // GeckoNode<'ld> and *const RawGeckoNode being the same.\n            #[allow(dead_code)]\n            unsafe fn static_assert() {\n                mem::transmute::<*mut RawGeckoNode, GeckoNode<'static>>(0xbadc0de as *mut _);\n            }\n            mem::transmute(nodes)\n        }\n    }\n\n    #[inline]\n    fn shadow_root(&self) -> Option<GeckoShadowRoot<'le>> {\n        let slots = self.extended_slots()?;\n        unsafe { slots.mShadowRoot.mRawPtr.as_ref().map(GeckoShadowRoot) }\n    }\n\n    fn note_highlight_pseudo_style_invalidated(&self) {\n        let doc = self.as_node().owner_doc().0;\n        unsafe {\n            bindings::Gecko_NoteHighlightPseudoStyleInvalidated(doc);\n        }\n    }\n\n    #[inline]\n    fn containing_shadow(&self) -> Option<GeckoShadowRoot<'le>> {\n        if !self.as_node().is_in_shadow_tree() {\n            return None;\n        }\n        let sr = self.as_node().0.mSubtreeRoot as *const structs::ShadowRoot;\n        Some(GeckoShadowRoot(unsafe { &*sr }))\n    }\n\n    fn each_anonymous_content_child<F>(&self, mut f: F)\n    where\n        F: FnMut(Self),\n    {\n        if !self.may_have_anonymous_children() {\n            return;\n        }\n\n        thin_vec::auto_thin_vec!(let array: [*mut nsIContent; 8]);\n        unsafe {\n            bindings::Gecko_GetAnonymousContentForElement(self.0, array.as_mut().as_mut_ptr())\n        };\n        for content in array.iter() {\n            let node = GeckoNode::from_content(unsafe { &**content });\n            let element = match node.as_element() {\n                Some(e) => e,\n                None => continue,\n            };\n            f(element);\n        }\n    }\n\n    #[inline]\n    fn as_node(&self) -> Self::ConcreteNode {\n        unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }\n    }\n\n    fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {\n        self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _\n    }\n\n    fn style_attribute(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {\n        if !self.may_have_style_attribute() {\n            return None;\n        }\n\n        unsafe {\n            let declarations = Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref()?;\n            Some(ArcBorrow::from_ref(declarations))\n        }\n    }\n\n    fn unset_dirty_style_attribute(&self) {\n        if !self.may_have_style_attribute() {\n            return;\n        }\n\n        unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };\n    }\n\n    fn smil_override(&self) -> Option<ArcBorrow<'_, Locked<PropertyDeclarationBlock>>> {\n        unsafe {\n            let slots = self.extended_slots()?;\n\n            let declaration: &structs::DeclarationBlock =\n                slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;\n\n            let raw: &structs::StyleLockedDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;\n            Some(ArcBorrow::from_ref(raw))\n        }\n    }\n\n    fn animation_rule(\n        &self,\n        _: &SharedStyleContext,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {\n        get_animation_rule(self, CascadeLevel::Animations)\n    }\n\n    fn transition_rule(\n        &self,\n        _: &SharedStyleContext,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {\n        get_animation_rule(self, CascadeLevel::Transitions)\n    }\n\n    #[inline]\n    fn state(&self) -> ElementState {\n        ElementState::from_bits_retain(self.state_internal())\n    }\n\n    #[inline]\n    fn has_part_attr(&self) -> bool {\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementHasPart)\n    }\n\n    #[inline]\n    fn exports_any_part(&self) -> bool {\n        snapshot_helpers::find_attr(self.attrs(), &atom!(\"exportparts\")).is_some()\n    }\n\n    // FIXME(emilio): we should probably just return a reference to the Atom.\n    #[inline]\n    fn id(&self) -> Option<&WeakAtom> {\n        if !self.has_id() {\n            return None;\n        }\n        snapshot_helpers::get_id(self.attrs())\n    }\n\n    fn each_attr_name<F>(&self, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        for attr in self.attrs() {\n            unsafe { AtomIdent::with(attr.mName.name(), |a| callback(a)) }\n        }\n    }\n\n    fn each_class<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        let attr = match self.get_class_attr() {\n            Some(c) => c,\n            None => return,\n        };\n\n        snapshot_helpers::each_class_or_part(attr, callback)\n    }\n\n    #[inline]\n    fn each_custom_state<F>(&self, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        if let Some(slots) = self.extended_slots() {\n            unsafe {\n                for atom in slots.mCustomStates.iter() {\n                    AtomIdent::with(atom.mRawPtr, &mut callback)\n                }\n            }\n        }\n    }\n\n    #[inline]\n    fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        snapshot_helpers::each_exported_part(self.attrs(), name, callback)\n    }\n\n    fn each_part<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        let attr = match self.get_part_attr() {\n            Some(c) => c,\n            None => return,\n        };\n\n        snapshot_helpers::each_class_or_part(attr, callback)\n    }\n\n    #[inline]\n    fn has_snapshot(&self) -> bool {\n        self.flags() & ELEMENT_HAS_SNAPSHOT != 0\n    }\n\n    #[inline]\n    fn handled_snapshot(&self) -> bool {\n        self.flags() & ELEMENT_HANDLED_SNAPSHOT != 0\n    }\n\n    unsafe fn set_handled_snapshot(&self) {\n        debug_assert!(self.has_data());\n        self.set_flags(ELEMENT_HANDLED_SNAPSHOT)\n    }\n\n    #[inline]\n    fn has_dirty_descendants(&self) -> bool {\n        self.flags() & ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO != 0\n    }\n\n    unsafe fn set_dirty_descendants(&self) {\n        debug_assert!(self.has_data());\n        debug!(\"Setting dirty descendants: {:?}\", self);\n        self.set_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)\n    }\n\n    unsafe fn unset_dirty_descendants(&self) {\n        self.unset_flags(ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO)\n    }\n\n    #[inline]\n    fn has_animation_only_dirty_descendants(&self) -> bool {\n        self.flags() & ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO != 0\n    }\n\n    unsafe fn set_animation_only_dirty_descendants(&self) {\n        self.set_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)\n    }\n\n    unsafe fn unset_animation_only_dirty_descendants(&self) {\n        self.unset_flags(ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO)\n    }\n\n    unsafe fn clear_descendant_bits(&self) {\n        self.unset_flags(\n            ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO\n                | ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO\n                | NODE_DESCENDANTS_NEED_FRAMES,\n        )\n    }\n\n    fn is_visited_link(&self) -> bool {\n        self.state().intersects(ElementState::VISITED)\n    }\n\n    /// We want to match rules from the same tree in all cases, except for native anonymous content\n    /// that _isn't_ part directly of a UA widget (e.g., such generated by form controls, or\n    /// pseudo-elements).\n    #[inline]\n    fn matches_user_and_content_rules(&self) -> bool {\n        !self.in_native_anonymous_subtree() || self.in_ua_widget()\n    }\n\n    #[inline]\n    fn implemented_pseudo_element(&self) -> Option<PseudoElement> {\n        if !self.in_native_anonymous_subtree() {\n            return None;\n        }\n\n        if !self.has_properties() {\n            return None;\n        }\n\n        let name = unsafe { bindings::Gecko_GetImplementedPseudoIdentifier(self.0) };\n        PseudoElement::from_pseudo_type(\n            unsafe { bindings::Gecko_GetImplementedPseudoType(self.0) },\n            if name.is_null() {\n                None\n            } else {\n                Some(AtomIdent::new(unsafe { Atom::from_raw(name) }))\n            },\n        )\n    }\n\n    #[inline]\n    fn store_children_to_process(&self, _: isize) {\n        // This is only used for bottom-up traversal, and is thus a no-op for Gecko.\n    }\n\n    fn did_process_child(&self) -> isize {\n        panic!(\"Atomic child count not implemented in Gecko\");\n    }\n\n    unsafe fn ensure_data(&self) -> ElementDataMut<'_> {\n        if !self.has_data() {\n            debug!(\"Creating ElementData for {:?}\", self);\n            let ptr = Box::into_raw(Box::new(ElementDataWrapper::default()));\n            self.0.mServoData.set(ptr);\n        }\n        self.mutate_data().unwrap()\n    }\n\n    unsafe fn clear_data(&self) {\n        #[cfg(debug_assertions)]\n        {\n            // Perform a mutable borrow of the data in debug builds. This serves as an assertion\n            // that there are no outstanding borrows when we destroy the data.\n            let _ = self.mutate_data();\n        }\n        let ptr = self.0.mServoData.get();\n        self.unset_flags(\n            ELEMENT_HAS_SNAPSHOT\n                | ELEMENT_HANDLED_SNAPSHOT\n                | structs::Element_kAllServoDescendantBits\n                | NODE_NEEDS_FRAME,\n        );\n        if !ptr.is_null() {\n            debug!(\"Dropping ElementData for {:?}\", self);\n            let _data = Box::from_raw(self.0.mServoData.get());\n            self.0.mServoData.set(ptr::null_mut());\n        }\n    }\n\n    #[inline]\n    fn skip_item_display_fixup(&self) -> bool {\n        debug_assert!(\n            !self.is_pseudo_element(),\n            \"Just don't call me if I'm a pseudo, you should know the answer already\"\n        );\n        self.is_root_of_native_anonymous_subtree()\n    }\n\n    #[inline]\n    fn may_have_animations(&self) -> bool {\n        if let Some(pseudo) = self.implemented_pseudo_element() {\n            if pseudo.animations_stored_in_parent() {\n                // FIXME(emilio): When would the parent of a ::before / ::after\n                // pseudo-element be null?\n                return self.parent_element().map_or(false, |p| {\n                    p.as_node()\n                        .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)\n                });\n            }\n        }\n        self.as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ElementHasAnimations)\n    }\n\n    /// Update various animation-related state on a given (pseudo-)element as\n    /// results of normal restyle.\n    fn update_animations(\n        &self,\n        before_change_style: Option<Arc<ComputedValues>>,\n        tasks: UpdateAnimationsTasks,\n    ) {\n        // We have to update animations even if the element has no computed\n        // style since it means the element is in a display:none subtree, we\n        // should destroy all CSS animations in display:none subtree.\n        let computed_data = self.borrow_data();\n        let computed_values = computed_data.as_ref().map(|d| d.styles.primary());\n        let before_change_values = before_change_style\n            .as_ref()\n            .map_or(ptr::null(), |x| x.as_gecko_computed_style());\n        let computed_values_opt = computed_values\n            .as_ref()\n            .map_or(ptr::null(), |x| x.as_gecko_computed_style());\n        unsafe {\n            Gecko_UpdateAnimations(\n                self.0,\n                before_change_values,\n                computed_values_opt,\n                tasks.bits(),\n            );\n        }\n    }\n\n    #[inline]\n    fn has_animations(&self, _: &SharedStyleContext) -> bool {\n        self.has_any_animation()\n    }\n\n    fn has_css_animations(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {\n        self.may_have_animations() && unsafe { Gecko_ElementHasCSSAnimations(self.0) }\n    }\n\n    fn has_css_transitions(&self, _: &SharedStyleContext, _: Option<PseudoElement>) -> bool {\n        self.may_have_animations() && unsafe { Gecko_ElementHasCSSTransitions(self.0) }\n    }\n\n    // Detect if there are any changes that require us to update transitions.\n    //\n    // This is used as a more thoroughgoing check than the cheaper\n    // might_need_transitions_update check.\n    //\n    // The following logic shadows the logic used on the Gecko side\n    // (nsTransitionManager::DoUpdateTransitions) where we actually perform the\n    // update.\n    //\n    // https://drafts.csswg.org/css-transitions/#starting\n    fn needs_transitions_update(\n        &self,\n        before_change_style: &ComputedValues,\n        after_change_style: &ComputedValues,\n    ) -> bool {\n        let after_change_ui_style = after_change_style.get_ui();\n        let existing_transitions = self.css_transitions_info();\n\n        if after_change_style.get_box().clone_display().is_none() {\n            // We need to cancel existing transitions.\n            return !existing_transitions.is_empty();\n        }\n\n        let mut transitions_to_keep = PropertyDeclarationIdSet::default();\n        for transition_property in after_change_style.transition_properties() {\n            let physical_property = transition_property\n                .property\n                .as_borrowed()\n                .to_physical(after_change_style.writing_mode);\n            transitions_to_keep.insert(physical_property);\n            if self.needs_transitions_update_per_property(\n                physical_property,\n                after_change_ui_style\n                    .transition_combined_duration_at(transition_property.index)\n                    .seconds(),\n                before_change_style,\n                after_change_style,\n                &existing_transitions,\n            ) {\n                return true;\n            }\n        }\n\n        // Check if we have to cancel the running transition because this is not\n        // a matching transition-property value.\n        existing_transitions\n            .keys()\n            .any(|property| !transitions_to_keep.contains(property.as_borrowed()))\n    }\n\n    /// Whether there is an ElementData container.\n    #[inline]\n    fn has_data(&self) -> bool {\n        self.get_data().is_some()\n    }\n\n    /// Immutably borrows the ElementData.\n    fn borrow_data(&self) -> Option<ElementDataRef<'_>> {\n        self.get_data().map(|d| d.borrow())\n    }\n\n    /// Mutably borrows the ElementData.\n    fn mutate_data(&self) -> Option<ElementDataMut<'_>> {\n        self.get_data().map(|d| d.borrow_mut())\n    }\n\n    #[inline]\n    fn lang_attr(&self) -> Option<AttrValue> {\n        let ptr = unsafe { bindings::Gecko_LangValue(self.0) };\n        if ptr.is_null() {\n            None\n        } else {\n            Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))\n        }\n    }\n\n    fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {\n        // Gecko supports :lang() from CSS Selectors 4, which accepts a list\n        // of language tags, and does BCP47-style range matching.\n        let override_lang_ptr = match override_lang {\n            Some(Some(ref atom)) => atom.as_ptr(),\n            _ => ptr::null_mut(),\n        };\n        value.0.iter().any(|lang| unsafe {\n            Gecko_MatchLang(\n                self.0,\n                override_lang_ptr,\n                override_lang.is_some(),\n                lang.as_slice().as_ptr(),\n            )\n        })\n    }\n\n    fn is_html_document_body_element(&self) -> bool {\n        if self.local_name() != &**local_name!(\"body\") {\n            return false;\n        }\n\n        if !self.is_html_element() {\n            return false;\n        }\n\n        unsafe { bindings::Gecko_IsDocumentBody(self.0) }\n    }\n\n    fn synthesize_view_transition_dynamic_rules<V>(&self, rules: &mut V)\n    where\n        V: Push<ApplicableDeclarationBlock>,\n    {\n        use crate::stylesheets::layer_rule::LayerOrder;\n        let declarations = unsafe { bindings::Gecko_GetViewTransitionDynamicRule(self.0).as_ref() };\n        if let Some(decl) = declarations {\n            rules.push(ApplicableDeclarationBlock::from_declarations(\n                unsafe { Arc::from_raw_addrefed(decl) },\n                ServoCascadeLevel::new(ServoCascadeOrigin::UA),\n                LayerOrder::root(),\n            ));\n        }\n    }\n\n    fn synthesize_presentational_hints_for_legacy_attributes<V>(\n        &self,\n        visited_handling: VisitedHandlingMode,\n        hints: &mut V,\n    ) where\n        V: Push<ApplicableDeclarationBlock>,\n    {\n        use crate::properties::longhands::_x_lang::SpecifiedValue as SpecifiedLang;\n        use crate::properties::longhands::color::SpecifiedValue as SpecifiedColor;\n        use crate::stylesheets::layer_rule::LayerOrder;\n        use crate::values::specified::{color::Color, font::XTextScale};\n        use std::sync::LazyLock;\n\n        static TABLE_COLOR_RULE: LazyLock<ApplicableDeclarationBlock> = LazyLock::new(|| {\n            let global_style_data = &*GLOBAL_STYLE_DATA;\n            let pdb = PropertyDeclarationBlock::with_one(\n                PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),\n                Importance::Normal,\n            );\n            let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));\n            ApplicableDeclarationBlock::from_declarations(\n                arc,\n                ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                LayerOrder::root(),\n            )\n        });\n        static MATHML_LANG_RULE: LazyLock<ApplicableDeclarationBlock> = LazyLock::new(|| {\n            let global_style_data = &*GLOBAL_STYLE_DATA;\n            let pdb = PropertyDeclarationBlock::with_one(\n                PropertyDeclaration::XLang(SpecifiedLang(atom!(\"x-math\"))),\n                Importance::Normal,\n            );\n            let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));\n            ApplicableDeclarationBlock::from_declarations(\n                arc,\n                ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                LayerOrder::root(),\n            )\n        });\n        static SVG_TEXT_DISABLE_SCALE_RULE: LazyLock<ApplicableDeclarationBlock> =\n            LazyLock::new(|| {\n                let global_style_data = &*GLOBAL_STYLE_DATA;\n                let pdb = PropertyDeclarationBlock::with_one(\n                    PropertyDeclaration::XTextScale(XTextScale::None),\n                    Importance::Normal,\n                );\n                let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));\n                ApplicableDeclarationBlock::from_declarations(\n                    arc,\n                    ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                    LayerOrder::root(),\n                )\n            });\n\n        let ns = self.namespace_id();\n        // <th> elements get a default MozCenterOrInherit which may get overridden\n        if ns == structs::kNameSpaceID_XHTML as i32 {\n            if self.local_name().as_ptr() == atom!(\"table\").as_ptr()\n                && self.as_node().owner_doc().quirks_mode() == QuirksMode::Quirks\n            {\n                hints.push(TABLE_COLOR_RULE.clone());\n            }\n        }\n        if ns == structs::kNameSpaceID_SVG as i32 {\n            if self.local_name().as_ptr() == atom!(\"text\").as_ptr() {\n                hints.push(SVG_TEXT_DISABLE_SCALE_RULE.clone());\n            }\n        }\n        let declarations =\n            unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() };\n        if let Some(decl) = declarations {\n            hints.push(ApplicableDeclarationBlock::from_declarations(\n                unsafe { Arc::from_raw_addrefed(decl) },\n                ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                LayerOrder::root(),\n            ));\n        }\n        let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() };\n        if let Some(decl) = declarations {\n            hints.push(ApplicableDeclarationBlock::from_declarations(\n                unsafe { Arc::from_raw_addrefed(decl) },\n                ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                LayerOrder::root(),\n            ));\n        }\n\n        // Support for link, vlink, and alink presentation hints on <body>\n        if self.is_link() {\n            // Unvisited vs. visited styles are computed up-front based on the\n            // visited mode (not the element's actual state).\n            let declarations = match visited_handling {\n                VisitedHandlingMode::AllLinksVisitedAndUnvisited => {\n                    unreachable!(\n                        \"We should never try to selector match with \\\n                         AllLinksVisitedAndUnvisited\"\n                    );\n                },\n                VisitedHandlingMode::AllLinksUnvisited => unsafe {\n                    Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref()\n                },\n                VisitedHandlingMode::RelevantLinkVisited => unsafe {\n                    Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref()\n                },\n            };\n            if let Some(decl) = declarations {\n                hints.push(ApplicableDeclarationBlock::from_declarations(\n                    unsafe { Arc::from_raw_addrefed(decl) },\n                    ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                    LayerOrder::root(),\n                ));\n            }\n\n            let active = self\n                .state()\n                .intersects(NonTSPseudoClass::Active.state_flag());\n            if active {\n                let declarations =\n                    unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() };\n                if let Some(decl) = declarations {\n                    hints.push(ApplicableDeclarationBlock::from_declarations(\n                        unsafe { Arc::from_raw_addrefed(decl) },\n                        ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                        LayerOrder::root(),\n                    ));\n                }\n            }\n        }\n\n        // xml:lang has precedence over lang, which can be\n        // set by Gecko_GetHTMLPresentationAttrDeclarationBlock\n        //\n        // http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#language\n        let ptr = unsafe { bindings::Gecko_GetXMLLangValue(self.0) };\n        if !ptr.is_null() {\n            let global_style_data = &*GLOBAL_STYLE_DATA;\n\n            let pdb = PropertyDeclarationBlock::with_one(\n                PropertyDeclaration::XLang(SpecifiedLang(unsafe { Atom::from_addrefed(ptr) })),\n                Importance::Normal,\n            );\n            let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));\n            hints.push(ApplicableDeclarationBlock::from_declarations(\n                arc,\n                ServoCascadeLevel::new(ServoCascadeOrigin::PresHints),\n                LayerOrder::root(),\n            ))\n        }\n        // MathML's default lang has precedence over both `lang` and `xml:lang`\n        if !static_prefs::pref!(\"mathml.font_family_math.enabled\")\n            && ns == structs::kNameSpaceID_MathML as i32\n        {\n            if self.local_name().as_ptr() == atom!(\"math\").as_ptr() {\n                hints.push(MATHML_LANG_RULE.clone());\n            }\n        }\n    }\n\n    fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {\n        let node_flags = selector_flags_to_node_flags(flags);\n        self.as_node().selector_flags() & node_flags == node_flags\n    }\n\n    fn relative_selector_search_direction(&self) -> ElementSelectorFlags {\n        use crate::gecko_bindings::structs::NodeSelectorFlags;\n        let flags = self.as_node().selector_flags();\n        if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling.0) != 0 {\n            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING\n        } else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestor.0) != 0 {\n            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR\n        } else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionSibling.0) != 0 {\n            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING\n        } else {\n            ElementSelectorFlags::empty()\n        }\n    }\n}\n\nimpl<'le> AttributeProvider for GeckoElement<'le> {\n    fn get_attr(&self, attr: &LocalName, namespace: &Namespace) -> Option<String> {\n        //TODO(bug 2003334): Avoid unnecessary string copies/conversions here.\n        let mut result = nsString::new();\n        if unsafe {\n            bindings::Gecko_LookupAttrValue(\n                self.0,\n                namespace.as_ptr(),\n                attr.0.as_ptr(),\n                &mut *result,\n            )\n        } {\n            Some(result.to_string())\n        } else {\n            None\n        }\n    }\n}\n\nimpl<'le> PartialEq for GeckoElement<'le> {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        self.0 as *const _ == other.0 as *const _\n    }\n}\n\nimpl<'le> Eq for GeckoElement<'le> {}\n\nimpl<'le> Hash for GeckoElement<'le> {\n    #[inline]\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        (self.0 as *const RawGeckoElement).hash(state);\n    }\n}\n\nimpl<'le> ::selectors::Element for GeckoElement<'le> {\n    type Impl = SelectorImpl;\n\n    #[inline]\n    fn opaque(&self) -> OpaqueElement {\n        OpaqueElement::new(self.0)\n    }\n\n    #[inline]\n    fn parent_element(&self) -> Option<Self> {\n        let parent_node = self.as_node().parent_node();\n        parent_node.and_then(|n| n.as_element())\n    }\n\n    #[inline]\n    fn parent_node_is_shadow_root(&self) -> bool {\n        self.as_node()\n            .parent_node()\n            .map_or(false, |p| p.is_shadow_root())\n    }\n\n    #[inline]\n    fn containing_shadow_host(&self) -> Option<Self> {\n        let shadow = self.containing_shadow()?;\n        Some(shadow.host())\n    }\n\n    #[inline]\n    fn is_pseudo_element(&self) -> bool {\n        self.implemented_pseudo_element().is_some()\n    }\n\n    #[inline]\n    fn pseudo_element_originating_element(&self) -> Option<Self> {\n        debug_assert!(self.is_pseudo_element());\n        debug_assert!(self.in_native_anonymous_subtree());\n        if self.in_ua_widget() {\n            return self.containing_shadow_host();\n        }\n        let mut current = *self;\n        loop {\n            let anon_root = current.is_root_of_native_anonymous_subtree();\n            current = current.traversal_parent()?;\n            if anon_root {\n                return Some(current);\n            }\n        }\n    }\n\n    #[inline]\n    fn assigned_slot(&self) -> Option<Self> {\n        let slot = self.extended_slots()?._base.mAssignedSlot.mRawPtr;\n\n        unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base)) }\n    }\n\n    #[inline]\n    fn prev_sibling_element(&self) -> Option<Self> {\n        let mut sibling = self.as_node().prev_sibling();\n        while let Some(sibling_node) = sibling {\n            if let Some(el) = sibling_node.as_element() {\n                return Some(el);\n            }\n            sibling = sibling_node.prev_sibling();\n        }\n        None\n    }\n\n    #[inline]\n    fn next_sibling_element(&self) -> Option<Self> {\n        let mut sibling = self.as_node().next_sibling();\n        while let Some(sibling_node) = sibling {\n            if let Some(el) = sibling_node.as_element() {\n                return Some(el);\n            }\n            sibling = sibling_node.next_sibling();\n        }\n        None\n    }\n\n    #[inline]\n    fn first_element_child(&self) -> Option<Self> {\n        let mut child = self.as_node().first_child();\n        while let Some(child_node) = child {\n            if let Some(el) = child_node.as_element() {\n                return Some(el);\n            }\n            child = child_node.next_sibling();\n        }\n        None\n    }\n\n    fn apply_selector_flags(&self, flags: ElementSelectorFlags) {\n        // Handle flags that apply to the element.\n        let self_flags = flags.for_self();\n        if !self_flags.is_empty() {\n            self.as_node()\n                .set_selector_flags(selector_flags_to_node_flags(flags))\n        }\n\n        // Handle flags that apply to the parent.\n        let parent_flags = flags.for_parent();\n        if !parent_flags.is_empty() {\n            if let Some(p) = self.as_node().parent_node() {\n                if p.is_element() || p.is_shadow_root() {\n                    p.set_selector_flags(selector_flags_to_node_flags(parent_flags));\n                }\n            }\n        }\n    }\n\n    fn has_attr_in_no_namespace(&self, local_name: &LocalName) -> bool {\n        for attr in self.attrs() {\n            if attr.mName.mBits == local_name.as_ptr() as usize {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn attr_matches(\n        &self,\n        ns: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        operation: &AttrSelectorOperation<&AttrValue>,\n    ) -> bool {\n        snapshot_helpers::attr_matches(self.attrs(), ns, local_name, operation)\n    }\n\n    #[inline]\n    fn is_root(&self) -> bool {\n        if self\n            .as_node()\n            .get_bool_flag(nsINode_BooleanFlag::ParentIsContent)\n        {\n            return false;\n        }\n\n        if !self.as_node().is_in_document() {\n            return false;\n        }\n\n        debug_assert!(self\n            .as_node()\n            .parent_node()\n            .map_or(false, |p| p.is_document()));\n        // XXX this should always return true at this point, shouldn't it?\n        unsafe { bindings::Gecko_IsRootElement(self.0) }\n    }\n\n    fn is_empty(&self) -> bool {\n        !self\n            .as_node()\n            .dom_children()\n            .any(|child| unsafe { Gecko_IsSignificantChild(child.0, true) })\n    }\n\n    #[inline]\n    fn has_local_name(&self, name: &WeakAtom) -> bool {\n        self.local_name() == name\n    }\n\n    #[inline]\n    fn has_namespace(&self, ns: &WeakNamespace) -> bool {\n        self.namespace() == ns\n    }\n\n    #[inline]\n    fn is_same_type(&self, other: &Self) -> bool {\n        self.local_name() == other.local_name() && self.namespace() == other.namespace()\n    }\n\n    fn match_non_ts_pseudo_class(\n        &self,\n        pseudo_class: &NonTSPseudoClass,\n        context: &mut MatchingContext<Self::Impl>,\n    ) -> bool {\n        use selectors::matching::*;\n        match *pseudo_class {\n            NonTSPseudoClass::Autofill\n            | NonTSPseudoClass::Defined\n            | NonTSPseudoClass::Focus\n            | NonTSPseudoClass::Enabled\n            | NonTSPseudoClass::Disabled\n            | NonTSPseudoClass::Checked\n            | NonTSPseudoClass::Fullscreen\n            | NonTSPseudoClass::Indeterminate\n            | NonTSPseudoClass::MozInert\n            | NonTSPseudoClass::PopoverOpen\n            | NonTSPseudoClass::PlaceholderShown\n            | NonTSPseudoClass::Target\n            | NonTSPseudoClass::Valid\n            | NonTSPseudoClass::Invalid\n            | NonTSPseudoClass::MozBroken\n            | NonTSPseudoClass::Required\n            | NonTSPseudoClass::Optional\n            | NonTSPseudoClass::ReadOnly\n            | NonTSPseudoClass::ReadWrite\n            | NonTSPseudoClass::FocusWithin\n            | NonTSPseudoClass::FocusVisible\n            | NonTSPseudoClass::MozDragOver\n            | NonTSPseudoClass::MozDevtoolsHighlighted\n            | NonTSPseudoClass::MozStyleeditorTransitioning\n            | NonTSPseudoClass::MozMathIncrementScriptLevel\n            | NonTSPseudoClass::InRange\n            | NonTSPseudoClass::OutOfRange\n            | NonTSPseudoClass::Default\n            | NonTSPseudoClass::UserValid\n            | NonTSPseudoClass::UserInvalid\n            | NonTSPseudoClass::MozMeterOptimum\n            | NonTSPseudoClass::MozMeterSubOptimum\n            | NonTSPseudoClass::MozMeterSubSubOptimum\n            | NonTSPseudoClass::MozHasDirAttr\n            | NonTSPseudoClass::MozDirAttrLTR\n            | NonTSPseudoClass::MozDirAttrRTL\n            | NonTSPseudoClass::MozDirAttrLikeAuto\n            | NonTSPseudoClass::Modal\n            | NonTSPseudoClass::MozTopmostModal\n            | NonTSPseudoClass::Open\n            | NonTSPseudoClass::Active\n            | NonTSPseudoClass::Hover\n            | NonTSPseudoClass::HasSlotted\n            | NonTSPseudoClass::MozAutofillPreview\n            | NonTSPseudoClass::MozRevealed\n            | NonTSPseudoClass::ActiveViewTransition\n            | NonTSPseudoClass::MozValueEmpty\n            | NonTSPseudoClass::MozSuppressForPrintSelection\n            | NonTSPseudoClass::Seeking\n            | NonTSPseudoClass::Buffering\n            | NonTSPseudoClass::Stalled\n            | NonTSPseudoClass::Muted => self.state().intersects(pseudo_class.state_flag()),\n            NonTSPseudoClass::Paused => {\n                self.is_html_media_element() && self.state().intersects(ElementState::PAUSED)\n            },\n            NonTSPseudoClass::Playing => {\n                self.is_html_media_element() && !self.state().intersects(ElementState::PAUSED)\n            },\n            NonTSPseudoClass::VolumeLocked => false, // Bug 2013371\n            NonTSPseudoClass::Dir(ref dir) => self.state().intersects(dir.element_state()),\n            NonTSPseudoClass::ActiveViewTransitionType(ref types) => {\n                self.state().intersects(pseudo_class.state_flag())\n                    && unsafe {\n                        bindings::Gecko_HasActiveViewTransitionTypes(\n                            self.as_node().owner_doc().0,\n                            types,\n                        )\n                    }\n            },\n            NonTSPseudoClass::AnyLink => self.is_link(),\n            NonTSPseudoClass::Link => {\n                self.is_link() && context.visited_handling().matches_unvisited()\n            },\n            NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),\n            NonTSPseudoClass::Visited => {\n                self.is_link() && context.visited_handling().matches_visited()\n            },\n            NonTSPseudoClass::MozFirstNode => {\n                if context.needs_selector_flags() {\n                    self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);\n                }\n                let mut elem = self.as_node();\n                while let Some(prev) = elem.prev_sibling() {\n                    if prev.contains_non_whitespace_content() {\n                        return false;\n                    }\n                    elem = prev;\n                }\n                true\n            },\n            NonTSPseudoClass::MozLastNode => {\n                if context.needs_selector_flags() {\n                    self.apply_selector_flags(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR);\n                }\n                let mut elem = self.as_node();\n                while let Some(next) = elem.next_sibling() {\n                    if next.contains_non_whitespace_content() {\n                        return false;\n                    }\n                    elem = next;\n                }\n                true\n            },\n            NonTSPseudoClass::MozOnlyWhitespace => {\n                if context.needs_selector_flags() {\n                    self.apply_selector_flags(ElementSelectorFlags::HAS_EMPTY_SELECTOR);\n                }\n                if self\n                    .as_node()\n                    .dom_children()\n                    .any(|c| c.contains_non_whitespace_content())\n                {\n                    return false;\n                }\n                true\n            },\n            NonTSPseudoClass::MozNativeAnonymous => !self.matches_user_and_content_rules(),\n            NonTSPseudoClass::MozTableBorderNonzero => unsafe {\n                bindings::Gecko_IsTableBorderNonzero(self.0)\n            },\n            NonTSPseudoClass::MozSelectListBox => unsafe {\n                bindings::Gecko_IsSelectListBox(self.0)\n            },\n            NonTSPseudoClass::MozIsHTML => self.as_node().owner_doc().is_html_document(),\n            NonTSPseudoClass::MozLocaleDir(..) | NonTSPseudoClass::MozWindowInactive => {\n                let state_bit = pseudo_class.document_state_flag();\n                if state_bit.is_empty() {\n                    debug_assert!(\n                        matches!(pseudo_class, NonTSPseudoClass::MozLocaleDir(..)),\n                        \"Only moz-locale-dir should ever return an empty state\"\n                    );\n                    return false;\n                }\n                if context\n                    .extra_data\n                    .invalidation_data\n                    .document_state\n                    .intersects(state_bit)\n                {\n                    return !context.in_negation();\n                }\n                self.document_state().contains(state_bit)\n            },\n            NonTSPseudoClass::MozPlaceholder => false,\n            NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),\n            NonTSPseudoClass::Heading(ref levels) => levels.matches_state(self.state()),\n        }\n    }\n\n    fn match_pseudo_element(\n        &self,\n        pseudo_selector: &PseudoElement,\n        _context: &mut MatchingContext<Self::Impl>,\n    ) -> bool {\n        // TODO(emilio): I believe we could assert we are a pseudo-element and\n        // match the proper pseudo-element, given how we rulehash the stuff\n        // based on the pseudo.\n        let pseudo = match self.implemented_pseudo_element() {\n            Some(pseudo) => pseudo,\n            None => return false,\n        };\n\n        pseudo.matches(pseudo_selector, self)\n    }\n\n    #[inline]\n    fn is_link(&self) -> bool {\n        self.state().intersects(ElementState::VISITED_OR_UNVISITED)\n    }\n\n    #[inline]\n    fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        if !self.has_id() {\n            return false;\n        }\n\n        let element_id = match snapshot_helpers::get_id(self.attrs()) {\n            Some(id) => id,\n            None => return false,\n        };\n\n        case_sensitivity.eq_atom(element_id, id)\n    }\n\n    #[inline]\n    fn is_part(&self, name: &AtomIdent) -> bool {\n        let attr = match self.get_part_attr() {\n            Some(c) => c,\n            None => return false,\n        };\n\n        snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)\n    }\n\n    #[inline]\n    fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {\n        snapshot_helpers::imported_part(self.attrs(), name)\n    }\n\n    #[inline(always)]\n    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        let attr = match self.get_class_attr() {\n            Some(c) => c,\n            None => return false,\n        };\n\n        snapshot_helpers::has_class_or_part(name, case_sensitivity, attr)\n    }\n\n    #[inline]\n    fn has_custom_state(&self, state: &AtomIdent) -> bool {\n        if !self.is_html_element() {\n            return false;\n        }\n        let check_state_ptr: *const nsAtom = state.as_ptr();\n        self.extended_slots().map_or(false, |slot| {\n            (&slot.mCustomStates).iter().any(|setstate| {\n                let setstate_ptr: *const nsAtom = setstate.mRawPtr;\n                setstate_ptr == check_state_ptr\n            })\n        })\n    }\n\n    #[inline]\n    fn is_html_element_in_html_document(&self) -> bool {\n        self.is_html_element() && self.as_node().owner_doc().is_html_document()\n    }\n\n    #[inline]\n    fn is_html_slot_element(&self) -> bool {\n        self.is_html_element() && self.local_name().as_ptr() == local_name!(\"slot\").as_ptr()\n    }\n\n    #[inline]\n    fn ignores_nth_child_selectors(&self) -> bool {\n        self.is_root_of_native_anonymous_subtree()\n    }\n\n    fn add_element_unique_hashes(&self, filter: &mut BloomFilter) -> bool {\n        each_relevant_element_hash(*self, |hash| filter.insert_hash(hash & BLOOM_HASH_MASK));\n        true\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Gecko's C++ bindings, along with some rust helpers to ease its use.\n\n// FIXME: We allow `improper_ctypes` (for now), because the lint doesn't allow\n// foreign structs to have `PhantomData`. We should remove this once the lint\n// ignores this case.\n\n#[allow(unknown_lints)]\n#[allow(\n    dead_code,\n    improper_ctypes,\n    non_camel_case_types,\n    non_snake_case,\n    non_upper_case_globals,\n    missing_docs,\n    unnecessary_transmutes\n)]\n// TODO: Remove this when updating bindgen, see\n// https://github.com/rust-lang/rust-bindgen/issues/1651\n#[cfg_attr(test, allow(deref_nullptr))]\npub mod structs {\n    include!(concat!(env!(\"OUT_DIR\"), \"/gecko/structs.rs\"));\n}\n\npub use self::structs as bindings;\n\npub mod sugar;\n"
  },
  {
    "path": "style/gecko_bindings/sugar/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Rust sugar and convenience methods for Gecko types.\n\nmod ns_com_ptr;\nmod ns_compatibility;\nmod ns_style_auto_array;\npub mod origin_flags;\npub mod ownership;\npub mod refptr;\n"
  },
  {
    "path": "style/gecko_bindings/sugar/ns_com_ptr.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Little helpers for `nsCOMPtr`.\n\nuse crate::gecko_bindings::structs::nsCOMPtr;\n\nimpl<T> nsCOMPtr<T> {\n    /// Get this pointer as a raw pointer.\n    #[inline]\n    pub fn raw(&self) -> *mut T {\n        self.mRawPtr\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/sugar/ns_compatibility.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Little helper for `nsCompatibility`.\n\nuse crate::context::QuirksMode;\nuse crate::gecko_bindings::structs::nsCompatibility;\n\nimpl From<nsCompatibility> for QuirksMode {\n    #[inline]\n    fn from(mode: nsCompatibility) -> QuirksMode {\n        match mode {\n            nsCompatibility::eCompatibility_FullStandards => QuirksMode::NoQuirks,\n            nsCompatibility::eCompatibility_AlmostStandards => QuirksMode::LimitedQuirks,\n            nsCompatibility::eCompatibility_NavQuirks => QuirksMode::Quirks,\n        }\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/sugar/ns_style_auto_array.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Rust helpers for Gecko's `nsStyleAutoArray`.\n\nuse crate::gecko_bindings::bindings::Gecko_EnsureStyleAnimationArrayLength;\nuse crate::gecko_bindings::bindings::Gecko_EnsureStyleScrollTimelineArrayLength;\nuse crate::gecko_bindings::bindings::Gecko_EnsureStyleTransitionArrayLength;\nuse crate::gecko_bindings::bindings::Gecko_EnsureStyleViewTimelineArrayLength;\nuse crate::gecko_bindings::structs::nsStyleAutoArray;\nuse crate::gecko_bindings::structs::{StyleAnimation, StyleTransition};\nuse crate::gecko_bindings::structs::{StyleScrollTimeline, StyleViewTimeline};\nuse std::iter::{once, Chain, Once};\nuse std::ops::{Index, IndexMut};\nuse std::slice::{Iter, IterMut};\n\nimpl<T> Index<usize> for nsStyleAutoArray<T> {\n    type Output = T;\n    fn index(&self, index: usize) -> &T {\n        match index {\n            0 => &self.mFirstElement,\n            _ => &self.mOtherElements[index - 1],\n        }\n    }\n}\n\nimpl<T> IndexMut<usize> for nsStyleAutoArray<T> {\n    fn index_mut(&mut self, index: usize) -> &mut T {\n        match index {\n            0 => &mut self.mFirstElement,\n            _ => &mut self.mOtherElements[index - 1],\n        }\n    }\n}\n\nimpl<T> nsStyleAutoArray<T> {\n    /// Mutably iterate over the array elements.\n    pub fn iter_mut(&mut self) -> Chain<Once<&mut T>, IterMut<'_, T>> {\n        once(&mut self.mFirstElement).chain(self.mOtherElements.iter_mut())\n    }\n\n    /// Iterate over the array elements.\n    pub fn iter(&self) -> Chain<Once<&T>, Iter<'_, T>> {\n        once(&self.mFirstElement).chain(self.mOtherElements.iter())\n    }\n\n    /// Returns the length of the array.\n    ///\n    /// Note that often structs containing autoarrays will have additional\n    /// member fields that contain the length, which must be kept in sync.\n    pub fn len(&self) -> usize {\n        1 + self.mOtherElements.len()\n    }\n}\n\nimpl nsStyleAutoArray<StyleAnimation> {\n    /// Ensures that the array has length at least the given length.\n    pub fn ensure_len(&mut self, len: usize) {\n        unsafe {\n            Gecko_EnsureStyleAnimationArrayLength(\n                self as *mut nsStyleAutoArray<StyleAnimation> as *mut _,\n                len,\n            );\n        }\n    }\n}\n\nimpl nsStyleAutoArray<StyleTransition> {\n    /// Ensures that the array has length at least the given length.\n    pub fn ensure_len(&mut self, len: usize) {\n        unsafe {\n            Gecko_EnsureStyleTransitionArrayLength(\n                self as *mut nsStyleAutoArray<StyleTransition> as *mut _,\n                len,\n            );\n        }\n    }\n}\n\nimpl nsStyleAutoArray<StyleViewTimeline> {\n    /// Ensures that the array has length at least the given length.\n    pub fn ensure_len(&mut self, len: usize) {\n        unsafe {\n            Gecko_EnsureStyleViewTimelineArrayLength(\n                self as *mut nsStyleAutoArray<StyleViewTimeline> as *mut _,\n                len,\n            );\n        }\n    }\n}\n\nimpl nsStyleAutoArray<StyleScrollTimeline> {\n    /// Ensures that the array has length at least the given length.\n    pub fn ensure_len(&mut self, len: usize) {\n        unsafe {\n            Gecko_EnsureStyleScrollTimelineArrayLength(\n                self as *mut nsStyleAutoArray<StyleScrollTimeline> as *mut _,\n                len,\n            );\n        }\n    }\n}\n\nimpl<'a, T> IntoIterator for &'a mut nsStyleAutoArray<T> {\n    type Item = &'a mut T;\n    type IntoIter = Chain<Once<&'a mut T>, IterMut<'a, T>>;\n    fn into_iter(self) -> Self::IntoIter {\n        self.iter_mut()\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/sugar/origin_flags.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Helper to iterate over `OriginFlags` bits.\n\nuse crate::gecko_bindings::structs::OriginFlags;\nuse crate::stylesheets::OriginSet;\n\n/// Checks that the values for OriginFlags are the ones we expect.\npub fn assert_flags_match() {\n    use crate::stylesheets::origin::*;\n    debug_assert_eq!(\n        OriginFlags::UserAgent.0,\n        OriginSet::ORIGIN_USER_AGENT.bits()\n    );\n    debug_assert_eq!(OriginFlags::Author.0, OriginSet::ORIGIN_AUTHOR.bits());\n    debug_assert_eq!(OriginFlags::User.0, OriginSet::ORIGIN_USER.bits());\n}\n\nimpl From<OriginFlags> for OriginSet {\n    fn from(flags: OriginFlags) -> Self {\n        Self::from_bits_retain(flags.0)\n    }\n}\n\nimpl From<OriginSet> for OriginFlags {\n    fn from(set: OriginSet) -> Self {\n        OriginFlags(set.bits())\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/sugar/ownership.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Helpers for different FFI pointer kinds that Gecko's FFI layer uses.\n\nuse crate::gecko_bindings::structs::root::mozilla::detail::CopyablePtr;\nuse servo_arc::Arc;\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\nuse std::ptr;\n\n/// Gecko-FFI-safe Arc (T is an ArcInner).\n///\n/// This can be null.\n///\n/// Leaks on drop. Please don't drop this.\n#[repr(C)]\npub struct Strong<GeckoType> {\n    ptr: *const GeckoType,\n    _marker: PhantomData<GeckoType>,\n}\n\nimpl<T> From<Arc<T>> for Strong<T> {\n    fn from(arc: Arc<T>) -> Self {\n        Self {\n            ptr: Arc::into_raw(arc),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<T> From<Option<Arc<T>>> for Strong<T> {\n    fn from(arc: Option<Arc<T>>) -> Self {\n        match arc {\n            Some(arc) => arc.into(),\n            None => Self::null(),\n        }\n    }\n}\n\nimpl<GeckoType> Strong<GeckoType> {\n    #[inline]\n    /// Returns whether this reference is null.\n    pub fn is_null(&self) -> bool {\n        self.ptr.is_null()\n    }\n\n    #[inline]\n    /// Returns a null pointer\n    pub fn null() -> Self {\n        Self {\n            ptr: ptr::null(),\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<T> Deref for CopyablePtr<T> {\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        &self.mPtr\n    }\n}\n\nimpl<T> DerefMut for CopyablePtr<T> {\n    fn deref_mut<'a>(&'a mut self) -> &'a mut T {\n        &mut self.mPtr\n    }\n}\n"
  },
  {
    "path": "style/gecko_bindings/sugar/refptr.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A rust helper to ease the use of Gecko's refcounted types.\n\nuse crate::gecko_bindings::{bindings, structs};\nuse crate::Atom;\nuse servo_arc::Arc;\nuse std::fmt::Write;\nuse std::marker::PhantomData;\nuse std::ops::Deref;\nuse std::{fmt, mem, ptr};\n\n/// Trait for all objects that have Addref() and Release\n/// methods and can be placed inside RefPtr<T>\npub unsafe trait RefCounted {\n    /// Bump the reference count.\n    fn addref(&self);\n    /// Decrease the reference count.\n    unsafe fn release(&self);\n}\n\n/// Trait for types which can be shared across threads in RefPtr.\npub unsafe trait ThreadSafeRefCounted: RefCounted {}\n\n/// A custom RefPtr implementation to take into account Drop semantics and\n/// a bit less-painful memory management.\npub struct RefPtr<T: RefCounted> {\n    ptr: *mut T,\n    _marker: PhantomData<T>,\n}\n\nimpl<T: RefCounted> fmt::Debug for RefPtr<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"RefPtr { \")?;\n        self.ptr.fmt(f)?;\n        f.write_char('}')\n    }\n}\n\nimpl<T: RefCounted> RefPtr<T> {\n    /// Create a new RefPtr from an already addrefed pointer obtained from FFI.\n    ///\n    /// The pointer must be valid, non-null and have been addrefed.\n    pub unsafe fn from_addrefed(ptr: *mut T) -> Self {\n        debug_assert!(!ptr.is_null());\n        RefPtr {\n            ptr,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Returns whether the current pointer is null.\n    pub fn is_null(&self) -> bool {\n        self.ptr.is_null()\n    }\n\n    /// Returns a null pointer.\n    pub fn null() -> Self {\n        Self {\n            ptr: ptr::null_mut(),\n            _marker: PhantomData,\n        }\n    }\n\n    /// Create a new RefPtr from a pointer obtained from FFI.\n    ///\n    /// This method calls addref() internally\n    pub unsafe fn new(ptr: *mut T) -> Self {\n        let ret = RefPtr {\n            ptr,\n            _marker: PhantomData,\n        };\n        ret.addref();\n        ret\n    }\n\n    /// Produces an FFI-compatible RefPtr that can be stored in style structs.\n    ///\n    /// structs::RefPtr does not have a destructor, so this may leak\n    pub fn forget(self) -> structs::RefPtr<T> {\n        let ret = structs::RefPtr {\n            mRawPtr: self.ptr,\n            _phantom_0: PhantomData,\n        };\n        mem::forget(self);\n        ret\n    }\n\n    /// Returns the raw inner pointer to be fed back into FFI.\n    pub fn get(&self) -> *mut T {\n        self.ptr\n    }\n\n    /// Addref the inner data, obviously leaky on its own.\n    pub fn addref(&self) {\n        if !self.ptr.is_null() {\n            unsafe {\n                (*self.ptr).addref();\n            }\n        }\n    }\n\n    /// Release the inner data.\n    ///\n    /// Call only when the data actually needs releasing.\n    pub unsafe fn release(&self) {\n        if !self.ptr.is_null() {\n            (*self.ptr).release();\n        }\n    }\n}\n\nimpl<T: RefCounted> Deref for RefPtr<T> {\n    type Target = T;\n    fn deref(&self) -> &T {\n        debug_assert!(!self.ptr.is_null());\n        unsafe { &*self.ptr }\n    }\n}\n\nimpl<T: RefCounted> structs::RefPtr<T> {\n    /// Produces a Rust-side RefPtr from an FFI RefPtr, bumping the refcount.\n    ///\n    /// Must be called on a valid, non-null structs::RefPtr<T>.\n    pub unsafe fn to_safe(&self) -> RefPtr<T> {\n        let r = RefPtr {\n            ptr: self.mRawPtr,\n            _marker: PhantomData,\n        };\n        r.addref();\n        r\n    }\n    /// Produces a Rust-side RefPtr, consuming the existing one (and not bumping\n    /// the refcount).\n    pub unsafe fn into_safe(self) -> RefPtr<T> {\n        debug_assert!(!self.mRawPtr.is_null());\n        RefPtr {\n            ptr: self.mRawPtr,\n            _marker: PhantomData,\n        }\n    }\n\n    /// Replace a structs::RefPtr<T> with a different one, appropriately\n    /// addref/releasing.\n    ///\n    /// Both `self` and `other` must be valid, but can be null.\n    ///\n    /// Safe when called on an aliased pointer because the refcount in that case\n    /// needs to be at least two.\n    pub unsafe fn set(&mut self, other: &Self) {\n        self.clear();\n        if !other.mRawPtr.is_null() {\n            *self = other.to_safe().forget();\n        }\n    }\n\n    /// Clear an instance of the structs::RefPtr<T>, by releasing\n    /// it and setting its contents to null.\n    ///\n    /// `self` must be valid, but can be null.\n    pub unsafe fn clear(&mut self) {\n        if !self.mRawPtr.is_null() {\n            (*self.mRawPtr).release();\n            self.mRawPtr = ptr::null_mut();\n        }\n    }\n\n    /// Replace a `structs::RefPtr<T>` with a `RefPtr<T>`,\n    /// consuming the `RefPtr<T>`, and releasing the old\n    /// value in `self` if necessary.\n    ///\n    /// `self` must be valid, possibly null.\n    pub fn set_move(&mut self, other: RefPtr<T>) {\n        if !self.mRawPtr.is_null() {\n            unsafe {\n                (*self.mRawPtr).release();\n            }\n        }\n        *self = other.forget();\n    }\n}\n\nimpl<T> structs::RefPtr<T> {\n    /// Returns a new, null refptr.\n    pub fn null() -> Self {\n        Self {\n            mRawPtr: ptr::null_mut(),\n            _phantom_0: PhantomData,\n        }\n    }\n\n    /// Create a new RefPtr from an arc.\n    pub fn from_arc(s: Arc<T>) -> Self {\n        Self {\n            mRawPtr: Arc::into_raw(s) as *mut _,\n            _phantom_0: PhantomData,\n        }\n    }\n\n    /// Sets the contents to an Arc<T>.\n    pub fn set_arc(&mut self, other: Arc<T>) {\n        unsafe {\n            if !self.mRawPtr.is_null() {\n                let _ = Arc::from_raw(self.mRawPtr);\n            }\n            self.mRawPtr = Arc::into_raw(other) as *mut _;\n        }\n    }\n}\n\nimpl<T: RefCounted> Drop for RefPtr<T> {\n    fn drop(&mut self) {\n        unsafe { self.release() }\n    }\n}\n\nimpl<T: RefCounted> Clone for RefPtr<T> {\n    fn clone(&self) -> Self {\n        self.addref();\n        RefPtr {\n            ptr: self.ptr,\n            _marker: PhantomData,\n        }\n    }\n}\n\nimpl<T: RefCounted> PartialEq for RefPtr<T> {\n    fn eq(&self, other: &Self) -> bool {\n        self.ptr == other.ptr\n    }\n}\n\nunsafe impl<T: ThreadSafeRefCounted> Send for RefPtr<T> {}\nunsafe impl<T: ThreadSafeRefCounted> Sync for RefPtr<T> {}\n\nmacro_rules! impl_refcount {\n    ($t:ty, $addref:path, $release:path) => {\n        unsafe impl RefCounted for $t {\n            #[inline]\n            fn addref(&self) {\n                unsafe { $addref(self as *const _ as *mut _) }\n            }\n\n            #[inline]\n            unsafe fn release(&self) {\n                $release(self as *const _ as *mut _)\n            }\n        }\n    };\n}\n\n// Companion of NS_DECL_THREADSAFE_FFI_REFCOUNTING.\n//\n// Gets you a free RefCounted impl implemented via FFI.\nmacro_rules! impl_threadsafe_refcount {\n    ($t:ty, $addref:path, $release:path) => {\n        impl_refcount!($t, $addref, $release);\n        unsafe impl ThreadSafeRefCounted for $t {}\n    };\n}\n\nimpl_threadsafe_refcount!(\n    structs::mozilla::URLExtraData,\n    bindings::Gecko_AddRefURLExtraDataArbitraryThread,\n    bindings::Gecko_ReleaseURLExtraDataArbitraryThread\n);\nimpl_threadsafe_refcount!(\n    structs::nsIURI,\n    bindings::Gecko_AddRefnsIURIArbitraryThread,\n    bindings::Gecko_ReleasensIURIArbitraryThread\n);\nimpl_threadsafe_refcount!(\n    structs::SheetLoadDataHolder,\n    bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread,\n    bindings::Gecko_ReleaseSheetLoadDataHolderArbitraryThread\n);\n\n#[inline]\nunsafe fn addref_atom(atom: *mut structs::nsAtom) {\n    mem::forget(Atom::from_raw(atom));\n}\n\n#[inline]\nunsafe fn release_atom(atom: *mut structs::nsAtom) {\n    let _ = Atom::from_addrefed(atom);\n}\nimpl_threadsafe_refcount!(structs::nsAtom, addref_atom, release_atom);\n"
  },
  {
    "path": "style/gecko_string_cache/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n// This is needed for the constants in atom_macro.rs, because we have some\n// atoms whose names differ only by case, e.g. datetime and dateTime.\n#![allow(non_upper_case_globals)]\n\n//! A drop-in replacement for string_cache, but backed by Gecko `nsAtom`s.\n\nuse crate::gecko_bindings::bindings::Gecko_AddRefAtom;\nuse crate::gecko_bindings::bindings::Gecko_Atomize;\nuse crate::gecko_bindings::bindings::Gecko_Atomize16;\nuse crate::gecko_bindings::bindings::Gecko_ReleaseAtom;\nuse crate::gecko_bindings::structs::root::mozilla::detail::gGkAtoms;\nuse crate::gecko_bindings::structs::root::mozilla::detail::GkAtoms_Atoms_AtomsCount;\nuse crate::gecko_bindings::structs::{nsAtom, nsDynamicAtom, nsStaticAtom};\nuse nsstring::{nsAString, nsStr};\nuse precomputed_hash::PrecomputedHash;\nuse serde::{Deserialize, Serialize};\nuse std::borrow::{Borrow, Cow};\nuse std::char::{self, DecodeUtf16};\nuse std::fmt::{self, Write};\nuse std::hash::{Hash, Hasher};\nuse std::iter::Cloned;\nuse std::mem::{self, ManuallyDrop};\nuse std::num::NonZeroUsize;\nuse std::ops::Deref;\nuse std::{slice, str};\nuse style_traits::SpecifiedValueInfo;\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\n#[macro_use]\n#[allow(improper_ctypes, non_camel_case_types, missing_docs)]\npub mod atom_macro {\n    include!(concat!(env!(\"OUT_DIR\"), \"/gecko/atom_macro.rs\"));\n}\n\n#[macro_use]\npub mod namespace;\n\npub use self::namespace::{Namespace, WeakNamespace};\n\n/// A handle to a Gecko atom. This is a type that can represent either:\n///\n///  * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case\n///    the `usize` just holds the pointer value.\n///\n///  * An index from `gGkAtoms` to the `nsStaticAtom` object (shifted to the left one bit, and with\n///    the lower bit set to `1` to differentiate it from the above), so `(index << 1 | 1)`.\n///\n#[derive(Eq, PartialEq)]\n#[repr(C)]\npub struct Atom(NonZeroUsize);\n\nimpl Serialize for Atom {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        // TODO(dshin, Bug 1929015): Optimization for static atoms is possible.\n        self.deref().with_str(|s| serializer.serialize_str(s))\n    }\n}\n\nstruct AtomStrVisitor;\nimpl<'de> serde::de::Visitor<'de> for AtomStrVisitor {\n    type Value = Atom;\n\n    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        write!(formatter, \"A string to atomize\")\n    }\n\n    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>\n    where\n        E: serde::de::Error,\n    {\n        Ok(Atom::from(s))\n    }\n}\n\nimpl<'de> Deserialize<'de> for Atom {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        deserializer.deserialize_str(AtomStrVisitor)\n    }\n}\n\n/// An atom *without* a strong reference.\n///\n/// Only usable as `&'a WeakAtom`,\n/// where `'a` is the lifetime of something that holds a strong reference to that atom.\npub struct WeakAtom(nsAtom);\n\n/// The number of static atoms we have.\nconst STATIC_ATOM_COUNT: usize = GkAtoms_Atoms_AtomsCount as usize;\n\nimpl Deref for Atom {\n    type Target = WeakAtom;\n\n    #[inline]\n    fn deref(&self) -> &WeakAtom {\n        unsafe {\n            let addr = if self.is_static() {\n                // This is really hot.\n                &gGkAtoms.mAtoms.get_unchecked(self.0.get() >> 1)._base as *const nsAtom\n            } else {\n                self.0.get() as *const nsAtom\n            };\n            WeakAtom::new(addr as *const nsAtom)\n        }\n    }\n}\n\nimpl PrecomputedHash for Atom {\n    #[inline]\n    fn precomputed_hash(&self) -> u32 {\n        self.get_hash()\n    }\n}\n\nimpl Borrow<WeakAtom> for Atom {\n    #[inline]\n    fn borrow(&self) -> &WeakAtom {\n        self\n    }\n}\n\nimpl ToShmem for Atom {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        if !self.is_static() {\n            return Err(format!(\n                \"ToShmem failed for Atom: must be a static atom: {}\",\n                self\n            ));\n        }\n\n        Ok(ManuallyDrop::new(Atom(self.0)))\n    }\n}\n\nimpl Eq for WeakAtom {}\nimpl PartialEq for WeakAtom {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        let weak: *const WeakAtom = self;\n        let other: *const WeakAtom = other;\n        weak == other\n    }\n}\n\nimpl PartialEq<Atom> for WeakAtom {\n    #[inline]\n    fn eq(&self, other: &Atom) -> bool {\n        self == &**other\n    }\n}\n\nunsafe impl Send for Atom {}\nunsafe impl Sync for Atom {}\nunsafe impl Sync for WeakAtom {}\n\nimpl WeakAtom {\n    /// Construct a `WeakAtom` from a raw `nsAtom`.\n    #[inline]\n    pub unsafe fn new<'a>(atom: *const nsAtom) -> &'a mut Self {\n        &mut *(atom as *mut WeakAtom)\n    }\n\n    /// Clone this atom, bumping the refcount if the atom is not static.\n    #[inline]\n    pub fn clone(&self) -> Atom {\n        unsafe { Atom::from_raw(self.as_ptr()) }\n    }\n\n    /// Get the atom hash.\n    #[inline]\n    pub fn get_hash(&self) -> u32 {\n        self.0.mHash\n    }\n\n    /// Get the atom as a slice of utf-16 chars.\n    #[inline]\n    pub fn as_slice(&self) -> &[u16] {\n        let string = if self.is_static() {\n            let atom_ptr = self.as_ptr() as *const nsStaticAtom;\n            let string_offset = unsafe { (*atom_ptr).mStringOffset };\n            let string_offset = -(string_offset as isize);\n            let u8_ptr = atom_ptr as *const u8;\n            // It is safe to use offset() here because both addresses are within\n            // the same struct, e.g. mozilla::detail::gGkAtoms.\n            unsafe { u8_ptr.offset(string_offset) as *const u16 }\n        } else {\n            let atom_ptr = self.as_ptr() as *const nsDynamicAtom;\n            let buffer_ptr = unsafe { (*atom_ptr).mStringBuffer.mRawPtr };\n            // Dynamic atom chars are stored at the end of the string buffer.\n            unsafe { buffer_ptr.offset(1) as *const u16 }\n        };\n        unsafe { slice::from_raw_parts(string, self.len() as usize) }\n    }\n\n    // NOTE: don't expose this, since it's slow, and easy to be misused.\n    fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<'_, u16>>> {\n        char::decode_utf16(self.as_slice().iter().cloned())\n    }\n\n    /// Execute `cb` with the string that this atom represents.\n    ///\n    /// Find alternatives to this function when possible, please, since it's\n    /// pretty slow.\n    pub fn with_str<F, Output>(&self, cb: F) -> Output\n    where\n        F: FnOnce(&str) -> Output,\n    {\n        let mut buffer = mem::MaybeUninit::<[u8; 64]>::uninit();\n        let buffer = unsafe { &mut *buffer.as_mut_ptr() };\n\n        // The total string length in utf16 is going to be less than or equal\n        // the slice length (each utf16 character is going to take at least one\n        // and at most 2 items in the utf16 slice).\n        //\n        // Each of those characters will take at most four bytes in the utf8\n        // one. Thus if the slice is less than 64 / 4 (16) we can guarantee that\n        // we'll decode it in place.\n        let owned_string;\n        let len = self.len();\n        let utf8_slice = if len <= 16 {\n            let mut total_len = 0;\n\n            for c in self.chars() {\n                let c = c.unwrap_or(char::REPLACEMENT_CHARACTER);\n                let utf8_len = c.encode_utf8(&mut buffer[total_len..]).len();\n                total_len += utf8_len;\n            }\n\n            let slice = unsafe { str::from_utf8_unchecked(&buffer[..total_len]) };\n            debug_assert_eq!(slice, String::from_utf16_lossy(self.as_slice()));\n            slice\n        } else {\n            owned_string = String::from_utf16_lossy(self.as_slice());\n            &*owned_string\n        };\n\n        cb(utf8_slice)\n    }\n\n    /// Returns whether this atom is static.\n    #[inline]\n    pub fn is_static(&self) -> bool {\n        self.0.mIsStatic() != 0\n    }\n\n    /// Returns whether this atom is ascii lowercase.\n    #[inline]\n    fn is_ascii_lowercase(&self) -> bool {\n        self.0.mIsAsciiLowercase() != 0\n    }\n\n    /// Returns the length of the atom string.\n    #[inline]\n    pub fn len(&self) -> u32 {\n        self.0.mLength()\n    }\n\n    /// Returns whether this atom is the empty string.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// Returns the atom as a mutable pointer.\n    #[inline]\n    pub fn as_ptr(&self) -> *mut nsAtom {\n        let const_ptr: *const nsAtom = &self.0;\n        const_ptr as *mut nsAtom\n    }\n\n    /// Convert this atom to ASCII lower-case\n    pub fn to_ascii_lowercase(&self) -> Atom {\n        if self.is_ascii_lowercase() {\n            return self.clone();\n        }\n\n        let slice = self.as_slice();\n        let mut buffer = mem::MaybeUninit::<[u16; 64]>::uninit();\n        let buffer = unsafe { &mut *buffer.as_mut_ptr() };\n        let mut vec;\n        let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {\n            buffer_prefix.copy_from_slice(slice);\n            buffer_prefix\n        } else {\n            vec = slice.to_vec();\n            &mut vec\n        };\n        for char16 in &mut *mutable_slice {\n            if *char16 <= 0x7F {\n                *char16 = (*char16 as u8).to_ascii_lowercase() as u16\n            }\n        }\n        Atom::from(&*mutable_slice)\n    }\n\n    /// Return whether two atoms are ASCII-case-insensitive matches\n    #[inline]\n    pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {\n        if self == other {\n            return true;\n        }\n\n        // If we know both atoms are ascii-lowercase, then we can stick with\n        // pointer equality.\n        if self.is_ascii_lowercase() && other.is_ascii_lowercase() {\n            debug_assert!(!self.eq_ignore_ascii_case_slow(other));\n            return false;\n        }\n\n        self.eq_ignore_ascii_case_slow(other)\n    }\n\n    fn eq_ignore_ascii_case_slow(&self, other: &Self) -> bool {\n        let a = self.as_slice();\n        let b = other.as_slice();\n\n        if a.len() != b.len() {\n            return false;\n        }\n\n        a.iter().zip(b).all(|(&a16, &b16)| {\n            if a16 <= 0x7F && b16 <= 0x7F {\n                (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))\n            } else {\n                a16 == b16\n            }\n        })\n    }\n}\n\nimpl fmt::Debug for WeakAtom {\n    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {\n        write!(w, \"Gecko WeakAtom({:p}, {})\", self, self)\n    }\n}\n\nimpl fmt::Display for WeakAtom {\n    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {\n        for c in self.chars() {\n            w.write_char(c.unwrap_or(char::REPLACEMENT_CHARACTER))?\n        }\n        Ok(())\n    }\n}\n\n#[inline]\nunsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize {\n    debug_assert!(!ptr.is_null());\n    if !WeakAtom::new(ptr).is_static() {\n        NonZeroUsize::new_unchecked(ptr as usize)\n    } else {\n        make_static_handle(ptr as *mut nsStaticAtom)\n    }\n}\n\n#[inline]\nunsafe fn make_static_handle(ptr: *const nsStaticAtom) -> NonZeroUsize {\n    let index = ptr.offset_from(&gGkAtoms.mAtoms[0] as *const _);\n    debug_assert!(index >= 0, \"Should be a non-negative index\");\n    debug_assert!(\n        (index as usize) < STATIC_ATOM_COUNT,\n        \"Should be a valid static atom index\"\n    );\n    NonZeroUsize::new_unchecked(((index as usize) << 1) | 1)\n}\n\nimpl Atom {\n    #[inline]\n    fn is_static(&self) -> bool {\n        self.0.get() & 1 == 1\n    }\n\n    /// Execute a callback with the atom represented by `ptr`.\n    pub unsafe fn with<F, R>(ptr: *const nsAtom, callback: F) -> R\n    where\n        F: FnOnce(&Atom) -> R,\n    {\n        let atom = Atom(make_handle(ptr as *mut nsAtom));\n        let ret = callback(&atom);\n        mem::forget(atom);\n        ret\n    }\n\n    /// Creates a static atom from its index in the static atom table, without\n    /// checking.\n    #[inline]\n    pub const unsafe fn from_index_unchecked(index: u16) -> Self {\n        debug_assert!((index as usize) < STATIC_ATOM_COUNT);\n        Atom(NonZeroUsize::new_unchecked(((index as usize) << 1) | 1))\n    }\n\n    /// Creates an atom from an atom pointer.\n    #[inline(always)]\n    pub unsafe fn from_raw(ptr: *mut nsAtom) -> Self {\n        let atom = Atom(make_handle(ptr));\n        if !atom.is_static() {\n            Gecko_AddRefAtom(ptr);\n        }\n        atom\n    }\n\n    /// Creates an atom from an atom pointer that has already had AddRef\n    /// called on it. This may be a static or dynamic atom.\n    #[inline]\n    pub unsafe fn from_addrefed(ptr: *mut nsAtom) -> Self {\n        assert!(!ptr.is_null());\n        Atom(make_handle(ptr))\n    }\n\n    /// Convert this atom into an addrefed nsAtom pointer.\n    #[inline]\n    pub fn into_addrefed(self) -> *mut nsAtom {\n        let ptr = self.as_ptr();\n        mem::forget(self);\n        ptr\n    }\n}\n\nimpl Hash for Atom {\n    fn hash<H>(&self, state: &mut H)\n    where\n        H: Hasher,\n    {\n        state.write_u32(self.get_hash());\n    }\n}\n\nimpl Hash for WeakAtom {\n    fn hash<H>(&self, state: &mut H)\n    where\n        H: Hasher,\n    {\n        state.write_u32(self.get_hash());\n    }\n}\n\nimpl Clone for Atom {\n    #[inline(always)]\n    fn clone(&self) -> Atom {\n        unsafe {\n            let atom = Atom(self.0);\n            if !atom.is_static() {\n                Gecko_AddRefAtom(atom.as_ptr());\n            }\n            atom\n        }\n    }\n}\n\nimpl Drop for Atom {\n    #[inline]\n    fn drop(&mut self) {\n        if !self.is_static() {\n            unsafe {\n                Gecko_ReleaseAtom(self.as_ptr());\n            }\n        }\n    }\n}\n\nimpl Default for Atom {\n    #[inline]\n    fn default() -> Self {\n        atom!(\"\")\n    }\n}\n\nimpl fmt::Debug for Atom {\n    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {\n        write!(w, \"Atom(0x{:08x}, {})\", self.0, self)\n    }\n}\n\nimpl fmt::Display for Atom {\n    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {\n        self.deref().fmt(w)\n    }\n}\n\nimpl<'a> From<&'a str> for Atom {\n    #[inline]\n    fn from(string: &str) -> Atom {\n        debug_assert!(string.len() <= u32::max_value() as usize);\n        unsafe {\n            Atom::from_addrefed(Gecko_Atomize(\n                string.as_ptr() as *const _,\n                string.len() as u32,\n            ))\n        }\n    }\n}\n\nimpl<'a> From<&'a [u16]> for Atom {\n    #[inline]\n    fn from(slice: &[u16]) -> Atom {\n        Atom::from(&*nsStr::from(slice))\n    }\n}\n\nimpl<'a> From<&'a nsAString> for Atom {\n    #[inline]\n    fn from(string: &nsAString) -> Atom {\n        unsafe { Atom::from_addrefed(Gecko_Atomize16(string)) }\n    }\n}\n\nimpl<'a> From<Cow<'a, str>> for Atom {\n    #[inline]\n    fn from(string: Cow<'a, str>) -> Atom {\n        Atom::from(&*string)\n    }\n}\n\nimpl From<String> for Atom {\n    #[inline]\n    fn from(string: String) -> Atom {\n        Atom::from(&*string)\n    }\n}\n\nmalloc_size_of::malloc_size_of_is_0!(Atom);\n\nimpl SpecifiedValueInfo for Atom {}\n"
  },
  {
    "path": "style/gecko_string_cache/namespace.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A type to represent a namespace.\n\nuse crate::derives::*;\nuse crate::gecko_bindings::structs::nsAtom;\nuse crate::string_cache::{Atom, WeakAtom};\nuse precomputed_hash::PrecomputedHash;\nuse std::borrow::Borrow;\nuse std::fmt;\nuse std::ops::Deref;\n\n/// In Gecko namespaces are just regular atoms, so this is a simple macro to\n/// forward one macro to the other.\n#[macro_export]\nmacro_rules! ns {\n    () => {\n        $crate::string_cache::Namespace(atom!(\"\"))\n    };\n    ($s:tt) => {\n        $crate::string_cache::Namespace(atom!($s))\n    };\n}\n\n/// A Gecko namespace is just a wrapped atom.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct Namespace(pub Atom);\n\nimpl PrecomputedHash for Namespace {\n    #[inline]\n    fn precomputed_hash(&self) -> u32 {\n        self.0.precomputed_hash()\n    }\n}\n\n/// A Gecko WeakNamespace is a wrapped WeakAtom.\n#[derive(Deref, Hash)]\npub struct WeakNamespace(WeakAtom);\n\nimpl Deref for Namespace {\n    type Target = WeakNamespace;\n\n    #[inline]\n    fn deref(&self) -> &WeakNamespace {\n        let weak: *const WeakAtom = &*self.0;\n        unsafe { &*(weak as *const WeakNamespace) }\n    }\n}\n\nimpl<'a> From<&'a str> for Namespace {\n    fn from(s: &'a str) -> Self {\n        Namespace(Atom::from(s))\n    }\n}\n\nimpl fmt::Display for Namespace {\n    fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {\n        self.0.fmt(w)\n    }\n}\n\nimpl Borrow<WeakNamespace> for Namespace {\n    #[inline]\n    fn borrow(&self) -> &WeakNamespace {\n        self\n    }\n}\n\nimpl WeakNamespace {\n    /// Trivially construct a WeakNamespace.\n    #[inline]\n    pub unsafe fn new<'a>(atom: *mut nsAtom) -> &'a Self {\n        &*(atom as *const WeakNamespace)\n    }\n\n    /// Clone this WeakNamespace to obtain a strong reference to the same\n    /// underlying namespace.\n    #[inline]\n    pub fn clone(&self) -> Namespace {\n        Namespace(self.0.clone())\n    }\n}\n\nimpl Eq for WeakNamespace {}\nimpl PartialEq for WeakNamespace {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        let weak: *const WeakNamespace = self;\n        let other: *const WeakNamespace = other;\n        weak == other\n    }\n}\n"
  },
  {
    "path": "style/global_style_data.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Global style data\n\nuse crate::context::StyleSystemOptions;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::bindings;\nuse crate::parallel::STYLE_THREAD_STACK_SIZE_KB;\nuse crate::shared_lock::SharedRwLock;\nuse crate::thread_state;\nuse parking_lot::{Mutex, RwLock, RwLockReadGuard};\n#[cfg(all(unix, not(target_arch = \"wasm32\")))]\nuse std::os::unix::thread::{JoinHandleExt, RawPthread};\n#[cfg(windows)]\nuse std::os::windows::{io::AsRawHandle, prelude::RawHandle};\nuse std::{io, sync::LazyLock, thread};\nuse thin_vec::ThinVec;\n\n/// Platform-specific handle to a thread.\n#[cfg(all(unix, not(target_arch = \"wasm32\")))]\npub type PlatformThreadHandle = RawPthread;\n/// Platform-specific handle to a thread.\n#[cfg(windows)]\npub type PlatformThreadHandle = RawHandle;\n\n/// A noop thread join handle for wasm\n/// The usize field is a dummy field to make this type non-zero sized so as not to confuse FFI\n#[cfg(all(target_arch = \"wasm32\", not(feature = \"gecko\")))]\npub struct DummyThreadHandle;\n#[cfg(all(target_arch = \"wasm32\", not(feature = \"gecko\")))]\nimpl DummyThreadHandle {\n    /// A noop thread join method for wasm\n    pub fn join(&self) {\n        // Do nothing\n    }\n}\n#[cfg(all(target_arch = \"wasm32\", not(feature = \"gecko\")))]\n/// Platform-specific handle to a thread.\npub type PlatformThreadHandle = DummyThreadHandle;\n\n/// Global style data\npub struct GlobalStyleData {\n    /// Shared RWLock for CSSOM objects\n    pub shared_lock: SharedRwLock,\n\n    /// Global style system options determined by env vars.\n    pub options: StyleSystemOptions,\n}\n\n/// Global thread pool.\npub struct StyleThreadPool {\n    /// How many threads parallel styling can use. If not using a thread pool, this is set to `None`.\n    pub num_threads: Option<usize>,\n\n    /// The parallel styling thread pool.\n    ///\n    /// For leak-checking purposes, we want to terminate the thread-pool, which\n    /// waits for all the async jobs to complete. Thus the RwLock.\n    style_thread_pool: RwLock<Option<rayon::ThreadPool>>,\n}\n\nfn thread_name(index: usize) -> String {\n    format!(\"StyleThread#{}\", index)\n}\n\n/// JoinHandles for spawned style threads. These will be joined during\n/// StyleThreadPool::shutdown() after exiting the thread pool.\n///\n/// This would be quite inefficient if rayon destroyed and re-created\n/// threads regularly during threadpool operation in response to demand,\n/// however rayon actually never destroys its threads until the entire\n/// thread pool is shut-down, so the size of this list is bounded.\nstatic STYLE_THREAD_JOIN_HANDLES: Mutex<Vec<thread::JoinHandle<()>>> = Mutex::new(Vec::new());\n\nfn thread_spawn(options: rayon::ThreadBuilder) -> io::Result<()> {\n    let mut b = thread::Builder::new();\n    if let Some(name) = options.name() {\n        b = b.name(name.to_owned());\n    }\n    if let Some(stack_size) = options.stack_size() {\n        b = b.stack_size(stack_size);\n    }\n    let join_handle = b.spawn(|| options.run())?;\n    STYLE_THREAD_JOIN_HANDLES.lock().push(join_handle);\n    Ok(())\n}\n\nfn thread_startup(_index: usize) {\n    thread_state::initialize_layout_worker_thread();\n    #[cfg(feature = \"gecko\")]\n    unsafe {\n        bindings::Gecko_SetJemallocThreadLocalArena(true);\n        let name = thread_name(_index);\n        gecko_profiler::register_thread(&name);\n    }\n}\n\nfn thread_shutdown(_: usize) {\n    #[cfg(feature = \"gecko\")]\n    unsafe {\n        gecko_profiler::unregister_thread();\n        bindings::Gecko_SetJemallocThreadLocalArena(false);\n    }\n}\n\nimpl StyleThreadPool {\n    /// Shuts down the thread pool, waiting for all work to complete.\n    pub fn shutdown() {\n        if STYLE_THREAD_JOIN_HANDLES.lock().is_empty() {\n            return;\n        }\n        {\n            // Drop the pool.\n            let _ = STYLE_THREAD_POOL.style_thread_pool.write().take();\n        }\n\n        // Join spawned threads until all of the threads have been joined. This\n        // will usually be pretty fast, as on shutdown there should be basically\n        // no threads left running.\n        while let Some(join_handle) = STYLE_THREAD_JOIN_HANDLES.lock().pop() {\n            let _ = join_handle.join();\n        }\n    }\n\n    /// Returns a reference to the thread pool.\n    ///\n    /// We only really want to give read-only access to the pool, except\n    /// for shutdown().\n    pub fn pool(&self) -> RwLockReadGuard<'_, Option<rayon::ThreadPool>> {\n        self.style_thread_pool.read()\n    }\n\n    /// Returns a list of the pool's platform-specific thread handles.\n    pub fn get_thread_handles(handles: &mut ThinVec<PlatformThreadHandle>) {\n        // Force the lazy initialization of STYLE_THREAD_POOL so that the threads get spawned and\n        // their join handles are added to STYLE_THREAD_JOIN_HANDLES.\n        LazyLock::force(&STYLE_THREAD_POOL);\n\n        for join_handle in STYLE_THREAD_JOIN_HANDLES.lock().iter() {\n            #[cfg(all(unix, not(target_arch = \"wasm32\")))]\n            let handle = join_handle.as_pthread_t();\n            #[cfg(windows)]\n            let handle = join_handle.as_raw_handle();\n            #[cfg(all(target_arch = \"wasm32\", not(feature = \"gecko\")))]\n            let handle = {\n                let _ = join_handle;\n                DummyThreadHandle\n            };\n\n            handles.push(handle);\n        }\n    }\n}\n\n#[cfg(feature = \"servo\")]\nfn stylo_threads_pref() -> i32 {\n    static_prefs::pref!(\"layout.threads\")\n}\n\n#[cfg(feature = \"gecko\")]\nfn stylo_threads_pref() -> i32 {\n    static_prefs::pref!(\"layout.css.stylo-threads\")\n}\n\n/// The performance benefit of additional threads seems to level off at around six, so we cap it\n/// there on many-core machines (see bug 1431285 comment 14).\npub(crate) const STYLO_MAX_THREADS: usize = 6;\n\n/// Global thread pool\npub static STYLE_THREAD_POOL: LazyLock<StyleThreadPool> = LazyLock::new(|| {\n    use std::cmp;\n    // We always set this pref on startup, before layout or script have had a chance of\n    // accessing (and thus creating) the thread-pool.\n    let threads_pref: i32 = stylo_threads_pref();\n    let num_threads = if threads_pref >= 0 {\n        threads_pref as usize\n    } else {\n        // Gecko may wish to override the default number of threads, for example on\n        // systems with heterogeneous CPUs.\n        #[cfg(feature = \"gecko\")]\n        let num_threads = unsafe { bindings::Gecko_GetNumStyleThreads() };\n        #[cfg(not(feature = \"gecko\"))]\n        let num_threads = -1;\n\n        if num_threads >= 0 {\n            num_threads as usize\n        } else {\n            // The default heuristic is num_virtual_cores * .75. This gives us three threads on a\n            // hyper-threaded dual core, and six threads on a hyper-threaded quad core.\n            cmp::max(num_cpus::get() * 3 / 4, 1)\n        }\n    };\n\n    let num_threads = cmp::min(num_threads, STYLO_MAX_THREADS);\n    // Since the main-thread is also part of the pool, having one thread or less doesn't make\n    // sense.\n    let (pool, num_threads) = if num_threads <= 1 {\n        (None, None)\n    } else {\n        let workers = rayon::ThreadPoolBuilder::new()\n            .spawn_handler(thread_spawn)\n            .use_current_thread()\n            .num_threads(num_threads)\n            .thread_name(thread_name)\n            .start_handler(thread_startup)\n            .exit_handler(thread_shutdown)\n            .stack_size(STYLE_THREAD_STACK_SIZE_KB * 1024)\n            .build();\n        (workers.ok(), Some(num_threads))\n    };\n\n    StyleThreadPool {\n        num_threads,\n        style_thread_pool: RwLock::new(pool),\n    }\n});\n\n/// Global style data\npub static GLOBAL_STYLE_DATA: LazyLock<GlobalStyleData> = LazyLock::new(|| GlobalStyleData {\n    shared_lock: SharedRwLock::new_leaked(),\n    options: StyleSystemOptions::default(),\n});\n"
  },
  {
    "path": "style/invalidation/element/document_state.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! An invalidation processor for style changes due to document state changes.\n\nuse crate::dom::TElement;\nuse crate::invalidation::element::invalidation_map::Dependency;\nuse crate::invalidation::element::invalidator::{\n    DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,\n};\nuse crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};\nuse crate::invalidation::element::state_and_attributes;\nuse crate::stylist::CascadeData;\nuse dom::DocumentState;\nuse selectors::matching::{\n    MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, QuirksMode,\n    SelectorCaches, VisitedHandlingMode,\n};\nuse selectors::OpaqueElement;\n\n/// A struct holding the members necessary to invalidate document state\n/// selectors.\n#[derive(Debug)]\npub struct InvalidationMatchingData {\n    /// The document state that has changed, which makes it always match.\n    pub document_state: DocumentState,\n}\n\nimpl Default for InvalidationMatchingData {\n    #[inline(always)]\n    fn default() -> Self {\n        Self {\n            document_state: DocumentState::empty(),\n        }\n    }\n}\n\n/// An invalidation processor for style changes due to state and attribute\n/// changes.\npub struct DocumentStateInvalidationProcessor<'a, 'b, E: TElement, I> {\n    rules: I,\n    matching_context: MatchingContext<'a, E::Impl>,\n    traversal_map: SiblingTraversalMap<E>,\n    document_states_changed: DocumentState,\n    _marker: std::marker::PhantomData<&'b ()>,\n}\n\nimpl<'a, 'b, E: TElement, I> DocumentStateInvalidationProcessor<'a, 'b, E, I> {\n    /// Creates a new DocumentStateInvalidationProcessor.\n    #[inline]\n    pub fn new(\n        rules: I,\n        document_states_changed: DocumentState,\n        selector_caches: &'a mut SelectorCaches,\n        quirks_mode: QuirksMode,\n    ) -> Self {\n        let mut matching_context = MatchingContext::<'a, E::Impl>::new_for_visited(\n            MatchingMode::Normal,\n            None,\n            selector_caches,\n            VisitedHandlingMode::AllLinksVisitedAndUnvisited,\n            quirks_mode,\n            NeedsSelectorFlags::No,\n            MatchingForInvalidation::No,\n        );\n\n        matching_context.extra_data.invalidation_data.document_state = document_states_changed;\n\n        Self {\n            rules,\n            document_states_changed,\n            matching_context,\n            traversal_map: SiblingTraversalMap::default(),\n            _marker: std::marker::PhantomData,\n        }\n    }\n}\n\nimpl<'a, 'b, E, I> InvalidationProcessor<'b, 'a, E>\n    for DocumentStateInvalidationProcessor<'a, 'b, E, I>\nwhere\n    E: TElement,\n    I: Iterator<Item = &'b CascadeData>,\n{\n    fn check_outer_dependency(&mut self, _: &Dependency, _: E, _: Option<OpaqueElement>) -> bool {\n        debug_assert!(\n            false,\n            \"how, we should only have parent-less dependencies here!\"\n        );\n        true\n    }\n\n    fn collect_invalidations(\n        &mut self,\n        _element: E,\n        self_invalidations: &mut InvalidationVector<'b>,\n        _descendant_invalidations: &mut DescendantInvalidationLists<'b>,\n        _sibling_invalidations: &mut InvalidationVector<'b>,\n    ) -> bool {\n        for cascade_data in &mut self.rules {\n            let map = cascade_data.invalidation_map();\n            for dependency in &map.document_state_selectors {\n                if !dependency.state.intersects(self.document_states_changed) {\n                    continue;\n                }\n\n                // We pass `None` as a scope, as document state selectors aren't\n                // affected by the current scope.\n                //\n                // FIXME(emilio): We should really pass the relevant host for\n                // self.rules, so that we invalidate correctly if the selector\n                // happens to have something like :host(:-moz-window-inactive)\n                // for example.\n                self_invalidations.push(Invalidation::new(\n                    &dependency.dependency,\n                    /* host = */ None,\n                    /* scope = */ None,\n                ));\n            }\n        }\n\n        false\n    }\n\n    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {\n        &mut self.matching_context\n    }\n\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {\n        &self.traversal_map\n    }\n\n    fn recursion_limit_exceeded(&mut self, _: E) {\n        unreachable!(\"We don't run document state invalidation with stack limits\")\n    }\n\n    fn should_process_descendants(&mut self, element: E) -> bool {\n        match element.borrow_data() {\n            Some(d) => state_and_attributes::should_process_descendants(&d),\n            None => false,\n        }\n    }\n\n    fn invalidated_descendants(&mut self, element: E, child: E) {\n        state_and_attributes::invalidated_descendants(element, child)\n    }\n\n    fn invalidated_self(&mut self, element: E) {\n        state_and_attributes::invalidated_self(element);\n    }\n\n    fn invalidated_sibling(&mut self, sibling: E, of: E) {\n        state_and_attributes::invalidated_sibling(sibling, of);\n    }\n}\n"
  },
  {
    "path": "style/invalidation/element/element_wrapper.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A wrapper over an element and a snapshot, that allows us to selector-match\n//! against a past state of the element.\n\nuse crate::dom::TElement;\nuse crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};\nuse crate::selector_parser::{Snapshot, SnapshotMap};\nuse crate::values::AtomIdent;\nuse crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom};\nuse dom::ElementState;\nuse selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};\nuse selectors::bloom::BloomFilter;\nuse selectors::matching::{ElementSelectorFlags, MatchingContext};\nuse selectors::{Element, OpaqueElement};\nuse std::cell::Cell;\nuse std::fmt;\n\n/// In order to compute restyle hints, we perform a selector match against a\n/// list of partial selectors whose rightmost simple selector may be sensitive\n/// to the thing being changed. We do this matching twice, once for the element\n/// as it exists now and once for the element as it existed at the time of the\n/// last restyle. If the results of the selector match differ, that means that\n/// the given partial selector is sensitive to the change, and we compute a\n/// restyle hint based on its combinator.\n///\n/// In order to run selector matching against the old element state, we generate\n/// a wrapper for the element which claims to have the old state. This is the\n/// ElementWrapper logic below.\n///\n/// Gecko does this differently for element states, and passes a mask called\n/// mStateMask, which indicates the states that need to be ignored during\n/// selector matching. This saves an ElementWrapper allocation and an additional\n/// selector match call at the expense of additional complexity inside the\n/// selector matching logic. This only works for boolean states though, so we\n/// still need to take the ElementWrapper approach for attribute-dependent\n/// style. So we do it the same both ways for now to reduce complexity, but it's\n/// worth measuring the performance impact (if any) of the mStateMask approach.\npub trait ElementSnapshot: Sized {\n    /// The state of the snapshot, if any.\n    fn state(&self) -> Option<ElementState>;\n\n    /// If this snapshot contains attribute information.\n    fn has_attrs(&self) -> bool;\n\n    /// Gets the attribute information of the snapshot as a string.\n    ///\n    /// Only for debugging purposes.\n    fn debug_list_attributes(&self) -> String {\n        String::new()\n    }\n\n    /// The ID attribute per this snapshot. Should only be called if\n    /// `has_attrs()` returns true.\n    fn id_attr(&self) -> Option<&WeakAtom>;\n\n    /// Whether this snapshot contains the class `name`. Should only be called\n    /// if `has_attrs()` returns true.\n    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;\n\n    /// Whether this snapshot represents the part named `name`. Should only be\n    /// called if `has_attrs()` returns true.\n    fn is_part(&self, name: &AtomIdent) -> bool;\n\n    /// See Element::imported_part.\n    fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent>;\n\n    /// A callback that should be called for each class of the snapshot. Should\n    /// only be called if `has_attrs()` returns true.\n    fn each_class<F>(&self, _: F)\n    where\n        F: FnMut(&AtomIdent);\n\n    /// If this snapshot contains CustomStateSet information.\n    fn has_custom_states(&self) -> bool;\n\n    /// A callback that should be called for each CustomState of the snapshot.\n    fn has_custom_state(&self, state: &AtomIdent) -> bool;\n\n    /// A callback that should be called for each CustomState of the snapshot.\n    fn each_custom_state<F>(&self, callback: F)\n    where\n        F: FnMut(&AtomIdent);\n\n    /// The `xml:lang=\"\"` or `lang=\"\"` attribute value per this snapshot.\n    fn lang_attr(&self) -> Option<AttrValue>;\n}\n\n/// A simple wrapper over an element and a snapshot, that allows us to\n/// selector-match against a past state of the element.\n#[derive(Clone)]\npub struct ElementWrapper<'a, E>\nwhere\n    E: TElement,\n{\n    element: E,\n    cached_snapshot: Cell<Option<&'a Snapshot>>,\n    snapshot_map: &'a SnapshotMap,\n}\n\nimpl<'a, E> ElementWrapper<'a, E>\nwhere\n    E: TElement,\n{\n    /// Trivially constructs an `ElementWrapper`.\n    pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {\n        ElementWrapper {\n            element: el,\n            cached_snapshot: Cell::new(None),\n            snapshot_map: snapshot_map,\n        }\n    }\n\n    /// Gets the snapshot associated with this element, if any.\n    pub fn snapshot(&self) -> Option<&'a Snapshot> {\n        if !self.element.has_snapshot() {\n            return None;\n        }\n\n        if let Some(s) = self.cached_snapshot.get() {\n            return Some(s);\n        }\n\n        let snapshot = self.snapshot_map.get(&self.element);\n        debug_assert!(snapshot.is_some(), \"has_snapshot lied!\");\n\n        self.cached_snapshot.set(snapshot);\n\n        snapshot\n    }\n\n    /// Returns the states that have changed since the element was snapshotted.\n    pub fn state_changes(&self) -> ElementState {\n        let snapshot = match self.snapshot() {\n            Some(s) => s,\n            None => return ElementState::empty(),\n        };\n\n        match snapshot.state() {\n            Some(state) => state ^ self.element.state(),\n            None => ElementState::empty(),\n        }\n    }\n\n    /// Returns the value of the `xml:lang=\"\"` (or, if appropriate, `lang=\"\"`)\n    /// attribute from this element's snapshot or the closest ancestor\n    /// element snapshot with the attribute specified.\n    fn get_lang(&self) -> Option<AttrValue> {\n        let mut current = self.clone();\n        loop {\n            let lang = match self.snapshot() {\n                Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),\n                _ => current.element.lang_attr(),\n            };\n            if lang.is_some() {\n                return lang;\n            }\n            current = current.parent_element()?;\n        }\n    }\n}\n\nimpl<'a, E> fmt::Debug for ElementWrapper<'a, E>\nwhere\n    E: TElement,\n{\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        // Ignore other fields for now, can change later if needed.\n        self.element.fmt(f)\n    }\n}\n\nimpl<'a, E> Element for ElementWrapper<'a, E>\nwhere\n    E: TElement,\n{\n    type Impl = SelectorImpl;\n\n    fn match_non_ts_pseudo_class(\n        &self,\n        pseudo_class: &NonTSPseudoClass,\n        context: &mut MatchingContext<Self::Impl>,\n    ) -> bool {\n        // Some pseudo-classes need special handling to evaluate them against\n        // the snapshot.\n        match *pseudo_class {\n            // For :link and :visited, we don't actually want to test the\n            // element state directly.\n            //\n            // Instead, we use the `visited_handling` to determine if they\n            // match.\n            NonTSPseudoClass::Link => {\n                return self.is_link() && context.visited_handling().matches_unvisited();\n            },\n            NonTSPseudoClass::Visited => {\n                return self.is_link() && context.visited_handling().matches_visited();\n            },\n\n            #[cfg(feature = \"gecko\")]\n            NonTSPseudoClass::MozTableBorderNonzero => {\n                if let Some(snapshot) = self.snapshot() {\n                    if snapshot.has_other_pseudo_class_state() {\n                        return snapshot.mIsTableBorderNonzero();\n                    }\n                }\n            },\n\n            #[cfg(feature = \"gecko\")]\n            NonTSPseudoClass::MozSelectListBox => {\n                if let Some(snapshot) = self.snapshot() {\n                    if snapshot.has_other_pseudo_class_state() {\n                        return snapshot.mIsSelectListBox();\n                    }\n                }\n            },\n\n            // :lang() needs to match using the closest ancestor xml:lang=\"\" or\n            // lang=\"\" attribtue from snapshots.\n            NonTSPseudoClass::Lang(ref lang_arg) => {\n                return self\n                    .element\n                    .match_element_lang(Some(self.get_lang()), lang_arg);\n            },\n\n            // :heading should match against snapshot before element\n            #[cfg(feature = \"gecko\")]\n            NonTSPseudoClass::Heading(ref levels) => {\n                return levels.matches_state(\n                    self.snapshot()\n                        .and_then(|s| s.state())\n                        .unwrap_or_else(|| self.element.state()),\n                );\n            },\n\n            // CustomStateSet should match against the snapshot before element\n            NonTSPseudoClass::CustomState(ref state) => return self.has_custom_state(&state.0),\n\n            _ => {},\n        }\n\n        let flag = pseudo_class.state_flag();\n        if flag.is_empty() {\n            return self\n                .element\n                .match_non_ts_pseudo_class(pseudo_class, context);\n        }\n        match self.snapshot().and_then(|s| s.state()) {\n            Some(snapshot_state) => snapshot_state.intersects(flag),\n            None => self\n                .element\n                .match_non_ts_pseudo_class(pseudo_class, context),\n        }\n    }\n\n    fn apply_selector_flags(&self, _flags: ElementSelectorFlags) {\n        debug_assert!(false, \"Shouldn't need selector flags for invalidation\");\n    }\n\n    fn match_pseudo_element(\n        &self,\n        pseudo_element: &PseudoElement,\n        context: &mut MatchingContext<Self::Impl>,\n    ) -> bool {\n        self.element.match_pseudo_element(pseudo_element, context)\n    }\n\n    fn is_link(&self) -> bool {\n        match self.snapshot().and_then(|s| s.state()) {\n            Some(state) => state.intersects(ElementState::VISITED_OR_UNVISITED),\n            None => self.element.is_link(),\n        }\n    }\n\n    fn opaque(&self) -> OpaqueElement {\n        self.element.opaque()\n    }\n\n    fn parent_element(&self) -> Option<Self> {\n        let parent = self.element.parent_element()?;\n        Some(Self::new(parent, self.snapshot_map))\n    }\n\n    fn parent_node_is_shadow_root(&self) -> bool {\n        self.element.parent_node_is_shadow_root()\n    }\n\n    fn containing_shadow_host(&self) -> Option<Self> {\n        let host = self.element.containing_shadow_host()?;\n        Some(Self::new(host, self.snapshot_map))\n    }\n\n    fn prev_sibling_element(&self) -> Option<Self> {\n        let sibling = self.element.prev_sibling_element()?;\n        Some(Self::new(sibling, self.snapshot_map))\n    }\n\n    fn next_sibling_element(&self) -> Option<Self> {\n        let sibling = self.element.next_sibling_element()?;\n        Some(Self::new(sibling, self.snapshot_map))\n    }\n\n    fn first_element_child(&self) -> Option<Self> {\n        let child = self.element.first_element_child()?;\n        Some(Self::new(child, self.snapshot_map))\n    }\n\n    #[inline]\n    fn is_html_element_in_html_document(&self) -> bool {\n        self.element.is_html_element_in_html_document()\n    }\n\n    #[inline]\n    fn is_html_slot_element(&self) -> bool {\n        self.element.is_html_slot_element()\n    }\n\n    #[inline]\n    fn has_local_name(\n        &self,\n        local_name: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName,\n    ) -> bool {\n        self.element.has_local_name(local_name)\n    }\n\n    #[inline]\n    fn has_namespace(\n        &self,\n        ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl,\n    ) -> bool {\n        self.element.has_namespace(ns)\n    }\n\n    #[inline]\n    fn is_same_type(&self, other: &Self) -> bool {\n        self.element.is_same_type(&other.element)\n    }\n\n    fn attr_matches(\n        &self,\n        ns: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        operation: &AttrSelectorOperation<&AttrValue>,\n    ) -> bool {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_attrs() => {\n                snapshot.attr_matches(ns, local_name, operation)\n            },\n            _ => self.element.attr_matches(ns, local_name, operation),\n        }\n    }\n\n    fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_attrs() => snapshot\n                .id_attr()\n                .map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),\n            _ => self.element.has_id(id, case_sensitivity),\n        }\n    }\n\n    fn is_part(&self, name: &AtomIdent) -> bool {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),\n            _ => self.element.is_part(name),\n        }\n    }\n\n    fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),\n            _ => self.element.imported_part(name),\n        }\n    }\n\n    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),\n            _ => self.element.has_class(name, case_sensitivity),\n        }\n    }\n\n    fn has_custom_state(&self, state: &AtomIdent) -> bool {\n        match self.snapshot() {\n            Some(snapshot) if snapshot.has_custom_states() => snapshot.has_custom_state(state),\n            _ => self.element.has_custom_state(state),\n        }\n    }\n\n    fn is_empty(&self) -> bool {\n        self.element.is_empty()\n    }\n\n    fn is_root(&self) -> bool {\n        self.element.is_root()\n    }\n\n    fn is_pseudo_element(&self) -> bool {\n        self.element.is_pseudo_element()\n    }\n\n    fn pseudo_element_originating_element(&self) -> Option<Self> {\n        self.element\n            .pseudo_element_originating_element()\n            .map(|e| ElementWrapper::new(e, self.snapshot_map))\n    }\n\n    fn assigned_slot(&self) -> Option<Self> {\n        self.element\n            .assigned_slot()\n            .map(|e| ElementWrapper::new(e, self.snapshot_map))\n    }\n\n    fn add_element_unique_hashes(&self, _filter: &mut BloomFilter) -> bool {\n        // Should not be relevant in the context of checking past elements in invalidation.\n        false\n    }\n}\n"
  },
  {
    "path": "style/invalidation/element/invalidation_map.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Code for invalidations due to state or attribute changes.\n\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::selector_map::{\n    MaybeCaseInsensitiveHashMap, PrecomputedHashMap, SelectorMap, SelectorMapEntry,\n};\nuse crate::selector_parser::{NonTSPseudoClass, SelectorImpl};\nuse crate::values::AtomIdent;\nuse crate::AllocErr;\nuse crate::{Atom, LocalName, Namespace, ShrinkIfNeeded};\nuse dom::{DocumentState, ElementState};\nuse selectors::attr::NamespaceConstraint;\nuse selectors::parser::{\n    Combinator, Component, RelativeSelector, RelativeSelectorCombinatorCount,\n    RelativeSelectorMatchHint,\n};\nuse selectors::parser::{Selector, SelectorIter};\nuse selectors::visitor::{SelectorListKind, SelectorVisitor};\nuse servo_arc::ThinArc;\nuse smallvec::SmallVec;\n\n/// Mapping between (partial) CompoundSelectors (and the combinator to their\n/// right) and the states and attributes they depend on.\n///\n/// In general, for all selectors in all applicable stylesheets of the form:\n///\n/// |a _ b _ c _ d _ e|\n///\n/// Where:\n///   * |b| and |d| are simple selectors that depend on state (like :hover) or\n///     attributes (like [attr...], .foo, or #foo).\n///   * |a|, |c|, and |e| are arbitrary simple selectors that do not depend on\n///     state or attributes.\n///\n/// We generate a Dependency for both |a _ b:X _| and |a _ b:X _ c _ d:Y _|,\n/// even though those selectors may not appear on their own in any stylesheet.\n/// This allows us to quickly scan through the dependency sites of all style\n/// rules and determine the maximum effect that a given state or attribute\n/// change may have on the style of elements in the document.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct Dependency {\n    /// The dependency selector.\n    #[ignore_malloc_size_of = \"CssRules have primary refs, we measure there\"]\n    pub selector: Selector<SelectorImpl>,\n\n    /// The offset into the selector that we should match on.\n    pub selector_offset: usize,\n\n    /// The next dependency for a selector chain. For example, consider\n    /// the following:\n    ///\n    ///     .foo .bar:where(.baz span) .qux\n    ///         ^               ^     ^\n    ///         A               B     C\n    ///\n    ///  We'd generate:\n    ///\n    ///    * One dependency for .qux (offset: 0, next: None)\n    ///    * One dependency for .baz pointing to B with next being a\n    ///      dependency pointing to C.\n    ///    * One dependency from .bar pointing to C (next: None)\n    ///    * One dependency from .foo pointing to A (next: None)\n    ///\n    /// Scope blocks can add multiple next entries: e.g. With\n    /// @scope (.a) { .b {/*...*/ } .c { /*...*/ }}\n    /// .a's Dependency would have two entries, for .b and .c.\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub next: Option<ThinArc<(), Dependency>>,\n\n    /// What kind of selector invalidation this generates.\n    kind: DependencyInvalidationKind,\n}\n\nimpl SelectorMapEntry for Dependency {\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl> {\n        self.selector.iter_from(self.selector_offset)\n    }\n}\n\n/// The kind of elements down the tree this dependency may affect.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, MallocSizeOf)]\npub enum NormalDependencyInvalidationKind {\n    /// This dependency may affect the element that changed itself.\n    Element,\n    /// This dependency affects the style of the element itself, and also the\n    /// style of its descendants.\n    ///\n    /// TODO(emilio): Each time this feels more of a hack for eager pseudos...\n    ElementAndDescendants,\n    /// This dependency may affect descendants down the tree.\n    Descendants,\n    /// This dependency may affect siblings to the right of the element that\n    /// changed.\n    Siblings,\n    /// This dependency may affect slotted elements of the element that changed.\n    SlottedElements,\n    /// This dependency may affect parts of the element that changed.\n    Parts,\n}\n\n/// The kind of elements up the tree this relative selector dependency may\n/// affect. Because this travels upwards, it's not viable for parallel subtree\n/// traversal, and is handled separately.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, MallocSizeOf)]\npub enum RelativeDependencyInvalidationKind {\n    /// This dependency may affect relative selector anchors for ancestors.\n    Ancestors,\n    /// This dependency may affect a relative selector anchor for the parent.\n    Parent,\n    /// This dependency may affect a relative selector anchor for the previous sibling.\n    PrevSibling,\n    /// This dependency may affect relative selector anchors for ancestors' previous siblings.\n    AncestorPrevSibling,\n    /// This dependency may affect relative selector anchors for earlier siblings.\n    EarlierSibling,\n    /// This dependency may affect relative selector anchors for ancestors' earlier siblings.\n    AncestorEarlierSibling,\n}\n\n/// The kind of invalidation the subject of this dependency triggers.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, MallocSizeOf)]\npub enum ScopeDependencyInvalidationKind {\n    /// This dependency's subject is an explicit scope root\n    ExplicitScope,\n    /// This dependency's subject is an implicit scope root\n    ImplicitScope,\n    /// This dependency's subject is an end scope condition\n    ScopeEnd,\n}\n\n/// Invalidation kind merging normal and relative dependencies.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, MallocSizeOf)]\npub enum DependencyInvalidationKind {\n    /// This dependency is for full selector invalidation.\n    /// It is assuumed that there will be no next dependency to look for.\n    FullSelector,\n    /// This dependency is a normal dependency.\n    Normal(NormalDependencyInvalidationKind),\n    /// This dependency is a relative dependency.\n    Relative(RelativeDependencyInvalidationKind),\n    /// This dependency is a scope dependency.\n    Scope(ScopeDependencyInvalidationKind),\n}\n\n/// The type of invalidation a non-relative selector can generate.\n#[derive(Clone, Copy, Debug, MallocSizeOf)]\npub enum GeneratedInvalidation<'a> {\n    /// Generates a normal invalidation.\n    Normal,\n    /// Generates a scope invalidation.\n    Scope(Option<&'a ThinArc<(), Dependency>>),\n}\n\n/// Return the type of normal invalidation given a selector & an offset.\n#[inline(always)]\nfn get_non_relative_invalidation_kind(\n    selector: &Selector<SelectorImpl>,\n    selector_offset: usize,\n    scope_kind: Option<ScopeDependencyInvalidationKind>,\n) -> DependencyInvalidationKind {\n    if let Some(kind) = scope_kind {\n        return DependencyInvalidationKind::Scope(kind);\n    }\n    if selector_offset == 0 {\n        return DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element);\n    }\n    let combinator = selector.combinator_at_match_order(selector_offset - 1);\n    DependencyInvalidationKind::Normal(match combinator {\n        Combinator::Child | Combinator::Descendant => NormalDependencyInvalidationKind::Descendants,\n        Combinator::LaterSibling | Combinator::NextSibling => {\n            NormalDependencyInvalidationKind::Siblings\n        },\n        Combinator::PseudoElement => NormalDependencyInvalidationKind::ElementAndDescendants,\n        Combinator::SlotAssignment => NormalDependencyInvalidationKind::SlottedElements,\n        Combinator::Part => NormalDependencyInvalidationKind::Parts,\n    })\n}\n\nimpl Dependency {\n    /// Generate a new dependency\n    pub fn new(\n        selector: Selector<SelectorImpl>,\n        selector_offset: usize,\n        next: Option<ThinArc<(), Dependency>>,\n        kind: DependencyInvalidationKind,\n    ) -> Self {\n        Self {\n            selector,\n            selector_offset,\n            next,\n            kind,\n        }\n    }\n    /// Creates a dummy dependency to invalidate the whole selector.\n    ///\n    /// This is necessary because document state invalidation wants to\n    /// invalidate all elements in the document.\n    ///\n    /// The offset is such as that Invalidation::new(self) returns a zero\n    /// offset. That is, it points to a virtual \"combinator\" outside of the\n    /// selector, so calling combinator() on such a dependency will panic.\n    pub fn for_full_selector_invalidation(selector: Selector<SelectorImpl>) -> Self {\n        Self {\n            selector_offset: selector.len() + 1,\n            selector,\n            next: None,\n            kind: DependencyInvalidationKind::FullSelector,\n        }\n    }\n\n    /// The kind of normal invalidation that this would generate. The dependency\n    /// in question must be a normal dependency.\n    pub fn normal_invalidation_kind(&self) -> NormalDependencyInvalidationKind {\n        if let DependencyInvalidationKind::Normal(kind) = self.kind {\n            return kind;\n        }\n        unreachable!(\"Querying normal invalidation kind on non-normal dependency.\");\n    }\n\n    /// The kind of relative invalidation that this would generate. The dependency\n    /// in question must be a relative dependency.\n    #[inline(always)]\n    pub fn relative_invalidation_kind(&self) -> RelativeDependencyInvalidationKind {\n        if let DependencyInvalidationKind::Relative(kind) = self.kind {\n            return kind;\n        }\n        unreachable!(\"Querying relative invalidation kind on non-relative dependency.\");\n    }\n\n    /// The kind of invalidation that this would generate.\n    pub fn invalidation_kind(&self) -> DependencyInvalidationKind {\n        self.kind\n    }\n\n    /// Is the combinator to the right of this dependency's compound selector\n    /// the next sibling combinator? This matters for insertion/removal in between\n    /// two elements connected through next sibling, e.g. `.foo:has(> .a + .b)`\n    /// where an element gets inserted between `.a` and `.b`.\n    pub fn right_combinator_is_next_sibling(&self) -> bool {\n        if self.selector_offset == 0 {\n            return false;\n        }\n        matches!(\n            self.selector\n                .combinator_at_match_order(self.selector_offset - 1),\n            Combinator::NextSibling\n        )\n    }\n\n    /// Is this dependency's compound selector a single compound in `:has`\n    /// with the next sibling relative combinator i.e. `:has(> .foo)`?\n    /// This matters for insertion between an anchor and an element\n    /// connected through next sibling, e.g. `.a:has(> .b)`.\n    pub fn dependency_is_relative_with_single_next_sibling(&self) -> bool {\n        match self.invalidation_kind() {\n            DependencyInvalidationKind::Relative(kind) => {\n                kind == RelativeDependencyInvalidationKind::PrevSibling\n            },\n            _ => false,\n        }\n    }\n}\n\n/// The same, but for state selectors, which can track more exactly what state\n/// do they track.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct StateDependency {\n    /// The other dependency fields.\n    pub dep: Dependency,\n    /// The state this dependency is affected by.\n    pub state: ElementState,\n}\n\nimpl SelectorMapEntry for StateDependency {\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl> {\n        self.dep.selector()\n    }\n}\n\n/// The same, but for document state selectors.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct DocumentStateDependency {\n    /// We track `Dependency` even though we don't need to track an offset,\n    /// since when it changes it changes for the whole document anyway.\n    #[cfg_attr(\n        feature = \"gecko\",\n        ignore_malloc_size_of = \"CssRules have primary refs, we measure there\"\n    )]\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n    pub dependency: Dependency,\n    /// The state this dependency is affected by.\n    pub state: DocumentState,\n}\n\n/// Dependency mapping for classes or IDs.\npub type IdOrClassDependencyMap = MaybeCaseInsensitiveHashMap<Atom, SmallVec<[Dependency; 1]>>;\n/// Dependency mapping for non-tree-strctural pseudo-class states.\npub type StateDependencyMap = SelectorMap<StateDependency>;\n/// Dependency mapping for local names.\npub type LocalNameDependencyMap = PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>;\n/// Dependency mapping for customstates\npub type CustomStateDependencyMap = PrecomputedHashMap<AtomIdent, SmallVec<[Dependency; 1]>>;\n\n/// A map where we store invalidations.\n///\n/// This is slightly different to a SelectorMap, in the sense of that the same\n/// selector may appear multiple times.\n///\n/// In particular, we want to lookup as few things as possible to get the fewer\n/// selectors the better, so this looks up by id, class, or looks at the list of\n/// state/other attribute affecting selectors.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct InvalidationMap {\n    /// A map from a given class name to all the selectors with that class\n    /// selector.\n    pub class_to_selector: IdOrClassDependencyMap,\n    /// A map from a given id to all the selectors with that ID in the\n    /// stylesheets currently applying to the document.\n    pub id_to_selector: IdOrClassDependencyMap,\n    /// A map of all the state dependencies.\n    pub state_affecting_selectors: StateDependencyMap,\n    /// A list of document state dependencies in the rules we represent.\n    pub document_state_selectors: Vec<DocumentStateDependency>,\n    /// A map of other attribute affecting selectors.\n    pub other_attribute_affecting_selectors: LocalNameDependencyMap,\n    /// A map of CSS custom states\n    pub custom_state_affecting_selectors: CustomStateDependencyMap,\n}\n\n/// Tree-structural pseudoclasses that we care about for (Relative selector) invalidation.\n/// Specifically, we need to store information on ones that don't generate the inner selector.\n/// Given the nature of these selectors:\n/// * These are only relevant during DOM mutation invalidations\n/// * Some invalidations may be optimized away.\n#[derive(Clone, Copy, Debug, MallocSizeOf)]\npub struct TSStateForInvalidation(u8);\n\nbitflags! {\n    impl TSStateForInvalidation : u8 {\n        /// :empty. This only needs to be considered for DOM mutation, and for\n        /// elements that do not have any children.\n        const EMPTY = 1 << 0;\n        /// :nth and related selectors, without of.\n        const NTH = 1 << 1;\n        /// :first-child. This only needs to be considered for DOM mutation, and\n        /// for elements that have no previous sibling.\n        const NTH_EDGE_FIRST = 1 << 2;\n        /// :last-child. This only needs to be considered for DOM mutation,\n        /// and for elements have no next sibling.\n        const NTH_EDGE_LAST = 1 << 3;\n    }\n}\n\nimpl TSStateForInvalidation {\n    /// Return true if this state invalidation could be skipped (As per comment\n    /// in the definition of this bitflags)\n    pub fn may_be_optimized(&self) -> bool {\n        (Self::EMPTY | Self::NTH_EDGE_FIRST | Self::NTH_EDGE_LAST).contains(*self)\n    }\n}\n\n/// Dependency for tree-structural pseudo-classes.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct TSStateDependency {\n    /// The other dependency fields.\n    pub dep: Dependency,\n    /// The state this dependency is affected by.\n    pub state: TSStateForInvalidation,\n}\n\nimpl SelectorMapEntry for TSStateDependency {\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl> {\n        self.dep.selector()\n    }\n}\n\n/// Dependency mapping for tree-structural pseudo-class states.\npub type TSStateDependencyMap = SelectorMap<TSStateDependency>;\n/// Dependency mapping for * selectors.\npub type AnyDependencyMap = SmallVec<[Dependency; 1]>;\n\n/// A map to store invalidation dependencies specific to relative selectors.\n/// This keeps a lot more data than the usual map, because any change can generate\n/// upward traversals that need to be handled separately.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct AdditionalRelativeSelectorInvalidationMap {\n    /// A map for a given tree-structural pseudo-class to all the relative selector dependencies with that type.\n    pub ts_state_to_selector: TSStateDependencyMap,\n    /// A map from a given type name to all the relative selector dependencies with that type.\n    pub type_to_selector: LocalNameDependencyMap,\n    /// All relative selector dependencies that specify `*`.\n    pub any_to_selector: AnyDependencyMap,\n    /// Flag indicating if any relative selector is used.\n    pub used: bool,\n    /// Flag indicating if invalidating a relative selector requires ancestor traversal.\n    pub needs_ancestors_traversal: bool,\n}\n\nimpl AdditionalRelativeSelectorInvalidationMap {\n    /// Creates an empty `InvalidationMap`.\n    pub fn new() -> Self {\n        Self {\n            ts_state_to_selector: TSStateDependencyMap::default(),\n            type_to_selector: LocalNameDependencyMap::default(),\n            any_to_selector: SmallVec::default(),\n            used: false,\n            needs_ancestors_traversal: false,\n        }\n    }\n\n    /// Clears this map, leaving it empty.\n    pub fn clear(&mut self) {\n        self.ts_state_to_selector.clear();\n        self.type_to_selector.clear();\n        self.any_to_selector.clear();\n    }\n\n    /// Shrink the capacity of hash maps if needed.\n    pub fn shrink_if_needed(&mut self) {\n        self.ts_state_to_selector.shrink_if_needed();\n        self.type_to_selector.shrink_if_needed();\n    }\n}\n\nimpl InvalidationMap {\n    /// Creates an empty `InvalidationMap`.\n    pub fn new() -> Self {\n        Self {\n            class_to_selector: IdOrClassDependencyMap::new(),\n            id_to_selector: IdOrClassDependencyMap::new(),\n            state_affecting_selectors: StateDependencyMap::new(),\n            document_state_selectors: Vec::new(),\n            other_attribute_affecting_selectors: LocalNameDependencyMap::default(),\n            custom_state_affecting_selectors: CustomStateDependencyMap::default(),\n        }\n    }\n\n    /// Returns the number of dependencies stored in the invalidation map.\n    pub fn len(&self) -> usize {\n        self.state_affecting_selectors.len()\n            + self.document_state_selectors.len()\n            + self\n                .other_attribute_affecting_selectors\n                .iter()\n                .fold(0, |accum, (_, ref v)| accum + v.len())\n            + self\n                .id_to_selector\n                .iter()\n                .fold(0, |accum, (_, ref v)| accum + v.len())\n            + self\n                .class_to_selector\n                .iter()\n                .fold(0, |accum, (_, ref v)| accum + v.len())\n            + self\n                .custom_state_affecting_selectors\n                .iter()\n                .fold(0, |accum, (_, ref v)| accum + v.len())\n    }\n\n    /// Clears this map, leaving it empty.\n    pub fn clear(&mut self) {\n        self.class_to_selector.clear();\n        self.id_to_selector.clear();\n        self.state_affecting_selectors.clear();\n        self.document_state_selectors.clear();\n        self.other_attribute_affecting_selectors.clear();\n        self.custom_state_affecting_selectors.clear();\n    }\n\n    /// Shrink the capacity of hash maps if needed.\n    pub fn shrink_if_needed(&mut self) {\n        self.class_to_selector.shrink_if_needed();\n        self.id_to_selector.shrink_if_needed();\n        self.state_affecting_selectors.shrink_if_needed();\n        self.other_attribute_affecting_selectors.shrink_if_needed();\n        self.custom_state_affecting_selectors.shrink_if_needed();\n    }\n}\n\n/// Adds a selector to the given `InvalidationMap`. Returns Err(..) to signify OOM.\npub fn note_selector_for_invalidation(\n    selector: &Selector<SelectorImpl>,\n    quirks_mode: QuirksMode,\n    map: &mut InvalidationMap,\n    relative_selector_invalidation_map: &mut InvalidationMap,\n    additional_relative_selector_invalidation_map: &mut AdditionalRelativeSelectorInvalidationMap,\n    inner_scope_dependencies: Option<&ThinArc<(), Dependency>>,\n    scope_kind: Option<ScopeDependencyInvalidationKind>,\n) -> Result<Option<Vec<Dependency>>, AllocErr> {\n    let next_dependency = Dependency::for_full_selector_invalidation(selector.clone());\n    let mut document_state = DocumentState::empty();\n    let mut scope_dependencies = ScopeSelectorCollectorState {\n        inner_dependencies: &inner_scope_dependencies.cloned(),\n        this_dependencies: None,\n        scope_kind,\n    };\n\n    {\n        let mut next_stack = NextSelectors::new();\n        let mut alloc_error = None;\n        let mut collector = SelectorDependencyCollector {\n            map,\n            relative_selector_invalidation_map,\n            additional_relative_selector_invalidation_map,\n            document_state: &mut document_state,\n            selector,\n            next_selectors: &mut next_stack,\n            quirks_mode,\n            compound_state: PerCompoundState::new(0),\n            relative_inner_collector: None,\n            scope_dependencies: &mut scope_dependencies,\n            alloc_error: &mut alloc_error,\n        };\n\n        let visit_result = collector.visit_whole_selector();\n\n        debug_assert_eq!(!visit_result, alloc_error.is_some());\n        if let Some(alloc_error) = alloc_error {\n            return Err(alloc_error);\n        }\n    }\n\n    if !document_state.is_empty() {\n        let dep = DocumentStateDependency {\n            state: document_state,\n            dependency: next_dependency,\n        };\n        map.document_state_selectors.try_reserve(1)?;\n        map.document_state_selectors.push(dep);\n    }\n    Ok(scope_dependencies.this_dependencies)\n}\n\nstruct PerCompoundState {\n    /// The offset at which our compound starts.\n    offset: usize,\n\n    /// The state this compound selector is affected by.\n    element_state: ElementState,\n}\n\nimpl PerCompoundState {\n    fn new(offset: usize) -> Self {\n        Self {\n            offset,\n            element_state: ElementState::empty(),\n        }\n    }\n}\n\nstruct NextDependencyEntry {\n    selector: Selector<SelectorImpl>,\n    offset: usize,\n    cached_dependency: Option<ThinArc<(), Dependency>>,\n}\n\nstruct RelativeSelectorInnerCollectorState<'a> {\n    next_dependency: &'a ThinArc<(), Dependency>,\n    relative_compound_state: RelativeSelectorCompoundStateAttributes,\n}\nstruct ScopeSelectorCollectorState<'a> {\n    // Inner scope dependencies this scope selector need to point to\n    inner_dependencies: &'a Option<ThinArc<(), Dependency>>,\n    // Scope dependencies added by this scope selector\n    this_dependencies: Option<Vec<Dependency>>,\n    // Whether this dependency is scope start, end, or other.\n    scope_kind: Option<ScopeDependencyInvalidationKind>,\n}\n\ntrait Collector {\n    fn dependency(&mut self) -> Dependency;\n    fn id_map(&mut self) -> &mut IdOrClassDependencyMap;\n    fn class_map(&mut self) -> &mut IdOrClassDependencyMap;\n    fn state_map(&mut self) -> &mut StateDependencyMap;\n    fn attribute_map(&mut self) -> &mut LocalNameDependencyMap;\n    fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap;\n    fn inner_scope_dependencies(&self) -> Option<ThinArc<(), Dependency>>;\n    fn this_scope_dependencies(&mut self) -> &mut Option<Vec<Dependency>>;\n    fn update_states(&mut self, element_state: ElementState, document_state: DocumentState);\n\n    // In normal invalidations, type-based dependencies don't need to be explicitly tracked;\n    // elements don't change their types, and mutations cause invalidations to go descendant\n    // (Where they are about to be styled anyway), and/or later-sibling direction (Where they\n    // siblings after inserted/removed elements get restyled anyway).\n    // However, for relative selectors, a DOM mutation can affect and arbitrary ancestor and/or\n    // earlier siblings, so we need to keep track of them.\n    fn type_map(&mut self) -> &mut LocalNameDependencyMap {\n        unreachable!();\n    }\n\n    // Tree-structural pseudo-selectors generally invalidates in a well-defined way, which are\n    // handled by RestyleManager. However, for relative selectors, as with type invalidations,\n    // the direction of invalidation becomes arbitrary, so we need to keep track of them.\n    fn ts_state_map(&mut self) -> &mut TSStateDependencyMap {\n        unreachable!();\n    }\n\n    // Same story as type invalidation maps.\n    fn any_vec(&mut self) -> &mut AnyDependencyMap {\n        unreachable!();\n    }\n}\n\nfn on_attribute<C: Collector>(\n    local_name: &LocalName,\n    local_name_lower: &LocalName,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    add_attr_dependency(local_name.clone(), collector)?;\n    if local_name != local_name_lower {\n        add_attr_dependency(local_name_lower.clone(), collector)?;\n    }\n    Ok(())\n}\n\nfn on_id_or_class<C: Collector>(\n    s: &Component<SelectorImpl>,\n    quirks_mode: QuirksMode,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    let dependency = collector.dependency();\n\n    let (atom, map) = match *s {\n        Component::ID(ref atom) => (atom, collector.id_map()),\n        Component::Class(ref atom) => (atom, collector.class_map()),\n        _ => unreachable!(),\n    };\n    let entry = map.try_entry(atom.0.clone(), quirks_mode)?;\n    let vec = entry.or_insert_with(SmallVec::new);\n    vec.try_reserve(1)?;\n    vec.push(dependency);\n    Ok(())\n}\n\nfn on_scope<C: Collector>(collector: &mut C) -> Result<(), AllocErr> {\n    let new_dependency = collector.dependency();\n    let this_scope_dependencies = collector.this_scope_dependencies();\n\n    this_scope_dependencies\n        .get_or_insert(Vec::new())\n        .push(new_dependency);\n\n    Ok(())\n}\n\nfn add_attr_dependency<C: Collector>(name: LocalName, collector: &mut C) -> Result<(), AllocErr> {\n    let dependency = collector.dependency();\n    let map = collector.attribute_map();\n    add_local_name(name, dependency, map)\n}\n\nfn add_custom_state_dependency<C: Collector>(\n    name: AtomIdent,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    let dependency = collector.dependency();\n    let map = collector.custom_state_map();\n    map.try_reserve(1)?;\n    let vec = map.entry(name).or_default();\n    vec.try_reserve(1)?;\n    vec.push(dependency);\n    Ok(())\n}\n\nfn add_local_name(\n    name: LocalName,\n    dependency: Dependency,\n    map: &mut LocalNameDependencyMap,\n) -> Result<(), AllocErr> {\n    map.try_reserve(1)?;\n    let vec = map.entry(name).or_default();\n    vec.try_reserve(1)?;\n    vec.push(dependency);\n    Ok(())\n}\n\nfn on_pseudo_class<C: Collector>(pc: &NonTSPseudoClass, collector: &mut C) -> Result<(), AllocErr> {\n    collector.update_states(pc.state_flag(), pc.document_state_flag());\n\n    let attr_name = match *pc {\n        #[cfg(feature = \"gecko\")]\n        NonTSPseudoClass::MozTableBorderNonzero => local_name!(\"border\"),\n        #[cfg(feature = \"gecko\")]\n        NonTSPseudoClass::MozSelectListBox => {\n            // This depends on two attributes.\n            add_attr_dependency(local_name!(\"multiple\"), collector)?;\n            return add_attr_dependency(local_name!(\"size\"), collector);\n        },\n        NonTSPseudoClass::Lang(..) => local_name!(\"lang\"),\n        NonTSPseudoClass::CustomState(ref name) => {\n            return add_custom_state_dependency(name.0.clone(), collector);\n        },\n        _ => return Ok(()),\n    };\n\n    add_attr_dependency(attr_name, collector)\n}\n\nfn add_pseudo_class_dependency<C: Collector>(\n    element_state: ElementState,\n    quirks_mode: QuirksMode,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    if element_state.is_empty() {\n        return Ok(());\n    }\n    let dependency = collector.dependency();\n    collector.state_map().insert(\n        StateDependency {\n            dep: dependency,\n            state: element_state,\n        },\n        quirks_mode,\n    )\n}\n\n/// Visit all the simple selectors in the iter compound\n/// and return the number of simple selectors visited.\n/// We need to return a tuple because we need to keep\n/// track of two things:\n/// 1) Should the traversal continue and\n/// 2) What the offset of the compound state is.\nfn visit_all_in_iter_compound<T: SelectorVisitor<Impl = SelectorImpl>>(\n    visitor: &mut T,\n    iter: &mut SelectorIter<'_, SelectorImpl>,\n) -> (bool, usize) {\n    let mut index = 0;\n    for ss in iter {\n        if !ss.visit(visitor) {\n            return (false, index);\n        }\n        index += 1;\n    }\n    (true, index)\n}\n\ntype NextSelectors = SmallVec<[NextDependencyEntry; 5]>;\n\n/// A struct that collects invalidations for a given compound selector.\nstruct SelectorDependencyCollector<'a, 'b, 'c> {\n    map: &'a mut InvalidationMap,\n    relative_selector_invalidation_map: &'a mut InvalidationMap,\n    additional_relative_selector_invalidation_map:\n        &'a mut AdditionalRelativeSelectorInvalidationMap,\n\n    /// The document this _complex_ selector is affected by.\n    ///\n    /// We don't need to track state per compound selector, since it's global\n    /// state and it changes for everything.\n    document_state: &'a mut DocumentState,\n\n    /// The current selector and offset we're iterating.\n    selector: &'a Selector<SelectorImpl>,\n\n    /// The stack of next selectors that we have, and at which offset of the\n    /// sequence.\n    ///\n    /// This starts empty. It grows when we find nested :is and :where selector\n    /// lists. The dependency field is cached and reference counted.\n    next_selectors: &'a mut NextSelectors,\n\n    /// The quirks mode of the document where we're inserting dependencies.\n    quirks_mode: QuirksMode,\n\n    /// State relevant to a given compound selector.\n    compound_state: PerCompoundState,\n\n    /// Additional state to keep track of for collecting nested inner selectors of relative selectors\n    /// Holds the next relative selector dependency and the state given to a relative selector.\n    relative_inner_collector: Option<RelativeSelectorInnerCollectorState<'b>>,\n\n    scope_dependencies: &'a mut ScopeSelectorCollectorState<'c>,\n\n    /// The allocation error, if we OOM.\n    alloc_error: &'a mut Option<AllocErr>,\n}\n\nfn next_dependency(\n    next_selector: &mut NextSelectors,\n    next_outer_dependency: Option<&ThinArc<(), Dependency>>,\n    next_scope_dependencies: Option<&ThinArc<(), Dependency>>,\n    scope_kind: Option<ScopeDependencyInvalidationKind>,\n) -> Option<ThinArc<(), Dependency>> {\n    if next_selector.is_empty() {\n        return match next_outer_dependency {\n            Some(..) => next_outer_dependency.cloned(),\n            None => next_scope_dependencies.cloned(),\n        };\n    }\n\n    fn dependencies_from(\n        entries: &mut [NextDependencyEntry],\n        next_outer_dependency: &Option<&ThinArc<(), Dependency>>,\n        next_scope_dependencies: &Option<&ThinArc<(), Dependency>>,\n        scope_kind: Option<ScopeDependencyInvalidationKind>,\n    ) -> Option<ThinArc<(), Dependency>> {\n        if entries.is_empty() {\n            return next_scope_dependencies.cloned();\n        }\n\n        let last_index = entries.len() - 1;\n        let (previous, last) = entries.split_at_mut(last_index);\n        let last = &mut last[0];\n        let selector = &last.selector;\n        let selector_offset = last.offset;\n\n        let dependency = Dependency {\n            selector: selector.clone(),\n            selector_offset,\n            next: dependencies_from(\n                previous,\n                next_outer_dependency,\n                next_scope_dependencies,\n                scope_kind,\n            ),\n            kind: get_non_relative_invalidation_kind(\n                selector,\n                selector_offset,\n                next_scope_dependencies\n                    .is_some()\n                    .then(|| scope_kind)\n                    .flatten(),\n            ),\n        };\n\n        Some(\n            last.cached_dependency\n                .get_or_insert_with(|| ThinArc::from_header_and_iter((), [dependency].into_iter()))\n                .clone(),\n        )\n    }\n\n    dependencies_from(\n        next_selector,\n        &next_outer_dependency,\n        &next_scope_dependencies,\n        scope_kind,\n    )\n}\n\nimpl<'a, 'b, 'c> Collector for SelectorDependencyCollector<'a, 'b, 'c> {\n    fn dependency(&mut self) -> Dependency {\n        let optional_dependency = self\n            .relative_inner_collector\n            .as_ref()\n            .map(|collector| collector.next_dependency);\n\n        let offset = self.compound_state.offset;\n\n        let scope_dependencies = self.inner_scope_dependencies();\n\n        let next = next_dependency(\n            self.next_selectors,\n            optional_dependency,\n            scope_dependencies.as_ref(),\n            self.scope_dependencies.scope_kind,\n        );\n\n        Dependency {\n            selector: self.selector.clone(),\n            selector_offset: offset,\n            next: next,\n            kind: get_non_relative_invalidation_kind(\n                self.selector,\n                offset,\n                scope_dependencies\n                    .is_some()\n                    .then(|| self.scope_dependencies.scope_kind)\n                    .flatten(),\n            ),\n        }\n    }\n\n    fn id_map(&mut self) -> &mut IdOrClassDependencyMap {\n        if self.relative_inner_collector.is_none() {\n            &mut self.map.id_to_selector\n        } else {\n            &mut self.relative_selector_invalidation_map.id_to_selector\n        }\n    }\n\n    fn class_map(&mut self) -> &mut IdOrClassDependencyMap {\n        if self.relative_inner_collector.is_none() {\n            &mut self.map.class_to_selector\n        } else {\n            &mut self.relative_selector_invalidation_map.class_to_selector\n        }\n    }\n\n    fn state_map(&mut self) -> &mut StateDependencyMap {\n        if self.relative_inner_collector.is_none() {\n            &mut self.map.state_affecting_selectors\n        } else {\n            &mut self\n                .relative_selector_invalidation_map\n                .state_affecting_selectors\n        }\n    }\n\n    fn attribute_map(&mut self) -> &mut LocalNameDependencyMap {\n        if self.relative_inner_collector.is_none() {\n            &mut self.map.other_attribute_affecting_selectors\n        } else {\n            &mut self\n                .relative_selector_invalidation_map\n                .other_attribute_affecting_selectors\n        }\n    }\n\n    fn inner_scope_dependencies(&self) -> Option<ThinArc<(), Dependency>> {\n        self.scope_dependencies.inner_dependencies.clone()\n    }\n\n    fn this_scope_dependencies(&mut self) -> &mut Option<Vec<Dependency>> {\n        &mut self.scope_dependencies.this_dependencies\n    }\n\n    fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {\n        self.compound_state.element_state |= element_state;\n        *self.document_state |= document_state;\n    }\n\n    fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {\n        if self.relative_inner_collector.is_none() {\n            &mut self.map.custom_state_affecting_selectors\n        } else {\n            &mut self\n                .relative_selector_invalidation_map\n                .custom_state_affecting_selectors\n        }\n    }\n\n    fn type_map(&mut self) -> &mut LocalNameDependencyMap {\n        debug_assert!(\n            self.relative_inner_collector.is_some(),\n            \"Asking for relative selector invalidation outside of relative selector\"\n        );\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .type_to_selector\n    }\n\n    fn ts_state_map(&mut self) -> &mut TSStateDependencyMap {\n        debug_assert!(\n            self.relative_inner_collector.is_some(),\n            \"Asking for relative selector invalidation outside of relative selector\"\n        );\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .ts_state_to_selector\n    }\n\n    fn any_vec(&mut self) -> &mut AnyDependencyMap {\n        debug_assert!(\n            self.relative_inner_collector.is_some(),\n            \"Asking for relative selector invalidation outside of relative selector\"\n        );\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .any_to_selector\n    }\n}\n\nimpl<'a, 'b, 'c> SelectorDependencyCollector<'a, 'b, 'c> {\n    fn visit_whole_selector(&mut self) -> bool {\n        let iter = self.selector.iter();\n        self.visit_whole_selector_from(iter, 0)\n    }\n\n    fn visit_whole_selector_from(\n        &mut self,\n        mut iter: SelectorIter<SelectorImpl>,\n        mut index: usize,\n    ) -> bool {\n        loop {\n            // Reset the compound state.\n            self.compound_state = PerCompoundState::new(index);\n            if let Some(state) = self.relative_inner_collector.as_mut() {\n                state.relative_compound_state = RelativeSelectorCompoundStateAttributes::new();\n            }\n\n            // Visit all the simple selectors in this sequence.\n            let (keep_traversing, index_offset) = visit_all_in_iter_compound(self, &mut iter);\n\n            if !keep_traversing {\n                return false;\n            }\n\n            index += index_offset;\n\n            if let Err(err) = add_pseudo_class_dependency(\n                self.compound_state.element_state,\n                self.quirks_mode,\n                self,\n            ) {\n                *self.alloc_error = Some(err);\n                return false;\n            }\n\n            if let Some(state) = self\n                .relative_inner_collector\n                .as_ref()\n                .map(|state| state.relative_compound_state)\n            {\n                if let Err(err) =\n                    add_ts_pseudo_class_dependency(state.ts_state, self.quirks_mode, self)\n                {\n                    *self.alloc_error = Some(err);\n                    return false;\n                }\n\n                if !state.added_entry {\n                    // Not great - we didn't add any uniquely identifiable information.\n                    if let Err(err) =\n                        add_non_unique_info(&self.selector, self.compound_state.offset, self)\n                    {\n                        *self.alloc_error = Some(err);\n                        return false;\n                    }\n                }\n            }\n\n            let combinator = iter.next_sequence();\n            if combinator.is_none() {\n                return true;\n            }\n            index += 1; // account for the combinator\n        }\n    }\n}\n\nimpl<'a, 'b, 'c> SelectorVisitor for SelectorDependencyCollector<'a, 'b, 'c> {\n    type Impl = SelectorImpl;\n\n    fn visit_selector_list(\n        &mut self,\n        _list_kind: SelectorListKind,\n        list: &[Selector<SelectorImpl>],\n    ) -> bool {\n        let next_relative_dependency = self\n            .relative_inner_collector\n            .is_some()\n            .then(|| ThinArc::from_header_and_iter((), std::iter::once(self.dependency())));\n        for selector in list {\n            // Here we cheat a bit: We can visit the rightmost compound with\n            // the \"outer\" visitor, and it'd be fine. This reduces the amount of\n            // state and attribute invalidations, and we need to check the outer\n            // selector to the left anyway to avoid over-invalidation, so it\n            // avoids matching it twice uselessly.\n            let mut iter = selector.iter();\n            let saved_added_entry = self\n                .relative_inner_collector\n                .as_ref()\n                .map(|state| state.relative_compound_state.added_entry);\n\n            let (keep_traversing, mut index) = visit_all_in_iter_compound(self, &mut iter);\n\n            if !keep_traversing {\n                return false;\n            }\n\n            if let Some(state) = self.relative_inner_collector.as_mut() {\n                state.relative_compound_state.added_entry = saved_added_entry.unwrap_or_default();\n            }\n            let combinator = iter.next_sequence();\n            if combinator.is_none() {\n                continue;\n            }\n\n            index += 1; // account for the combinator.\n\n            let offset = self.compound_state.offset;\n\n            if self.relative_inner_collector.is_none() {\n                self.next_selectors.push(NextDependencyEntry {\n                    selector: self.selector.clone(),\n                    offset: offset,\n                    cached_dependency: None,\n                });\n            }\n            debug_assert!(\n                next_relative_dependency.is_some() == self.relative_inner_collector.is_some(),\n                \"Next relative dependency and relative inner collector must be Some/None at the same time!\"\n            );\n            let mut nested = SelectorDependencyCollector {\n                map: &mut *self.map,\n                relative_selector_invalidation_map: &mut *self.relative_selector_invalidation_map,\n                additional_relative_selector_invalidation_map: &mut *self\n                    .additional_relative_selector_invalidation_map,\n                document_state: &mut *self.document_state,\n                selector,\n                next_selectors: &mut *self.next_selectors,\n                quirks_mode: self.quirks_mode,\n                compound_state: PerCompoundState::new(index),\n                relative_inner_collector: next_relative_dependency.as_ref().map(\n                    |next_dependency| RelativeSelectorInnerCollectorState {\n                        next_dependency,\n                        relative_compound_state: RelativeSelectorCompoundStateAttributes::new(),\n                    },\n                ),\n                scope_dependencies: &mut self.scope_dependencies,\n                alloc_error: &mut *self.alloc_error,\n            };\n            if !nested.visit_whole_selector_from(iter, index) {\n                return false;\n            }\n            self.next_selectors.pop();\n        }\n        true\n    }\n\n    fn visit_relative_selector_list(\n        &mut self,\n        list: &[selectors::parser::RelativeSelector<Self::Impl>],\n    ) -> bool {\n        // Ignore nested relative selectors. This can happen as a result of nesting.\n        if self.relative_inner_collector.is_some() {\n            return true;\n        }\n\n        self.additional_relative_selector_invalidation_map.used = true;\n        for relative_selector in list {\n            // We can't cheat here like we do with other selector lists - the rightmost\n            // compound of a relative selector is not the subject of the invalidation.\n            self.next_selectors.push(NextDependencyEntry {\n                selector: self.selector.clone(),\n                offset: self.compound_state.offset,\n                cached_dependency: None,\n            });\n            let mut nested = RelativeSelectorDependencyCollector {\n                map: &mut *self.map,\n                relative_selector_invalidation_map: &mut *self.relative_selector_invalidation_map,\n                additional_relative_selector_invalidation_map: &mut *self\n                    .additional_relative_selector_invalidation_map,\n                document_state: &mut *self.document_state,\n                selector: &relative_selector,\n                combinator_count: RelativeSelectorCombinatorCount::new(relative_selector),\n                next_selectors: &mut *self.next_selectors,\n                quirks_mode: self.quirks_mode,\n                compound_state: PerCompoundState::new(0),\n                compound_state_attributes: RelativeSelectorCompoundStateAttributes::new(),\n                scope_dependencies: &mut self.scope_dependencies,\n                alloc_error: &mut *self.alloc_error,\n            };\n            if !nested.visit_whole_selector() {\n                return false;\n            }\n            self.next_selectors.pop();\n        }\n        true\n    }\n\n    fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {\n        match on_simple_selector(s, self.quirks_mode, self) {\n            Ok(result) => {\n                if let ComponentVisitResult::Handled(state) = result {\n                    if let Some(inner_collector_state) = self.relative_inner_collector.as_mut() {\n                        inner_collector_state.relative_compound_state.added_entry = true;\n                        inner_collector_state\n                            .relative_compound_state\n                            .ts_state\n                            .insert(state);\n                    }\n                }\n                true\n            },\n            Err(err) => {\n                *self.alloc_error = Some(err.into());\n                false\n            },\n        }\n    }\n\n    fn visit_attribute_selector(\n        &mut self,\n        _: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        local_name_lower: &LocalName,\n    ) -> bool {\n        if let Some(state) = self.relative_inner_collector.as_mut() {\n            state.relative_compound_state.added_entry = true;\n        }\n        if let Err(err) = on_attribute(local_name, local_name_lower, self) {\n            *self.alloc_error = Some(err);\n            return false;\n        }\n        true\n    }\n}\n\n#[derive(Clone, Copy)]\nstruct RelativeSelectorCompoundStateAttributes {\n    ts_state: TSStateForInvalidation,\n    added_entry: bool,\n}\n\nimpl RelativeSelectorCompoundStateAttributes {\n    fn new() -> Self {\n        Self {\n            ts_state: TSStateForInvalidation::empty(),\n            added_entry: false,\n        }\n    }\n}\n\n/// A struct that collects invalidations for a given compound selector.\nstruct RelativeSelectorDependencyCollector<'a, 'b> {\n    map: &'a mut InvalidationMap,\n    relative_selector_invalidation_map: &'a mut InvalidationMap,\n    additional_relative_selector_invalidation_map:\n        &'a mut AdditionalRelativeSelectorInvalidationMap,\n\n    /// The document this _complex_ selector is affected by.\n    ///\n    /// We don't need to track state per compound selector, since it's global\n    /// state and it changes for everything.\n    document_state: &'a mut DocumentState,\n\n    /// The current inner relative selector and offset we're iterating.\n    selector: &'a RelativeSelector<SelectorImpl>,\n    /// Running combinator for this inner relative selector.\n    combinator_count: RelativeSelectorCombinatorCount,\n\n    /// The stack of next selectors that we have, and at which offset of the\n    /// sequence.\n    ///\n    /// This starts empty. It grows when we find nested :is and :where selector\n    /// lists. The dependency field is cached and reference counted.\n    next_selectors: &'a mut NextSelectors,\n\n    /// The quirks mode of the document where we're inserting dependencies.\n    quirks_mode: QuirksMode,\n\n    /// State relevant to a given compound selector.\n    compound_state: PerCompoundState,\n\n    /// Attributes relevant to the relative compound selector state.\n    compound_state_attributes: RelativeSelectorCompoundStateAttributes,\n\n    scope_dependencies: &'a mut ScopeSelectorCollectorState<'b>,\n\n    /// The allocation error, if we OOM.\n    alloc_error: &'a mut Option<AllocErr>,\n}\n\nfn add_non_unique_info<C: Collector>(\n    selector: &Selector<SelectorImpl>,\n    offset: usize,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    // Go through this compound again.\n    for ss in selector.iter_from(offset) {\n        match ss {\n            Component::LocalName(ref name) => {\n                let dependency = collector.dependency();\n                add_local_name(name.name.clone(), dependency, &mut collector.type_map())?;\n                if name.name != name.lower_name {\n                    let dependency = collector.dependency();\n                    add_local_name(\n                        name.lower_name.clone(),\n                        dependency,\n                        &mut collector.type_map(),\n                    )?;\n                }\n                return Ok(());\n            },\n            _ => (),\n        };\n    }\n    // Ouch. Add one for *.\n    collector.any_vec().try_reserve(1)?;\n    let dependency = collector.dependency();\n    collector.any_vec().push(dependency);\n    Ok(())\n}\n\nfn add_ts_pseudo_class_dependency<C: Collector>(\n    state: TSStateForInvalidation,\n    quirks_mode: QuirksMode,\n    collector: &mut C,\n) -> Result<(), AllocErr> {\n    if state.is_empty() {\n        return Ok(());\n    }\n    let dependency = collector.dependency();\n    collector.ts_state_map().insert(\n        TSStateDependency {\n            dep: dependency,\n            state,\n        },\n        quirks_mode,\n    )\n}\n\nimpl<'a, 'b> RelativeSelectorDependencyCollector<'a, 'b> {\n    fn visit_whole_selector(&mut self) -> bool {\n        let mut iter = self.selector.selector.iter_skip_relative_selector_anchor();\n        let mut index = 0;\n\n        self.additional_relative_selector_invalidation_map\n            .needs_ancestors_traversal |= match self.selector.match_hint {\n            RelativeSelectorMatchHint::InNextSiblingSubtree\n            | RelativeSelectorMatchHint::InSiblingSubtree\n            | RelativeSelectorMatchHint::InSubtree => true,\n            _ => false,\n        };\n        loop {\n            // Reset the compound state.\n            self.compound_state = PerCompoundState::new(index);\n\n            let (keep_traversing, index_offset) = visit_all_in_iter_compound(self, &mut iter);\n\n            if !keep_traversing {\n                return false;\n            }\n\n            index += index_offset;\n\n            if let Err(err) = add_pseudo_class_dependency(\n                self.compound_state.element_state,\n                self.quirks_mode,\n                self,\n            ) {\n                *self.alloc_error = Some(err);\n                return false;\n            }\n\n            if let Err(err) = add_ts_pseudo_class_dependency(\n                self.compound_state_attributes.ts_state,\n                self.quirks_mode,\n                self,\n            ) {\n                *self.alloc_error = Some(err);\n                return false;\n            }\n\n            if !self.compound_state_attributes.added_entry {\n                // Not great - we didn't add any uniquely identifiable information.\n                if let Err(err) =\n                    add_non_unique_info(&self.selector.selector, self.compound_state.offset, self)\n                {\n                    *self.alloc_error = Some(err);\n                    return false;\n                }\n            }\n\n            let combinator = iter.next_sequence();\n            if let Some(c) = combinator {\n                match c {\n                    Combinator::Child | Combinator::Descendant => {\n                        self.combinator_count.child_or_descendants -= 1\n                    },\n                    Combinator::NextSibling | Combinator::LaterSibling => {\n                        self.combinator_count.adjacent_or_next_siblings -= 1\n                    },\n                    Combinator::Part | Combinator::PseudoElement | Combinator::SlotAssignment => (),\n                }\n            } else {\n                return true;\n            }\n            index += 1; // account for the combinator\n        }\n    }\n}\n\nimpl<'a, 'b> Collector for RelativeSelectorDependencyCollector<'a, 'b> {\n    fn dependency(&mut self) -> Dependency {\n        let scope_dependencies = self.inner_scope_dependencies();\n        let scope_kind = self.scope_dependencies.scope_kind;\n\n        let next = next_dependency(\n            self.next_selectors,\n            None,\n            scope_dependencies.as_ref(),\n            scope_kind,\n        );\n        debug_assert!(\n            next.as_ref().is_some_and(|d| !matches!(\n                d.slice()[0].kind,\n                DependencyInvalidationKind::Relative(_)\n            )),\n            \"Duplicate relative dependency?\"\n        );\n        debug_assert!(\n            next.as_ref().is_some_and(|d| !d.slice().is_empty()),\n            \"Empty dependency?\"\n        );\n\n        Dependency {\n            selector: self.selector.selector.clone(),\n            selector_offset: self.compound_state.offset,\n            kind: DependencyInvalidationKind::Relative(\n                match self.combinator_count.get_match_hint() {\n                    RelativeSelectorMatchHint::InChild => {\n                        RelativeDependencyInvalidationKind::Parent\n                    },\n                    RelativeSelectorMatchHint::InSubtree => {\n                        RelativeDependencyInvalidationKind::Ancestors\n                    },\n                    RelativeSelectorMatchHint::InNextSibling => {\n                        RelativeDependencyInvalidationKind::PrevSibling\n                    },\n                    RelativeSelectorMatchHint::InSibling => {\n                        RelativeDependencyInvalidationKind::EarlierSibling\n                    },\n                    RelativeSelectorMatchHint::InNextSiblingSubtree => {\n                        RelativeDependencyInvalidationKind::AncestorPrevSibling\n                    },\n                    RelativeSelectorMatchHint::InSiblingSubtree => {\n                        RelativeDependencyInvalidationKind::AncestorEarlierSibling\n                    },\n                },\n            ),\n            next: next,\n        }\n    }\n\n    fn id_map(&mut self) -> &mut IdOrClassDependencyMap {\n        &mut self.relative_selector_invalidation_map.id_to_selector\n    }\n\n    fn class_map(&mut self) -> &mut IdOrClassDependencyMap {\n        &mut self.relative_selector_invalidation_map.class_to_selector\n    }\n\n    fn state_map(&mut self) -> &mut StateDependencyMap {\n        &mut self\n            .relative_selector_invalidation_map\n            .state_affecting_selectors\n    }\n\n    fn attribute_map(&mut self) -> &mut LocalNameDependencyMap {\n        &mut self\n            .relative_selector_invalidation_map\n            .other_attribute_affecting_selectors\n    }\n\n    fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {\n        &mut self\n            .relative_selector_invalidation_map\n            .custom_state_affecting_selectors\n    }\n\n    fn inner_scope_dependencies(&self) -> Option<ThinArc<(), Dependency>> {\n        self.scope_dependencies.inner_dependencies.clone()\n    }\n\n    fn this_scope_dependencies(&mut self) -> &mut Option<Vec<Dependency>> {\n        &mut self.scope_dependencies.this_dependencies\n    }\n\n    fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {\n        self.compound_state.element_state |= element_state;\n        *self.document_state |= document_state;\n    }\n\n    fn type_map(&mut self) -> &mut LocalNameDependencyMap {\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .type_to_selector\n    }\n\n    fn ts_state_map(&mut self) -> &mut TSStateDependencyMap {\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .ts_state_to_selector\n    }\n\n    fn any_vec(&mut self) -> &mut AnyDependencyMap {\n        &mut self\n            .additional_relative_selector_invalidation_map\n            .any_to_selector\n    }\n}\n\nenum ComponentVisitResult {\n    /// This component is not relevant for building up the invalidation map.\n    IsIrrelevant,\n    /// This component has been added to the invalidation map. Any additional\n    /// tree-structural pseudo-class dependency is also included, if required.\n    Handled(TSStateForInvalidation),\n}\n\n#[inline(always)]\nfn on_simple_selector<C: Collector>(\n    s: &Component<SelectorImpl>,\n    quirks_mode: QuirksMode,\n    collector: &mut C,\n) -> Result<ComponentVisitResult, AllocErr> {\n    match *s {\n        Component::ID(..) | Component::Class(..) => {\n            on_id_or_class(s, quirks_mode, collector)?;\n            Ok(ComponentVisitResult::Handled(\n                TSStateForInvalidation::empty(),\n            ))\n        },\n        Component::ImplicitScope | Component::Scope => {\n            on_scope(collector)?;\n            Ok(ComponentVisitResult::Handled(\n                TSStateForInvalidation::empty(),\n            ))\n        },\n        Component::NonTSPseudoClass(ref pc) => {\n            on_pseudo_class(pc, collector)?;\n            Ok(ComponentVisitResult::Handled(\n                TSStateForInvalidation::empty(),\n            ))\n        },\n        Component::Empty => Ok(ComponentVisitResult::Handled(TSStateForInvalidation::EMPTY)),\n        Component::Nth(data) => {\n            let kind = if data.is_simple_edge() {\n                if data.ty.is_from_end() {\n                    TSStateForInvalidation::NTH_EDGE_LAST\n                } else {\n                    TSStateForInvalidation::NTH_EDGE_FIRST\n                }\n            } else {\n                TSStateForInvalidation::NTH\n            };\n            Ok(ComponentVisitResult::Handled(kind))\n        },\n        Component::RelativeSelectorAnchor => unreachable!(\"Should not visit this far\"),\n        _ => Ok(ComponentVisitResult::IsIrrelevant),\n    }\n}\n\nimpl<'a, 'b> SelectorVisitor for RelativeSelectorDependencyCollector<'a, 'b> {\n    type Impl = SelectorImpl;\n\n    fn visit_selector_list(\n        &mut self,\n        _list_kind: SelectorListKind,\n        list: &[Selector<SelectorImpl>],\n    ) -> bool {\n        let mut next_stack = NextSelectors::new();\n        let next_dependency = ThinArc::from_header_and_iter((), [self.dependency()].into_iter());\n        for selector in list {\n            let mut iter = selector.iter();\n            let saved_added_entry = self.compound_state_attributes.added_entry;\n\n            let (keep_traversing, mut index) = visit_all_in_iter_compound(self, &mut iter);\n\n            if !keep_traversing {\n                return false;\n            }\n\n            let combinator = iter.next_sequence();\n\n            // We want to preserve added_entry, to handle all DOM manipulations\n            // correctly. For example, given `.anchor:has(:not(.foo))`, and a\n            // DOM tree `.anchor > .foo`, insertion of _any_ element without\n            // `.foo` as `.anchor`'s child must trigger an invalidation.\n            self.compound_state_attributes.added_entry = saved_added_entry;\n            if combinator.is_none() {\n                continue;\n            }\n\n            index += 1; // account for the combinator.\n\n            let mut nested = SelectorDependencyCollector {\n                map: &mut *self.map,\n                relative_selector_invalidation_map: &mut *self.relative_selector_invalidation_map,\n                additional_relative_selector_invalidation_map: self\n                    .additional_relative_selector_invalidation_map,\n                document_state: &mut *self.document_state,\n                selector,\n                next_selectors: &mut next_stack,\n                quirks_mode: self.quirks_mode,\n                compound_state: PerCompoundState::new(index),\n                relative_inner_collector: Some(RelativeSelectorInnerCollectorState {\n                    next_dependency: &next_dependency,\n                    relative_compound_state: RelativeSelectorCompoundStateAttributes::new(),\n                }),\n                scope_dependencies: &mut self.scope_dependencies,\n                alloc_error: &mut *self.alloc_error,\n            };\n            if !nested.visit_whole_selector_from(iter, index) {\n                return false;\n            }\n        }\n        true\n    }\n\n    fn visit_relative_selector_list(\n        &mut self,\n        _list: &[selectors::parser::RelativeSelector<Self::Impl>],\n    ) -> bool {\n        // Ignore nested relative selectors. These can happen as a result of nesting.\n        true\n    }\n\n    fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {\n        match on_simple_selector(s, self.quirks_mode, self) {\n            Ok(result) => {\n                if let ComponentVisitResult::Handled(state) = result {\n                    self.compound_state_attributes.added_entry = true;\n                    self.compound_state_attributes.ts_state.insert(state);\n                }\n                true\n            },\n            Err(err) => {\n                *self.alloc_error = Some(err.into());\n                false\n            },\n        }\n    }\n\n    fn visit_attribute_selector(\n        &mut self,\n        _: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        local_name_lower: &LocalName,\n    ) -> bool {\n        self.compound_state_attributes.added_entry = true;\n        if let Err(err) = on_attribute(local_name, local_name_lower, self) {\n            *self.alloc_error = Some(err);\n            return false;\n        }\n        true\n    }\n}\n"
  },
  {
    "path": "style/invalidation/element/invalidator.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The struct that takes care of encapsulating all the logic on where and how\n//! element styles need to be invalidated.\n\nuse crate::context::StackLimitChecker;\nuse crate::dom::{TElement, TNode, TShadowRoot};\nuse crate::invalidation::element::invalidation_map::{\n    Dependency, DependencyInvalidationKind, NormalDependencyInvalidationKind,\n    RelativeDependencyInvalidationKind, ScopeDependencyInvalidationKind,\n};\nuse selectors::matching::matches_compound_selector_from;\nuse selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};\nuse selectors::parser::{Combinator, Component, Selector, SelectorVisitor};\nuse selectors::{OpaqueElement, SelectorImpl};\nuse smallvec::{smallvec, SmallVec};\nuse std::fmt;\nuse std::fmt::Write;\n\nstruct SiblingInfo<E>\nwhere\n    E: TElement,\n{\n    affected: E,\n    prev_sibling: Option<E>,\n    next_sibling: Option<E>,\n}\n\n/// Traversal mapping for elements under consideration. It acts like a snapshot map,\n/// though this only \"maps\" one element at most.\n/// For general invalidations, this has no effect, especially since when\n/// DOM mutates, the mutation's effect should not escape the subtree being mutated.\n/// This is not the case for relative selectors, unfortunately, so we may end up\n/// traversing a portion of the DOM tree that mutated. In case the mutation is removal,\n/// its sibling relation is severed by the time the invalidation happens. This structure\n/// recovers that relation. Note - it assumes that there is only one element under this\n/// effect.\npub struct SiblingTraversalMap<E>\nwhere\n    E: TElement,\n{\n    info: Option<SiblingInfo<E>>,\n}\n\nimpl<E> Default for SiblingTraversalMap<E>\nwhere\n    E: TElement,\n{\n    fn default() -> Self {\n        Self { info: None }\n    }\n}\n\nimpl<E> SiblingTraversalMap<E>\nwhere\n    E: TElement,\n{\n    /// Create a new traversal map with the affected element.\n    pub fn new(affected: E, prev_sibling: Option<E>, next_sibling: Option<E>) -> Self {\n        Self {\n            info: Some(SiblingInfo {\n                affected,\n                prev_sibling,\n                next_sibling,\n            }),\n        }\n    }\n\n    /// Get the element's previous sibling element.\n    pub fn next_sibling_for(&self, element: &E) -> Option<E> {\n        if let Some(ref info) = self.info {\n            if *element == info.affected {\n                return info.next_sibling;\n            }\n        }\n        element.next_sibling_element()\n    }\n\n    /// Get the element's previous sibling element.\n    pub fn prev_sibling_for(&self, element: &E) -> Option<E> {\n        if let Some(ref info) = self.info {\n            if *element == info.affected {\n                return info.prev_sibling;\n            }\n        }\n        element.prev_sibling_element()\n    }\n}\n\n/// A trait to abstract the collection of invalidations for a given pass.\npub trait InvalidationProcessor<'a, 'b, E>\nwhere\n    E: TElement,\n{\n    /// Whether an invalidation that contains only a pseudo-element selector\n    /// like ::before or ::after triggers invalidation of the element that would\n    /// originate it.\n    fn invalidates_on_pseudo_element(&self) -> bool {\n        false\n    }\n\n    /// Whether the invalidation processor only cares about light-tree\n    /// descendants of a given element, that is, doesn't invalidate\n    /// pseudo-elements, NAC, shadow dom...\n    fn light_tree_only(&self) -> bool {\n        false\n    }\n\n    /// When a dependency from a :where or :is selector matches, it may still be\n    /// the case that we don't need to invalidate the full style. Consider the\n    /// case of:\n    ///\n    ///   div .foo:where(.bar *, .baz) .qux\n    ///\n    /// We can get to the `*` part after a .bar class change, but you only need\n    /// to restyle the element if it also matches .foo.\n    ///\n    /// Similarly, you only need to restyle .baz if the whole result of matching\n    /// the selector changes.\n    ///\n    /// This function is called to check the result of matching the \"outer\"\n    /// dependency that we generate for the parent of the `:where` selector,\n    /// that is, in the case above it should match\n    /// `div .foo:where(.bar *, .baz)`.\n    ///\n    /// `scope` is set to `Some()` if this dependency follows a scope invalidation\n    /// Matching context should be adjusted accordingly with `nest_for_scope`.\n    ///\n    /// Returning true unconditionally here is over-optimistic and may\n    /// over-invalidate.\n    fn check_outer_dependency(\n        &mut self,\n        dependency: &Dependency,\n        element: E,\n        scope: Option<OpaqueElement>,\n    ) -> bool;\n\n    /// The matching context that should be used to process invalidations.\n    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl>;\n\n    /// The traversal map that should be used to process invalidations.\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E>;\n\n    /// Collect invalidations for a given element's descendants and siblings.\n    ///\n    /// Returns whether the element itself was invalidated.\n    fn collect_invalidations(\n        &mut self,\n        element: E,\n        self_invalidations: &mut InvalidationVector<'a>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n        sibling_invalidations: &mut InvalidationVector<'a>,\n    ) -> bool;\n\n    /// Returns whether the invalidation process should process the descendants\n    /// of the given element.\n    fn should_process_descendants(&mut self, element: E) -> bool;\n\n    /// Executes an arbitrary action when the recursion limit is exceded (if\n    /// any).\n    fn recursion_limit_exceeded(&mut self, element: E);\n\n    /// Executes an action when `Self` is invalidated.\n    fn invalidated_self(&mut self, element: E);\n\n    /// Executes an action when `sibling` is invalidated as a sibling of\n    /// `of`.\n    fn invalidated_sibling(&mut self, sibling: E, of: E);\n\n    /// Called when a highlight pseudo-element (::selection, ::highlight,\n    /// ::target-text) style is invalidated. These pseudos have their styles\n    /// resolved lazily during painting rather than during the restyle traversal,\n    /// so style changes don't automatically trigger repaints.\n    fn invalidated_highlight_pseudo(&mut self, _element: E) {}\n\n    /// Executes an action when any descendant of `Self` is invalidated.\n    fn invalidated_descendants(&mut self, element: E, child: E);\n\n    /// Executes an action when an element in a relative selector is reached.\n    /// Lets the dependency to be borrowed for further processing out of the\n    /// invalidation traversal.\n    fn found_relative_selector_invalidation(\n        &mut self,\n        _element: E,\n        _kind: RelativeDependencyInvalidationKind,\n        _relative_dependency: &'a Dependency,\n    ) {\n        debug_assert!(false, \"Reached relative selector dependency\");\n    }\n}\n\n/// Different invalidation lists for descendants.\n#[derive(Debug, Default)]\npub struct DescendantInvalidationLists<'a> {\n    /// Invalidations for normal DOM children and pseudo-elements.\n    ///\n    /// TODO(emilio): Having a list of invalidations just for pseudo-elements\n    /// may save some work here and there.\n    pub dom_descendants: InvalidationVector<'a>,\n    /// Invalidations for slotted children of an element.\n    pub slotted_descendants: InvalidationVector<'a>,\n    /// Invalidations for ::part()s of an element.\n    pub parts: InvalidationVector<'a>,\n}\n\nimpl<'a> DescendantInvalidationLists<'a> {\n    fn is_empty(&self) -> bool {\n        self.dom_descendants.is_empty()\n            && self.slotted_descendants.is_empty()\n            && self.parts.is_empty()\n    }\n}\n\n/// The struct that takes care of encapsulating all the logic on where and how\n/// element styles need to be invalidated.\npub struct TreeStyleInvalidator<'a, 'b, 'c, E, P: 'a>\nwhere\n    'b: 'a,\n    E: TElement,\n    P: InvalidationProcessor<'b, 'c, E>,\n{\n    element: E,\n    stack_limit_checker: Option<&'a StackLimitChecker>,\n    processor: &'a mut P,\n    _marker: std::marker::PhantomData<(&'b (), &'c ())>,\n}\n\n/// A vector of invalidations, optimized for small invalidation sets.\npub type InvalidationVector<'a> = SmallVec<[Invalidation<'a>; 10]>;\n\n/// The kind of descendant invalidation we're processing.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum DescendantInvalidationKind {\n    /// A DOM descendant invalidation.\n    Dom,\n    /// A ::slotted() descendant invalidation.\n    Slotted,\n    /// A ::part() descendant invalidation.\n    Part,\n}\n\n/// The kind of invalidation we're processing.\n///\n/// We can use this to avoid pushing invalidations of the same kind to our\n/// descendants or siblings.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum InvalidationKind {\n    Descendant(DescendantInvalidationKind),\n    Sibling,\n}\n\n/// The kind of traversal an invalidation requires.\npub enum InvalidationAddOverride {\n    /// This invalidation should be added to descendant invalidation\n    Descendant,\n    /// This invalidation should be added to sibling invalidations\n    Sibling,\n}\n\n/// An `Invalidation` is a complex selector that describes which elements,\n/// relative to a current element we are processing, must be restyled.\n#[derive(Clone)]\npub struct Invalidation<'a> {\n    /// The dependency that generated this invalidation.\n    ///\n    /// Note that the offset inside the dependency is not really useful after\n    /// construction.\n    dependency: &'a Dependency,\n    /// The right shadow host from where the rule came from, if any.\n    ///\n    /// This is needed to ensure that we match the selector with the right\n    /// state, as whether some selectors like :host and ::part() match depends\n    /// on it.\n    host: Option<OpaqueElement>,\n    /// The scope element from which this rule comes from, if any.\n    scope: Option<OpaqueElement>,\n    /// The offset of the selector pointing to a compound selector.\n    ///\n    /// This order is a \"parse order\" offset, that is, zero is the leftmost part\n    /// of the selector written as parsed / serialized.\n    ///\n    /// It is initialized from the offset from `dependency`.\n    offset: usize,\n    /// Whether the invalidation was already matched by any previous sibling or\n    /// ancestor.\n    ///\n    /// If this is the case, we can avoid pushing invalidations generated by\n    /// this one if the generated invalidation is effective for all the siblings\n    /// or descendants after us.\n    matched_by_any_previous: bool,\n    /// Whether this incalidation should always be pushed to next invalidations.\n    ///\n    /// This is useful for overriding invalidations we would otherwise skip.\n    ///  e.g @scope(.a){:not(:scope)} where we would need the :not(:scope)\n    /// invalidation to traverse down for all children of the scope root\n    always_effective_for_next_descendant: bool,\n}\n\nimpl<'a> Invalidation<'a> {\n    /// Create a new invalidation for matching a dependency.\n    pub fn new(\n        dependency: &'a Dependency,\n        host: Option<OpaqueElement>,\n        scope: Option<OpaqueElement>,\n    ) -> Self {\n        debug_assert!(\n            dependency.selector_offset == dependency.selector.len() + 1\n                || dependency.invalidation_kind()\n                    != DependencyInvalidationKind::Normal(\n                        NormalDependencyInvalidationKind::Element\n                    ),\n            \"No point to this, if the dependency matched the element we should just invalidate it\"\n        );\n        Self {\n            dependency,\n            host,\n            scope,\n            // + 1 to go past the combinator.\n            offset: dependency.selector.len() + 1 - dependency.selector_offset,\n            matched_by_any_previous: false,\n            always_effective_for_next_descendant: false,\n        }\n    }\n\n    /// Create a new invalidation for matching a dependency from the selector's subject.\n    /// Using this should be avoided whenever possible as it overinvalidates.\n    /// Only use it when it's not possible to match the selector in order due to\n    /// invalidations that don't necessarily start at the pointed compound, such as\n    /// what happens in note_scope_dependency_force_at_subject.\n    pub fn new_subject_invalidation(\n        dependency: &'a Dependency,\n        host: Option<OpaqueElement>,\n        scope: Option<OpaqueElement>,\n    ) -> Self {\n        let mut compound_offset = 0;\n        for s in dependency.selector.iter_raw_match_order() {\n            if s.is_combinator() {\n                break;\n            }\n            compound_offset += 1;\n        }\n\n        Self {\n            dependency,\n            host,\n            scope,\n            offset: dependency.selector.len() - compound_offset,\n            matched_by_any_previous: false,\n            always_effective_for_next_descendant: true,\n        }\n    }\n\n    /// Create a new invalidation for matching a dependency that should always check\n    /// its next descendants. It tends to overinvalidate less than new_subject_invalidation\n    /// but it should also be avoided whenever possible. Specifically used when crossing\n    /// into implicit scope invalidation.\n    pub fn new_always_effective_for_next_descendant(\n        dependency: &'a Dependency,\n        host: Option<OpaqueElement>,\n        scope: Option<OpaqueElement>,\n    ) -> Self {\n        if dependency.selector.is_rightmost(dependency.selector_offset) {\n            return Self::new_subject_invalidation(dependency, host, scope);\n        }\n\n        Self {\n            dependency,\n            host,\n            scope,\n            // + 1 to go past the combinator.\n            offset: dependency.selector.len() + 1 - dependency.selector_offset,\n            matched_by_any_previous: false,\n            always_effective_for_next_descendant: true,\n        }\n    }\n\n    /// Return the combinator to the right of the currently invalidating compound\n    /// Useful for determining whether this invalidation should be pushed to\n    /// sibling or descendant invalidations.\n    pub fn combinator_to_right(&self) -> Combinator {\n        debug_assert_ne!(self.dependency.selector_offset, 0);\n        self.dependency\n            .selector\n            .combinator_at_match_order(self.dependency.selector.len() - self.offset)\n    }\n\n    /// Whether this invalidation is effective for the next sibling or\n    /// descendant after us.\n    fn effective_for_next(&self) -> bool {\n        if self.offset == 0 || self.always_effective_for_next_descendant {\n            return true;\n        }\n\n        // TODO(emilio): For pseudo-elements this should be mostly false, except\n        // for the weird pseudos in <input type=\"number\">.\n        //\n        // We should be able to do better here!\n        match self\n            .dependency\n            .selector\n            .combinator_at_parse_order(self.offset - 1)\n        {\n            Combinator::Descendant | Combinator::LaterSibling | Combinator::PseudoElement => true,\n            Combinator::Part\n            | Combinator::SlotAssignment\n            | Combinator::NextSibling\n            | Combinator::Child => false,\n        }\n    }\n\n    fn kind(&self) -> InvalidationKind {\n        if self.offset == 0 {\n            return InvalidationKind::Descendant(DescendantInvalidationKind::Dom);\n        }\n\n        match self\n            .dependency\n            .selector\n            .combinator_at_parse_order(self.offset - 1)\n        {\n            Combinator::Child | Combinator::Descendant | Combinator::PseudoElement => {\n                InvalidationKind::Descendant(DescendantInvalidationKind::Dom)\n            },\n            Combinator::Part => InvalidationKind::Descendant(DescendantInvalidationKind::Part),\n            Combinator::SlotAssignment => {\n                InvalidationKind::Descendant(DescendantInvalidationKind::Slotted)\n            },\n            Combinator::NextSibling | Combinator::LaterSibling => InvalidationKind::Sibling,\n        }\n    }\n}\n\n/// A struct that visits a selector and determines if there is a `:scope`\n/// component nested withing a negation. eg. :not(:scope)\nstruct NegationScopeVisitor {\n    /// Have we found a negation list yet\n    in_negation: bool,\n    /// Have we found a :scope inside a negation yet\n    found_scope_in_negation: bool,\n}\n\nimpl NegationScopeVisitor {\n    /// Create a new NegationScopeVisitor\n    fn new() -> Self {\n        Self {\n            in_negation: false,\n            found_scope_in_negation: false,\n        }\n    }\n\n    fn traverse_selector(\n        mut self,\n        selector: &Selector<<NegationScopeVisitor as SelectorVisitor>::Impl>,\n    ) -> bool {\n        selector.visit(&mut self);\n        self.found_scope_in_negation\n    }\n\n    /// Traverse all the next dependencies in an outer dependency until we reach\n    /// 1. :not(* :scope *)\n    /// 2. a scope or relative dependency\n    /// 3. the end of the chain of dependencies\n    /// Return whether or not we encountered :not(* :scope *)\n    fn traverse_dependency(mut self, dependency: &Dependency) -> bool {\n        if dependency.next.is_none()\n            || !matches!(\n                dependency.invalidation_kind(),\n                DependencyInvalidationKind::Normal(..)\n            )\n        {\n            dependency.selector.visit(&mut self);\n            return self.found_scope_in_negation;\n        }\n\n        let nested_visitor = Self {\n            in_negation: self.in_negation,\n            found_scope_in_negation: false,\n        };\n        dependency.selector.visit(&mut self);\n        // Has to be normal dependency and next.is_some()\n        nested_visitor.traverse_dependency(&dependency.next.as_ref().unwrap().slice()[0])\n    }\n}\n\nimpl SelectorVisitor for NegationScopeVisitor {\n    type Impl = crate::selector_parser::SelectorImpl;\n\n    fn visit_attribute_selector(\n        &mut self,\n        _namespace: &selectors::attr::NamespaceConstraint<\n            &<Self::Impl as SelectorImpl>::NamespaceUrl,\n        >,\n        _local_name: &<Self::Impl as SelectorImpl>::LocalName,\n        _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,\n    ) -> bool {\n        true\n    }\n\n    fn visit_simple_selector(&mut self, component: &Component<Self::Impl>) -> bool {\n        if self.in_negation {\n            match component {\n                Component::Scope => {\n                    self.found_scope_in_negation = true;\n                },\n                _ => {},\n            }\n        }\n        true\n    }\n\n    fn visit_relative_selector_list(\n        &mut self,\n        _list: &[selectors::parser::RelativeSelector<Self::Impl>],\n    ) -> bool {\n        true\n    }\n\n    fn visit_selector_list(\n        &mut self,\n        list_kind: selectors::visitor::SelectorListKind,\n        list: &[selectors::parser::Selector<Self::Impl>],\n    ) -> bool {\n        for nested in list {\n            let nested_visitor = Self {\n                in_negation: list_kind.in_negation(),\n                found_scope_in_negation: false,\n            };\n\n            self.found_scope_in_negation |= nested_visitor.traverse_selector(nested);\n        }\n        true\n    }\n\n    fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {\n        true\n    }\n}\n\n/// Determines if we can find a selector in the form of :not(:scope)\n/// anywhere down the chain of dependencies.\npub fn any_next_has_scope_in_negation(dependency: &Dependency) -> bool {\n    let next = match dependency.next.as_ref() {\n        None => return false,\n        Some(l) => l,\n    };\n\n    next.slice().iter().any(|dep| {\n        let visitor = NegationScopeVisitor::new();\n        visitor.traverse_dependency(dep)\n    })\n}\n\nimpl<'a> fmt::Debug for Invalidation<'a> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        use cssparser::ToCss;\n\n        f.write_str(\"Invalidation(\")?;\n        for component in self\n            .dependency\n            .selector\n            .iter_raw_parse_order_from(self.offset)\n        {\n            if matches!(*component, Component::Combinator(..)) {\n                break;\n            }\n            component.to_css(f)?;\n        }\n        f.write_char(')')\n    }\n}\n\n/// The result of processing a single invalidation for a given element.\nstruct ProcessInvalidationResult {\n    /// Whether the element itself was invalidated.\n    invalidated_self: bool,\n    /// Whether the invalidation matched, either invalidating the element or\n    /// generating another invalidation.\n    matched: bool,\n}\n\n/// The result of a whole invalidation process for a given element.\npub struct InvalidationResult {\n    /// Whether the element itself was invalidated.\n    invalidated_self: bool,\n    /// Whether the element's descendants were invalidated.\n    invalidated_descendants: bool,\n    /// Whether the element's siblings were invalidated.\n    invalidated_siblings: bool,\n}\n\nimpl InvalidationResult {\n    /// Create an emtpy result.\n    pub fn empty() -> Self {\n        Self {\n            invalidated_self: false,\n            invalidated_descendants: false,\n            invalidated_siblings: false,\n        }\n    }\n\n    /// Whether the invalidation has invalidate the element itself.\n    pub fn has_invalidated_self(&self) -> bool {\n        self.invalidated_self\n    }\n\n    /// Whether the invalidation has invalidate desendants.\n    pub fn has_invalidated_descendants(&self) -> bool {\n        self.invalidated_descendants\n    }\n\n    /// Whether the invalidation has invalidate siblings.\n    pub fn has_invalidated_siblings(&self) -> bool {\n        self.invalidated_siblings\n    }\n}\n\nimpl<'a, 'b, 'c, E, P: 'a> TreeStyleInvalidator<'a, 'b, 'c, E, P>\nwhere\n    'b: 'a,\n    E: TElement,\n    P: InvalidationProcessor<'b, 'c, E>,\n{\n    /// Trivially constructs a new `TreeStyleInvalidator`.\n    pub fn new(\n        element: E,\n        stack_limit_checker: Option<&'a StackLimitChecker>,\n        processor: &'a mut P,\n    ) -> Self {\n        Self {\n            element,\n            stack_limit_checker,\n            processor,\n            _marker: std::marker::PhantomData,\n        }\n    }\n\n    /// Perform the invalidation pass.\n    pub fn invalidate(mut self) -> InvalidationResult {\n        debug!(\"StyleTreeInvalidator::invalidate({:?})\", self.element);\n\n        let mut self_invalidations = InvalidationVector::new();\n        let mut descendant_invalidations = DescendantInvalidationLists::default();\n        let mut sibling_invalidations = InvalidationVector::new();\n\n        let mut invalidated_self = self.processor.collect_invalidations(\n            self.element,\n            &mut self_invalidations,\n            &mut descendant_invalidations,\n            &mut sibling_invalidations,\n        );\n\n        debug!(\"Collected invalidations (self: {}): \", invalidated_self);\n        debug!(\n            \" > self: {}, {:?}\",\n            self_invalidations.len(),\n            self_invalidations\n        );\n        debug!(\" > descendants: {:?}\", descendant_invalidations);\n        debug!(\n            \" > siblings: {}, {:?}\",\n            sibling_invalidations.len(),\n            sibling_invalidations\n        );\n\n        let invalidated_self_from_collection = invalidated_self;\n\n        invalidated_self |= self.process_descendant_invalidations(\n            &self_invalidations,\n            &mut descendant_invalidations,\n            &mut sibling_invalidations,\n            DescendantInvalidationKind::Dom,\n        );\n\n        if invalidated_self && !invalidated_self_from_collection {\n            self.processor.invalidated_self(self.element);\n        }\n\n        let invalidated_descendants = self.invalidate_descendants(&descendant_invalidations);\n        let invalidated_siblings = self.invalidate_siblings(&mut sibling_invalidations);\n\n        InvalidationResult {\n            invalidated_self,\n            invalidated_descendants,\n            invalidated_siblings,\n        }\n    }\n\n    /// Go through later DOM siblings, invalidating style as needed using the\n    /// `sibling_invalidations` list.\n    ///\n    /// Returns whether any sibling's style or any sibling descendant's style\n    /// was invalidated.\n    fn invalidate_siblings(&mut self, sibling_invalidations: &mut InvalidationVector<'b>) -> bool {\n        if sibling_invalidations.is_empty() {\n            return false;\n        }\n\n        let mut current = self\n            .processor\n            .sibling_traversal_map()\n            .next_sibling_for(&self.element);\n        let mut any_invalidated = false;\n\n        while let Some(sibling) = current {\n            let mut sibling_invalidator =\n                TreeStyleInvalidator::new(sibling, self.stack_limit_checker, self.processor);\n\n            let mut invalidations_for_descendants = DescendantInvalidationLists::default();\n            let invalidated_sibling = sibling_invalidator.process_sibling_invalidations(\n                &mut invalidations_for_descendants,\n                sibling_invalidations,\n            );\n\n            if invalidated_sibling {\n                sibling_invalidator\n                    .processor\n                    .invalidated_sibling(sibling, self.element);\n            }\n\n            any_invalidated |= invalidated_sibling;\n\n            any_invalidated |=\n                sibling_invalidator.invalidate_descendants(&invalidations_for_descendants);\n\n            if sibling_invalidations.is_empty() {\n                break;\n            }\n\n            current = self\n                .processor\n                .sibling_traversal_map()\n                .next_sibling_for(&sibling);\n        }\n\n        any_invalidated\n    }\n\n    fn invalidate_pseudo_element_or_nac(\n        &mut self,\n        child: E,\n        invalidations: &[Invalidation<'b>],\n    ) -> bool {\n        let mut sibling_invalidations = InvalidationVector::new();\n\n        let result = self.invalidate_child(\n            child,\n            invalidations,\n            &mut sibling_invalidations,\n            DescendantInvalidationKind::Dom,\n        );\n\n        // Roots of NAC subtrees can indeed generate sibling invalidations, but\n        // they can be just ignored, since they have no siblings.\n        //\n        // Note that we can end up testing selectors that wouldn't end up\n        // matching due to this being NAC, like those coming from document\n        // rules, but we overinvalidate instead of checking this.\n\n        result\n    }\n\n    /// Invalidate a child and recurse down invalidating its descendants if\n    /// needed.\n    fn invalidate_child(\n        &mut self,\n        child: E,\n        invalidations: &[Invalidation<'b>],\n        sibling_invalidations: &mut InvalidationVector<'b>,\n        descendant_invalidation_kind: DescendantInvalidationKind,\n    ) -> bool {\n        let mut invalidations_for_descendants = DescendantInvalidationLists::default();\n\n        let mut invalidated_child = false;\n        let invalidated_descendants = {\n            let mut child_invalidator =\n                TreeStyleInvalidator::new(child, self.stack_limit_checker, self.processor);\n\n            invalidated_child |= child_invalidator.process_sibling_invalidations(\n                &mut invalidations_for_descendants,\n                sibling_invalidations,\n            );\n\n            invalidated_child |= child_invalidator.process_descendant_invalidations(\n                invalidations,\n                &mut invalidations_for_descendants,\n                sibling_invalidations,\n                descendant_invalidation_kind,\n            );\n\n            if invalidated_child {\n                child_invalidator.processor.invalidated_self(child);\n            }\n\n            child_invalidator.invalidate_descendants(&invalidations_for_descendants)\n        };\n\n        // The child may not be a flattened tree child of the current element,\n        // but may be arbitrarily deep.\n        //\n        // Since we keep the traversal flags in terms of the flattened tree,\n        // we need to propagate it as appropriate.\n        if invalidated_child || invalidated_descendants {\n            self.processor.invalidated_descendants(self.element, child);\n        }\n\n        invalidated_child || invalidated_descendants\n    }\n\n    fn invalidate_nac(&mut self, invalidations: &[Invalidation<'b>]) -> bool {\n        let mut any_nac_root = false;\n\n        let element = self.element;\n        element.each_anonymous_content_child(|nac| {\n            any_nac_root |= self.invalidate_pseudo_element_or_nac(nac, invalidations);\n        });\n\n        any_nac_root\n    }\n\n    // NB: It's important that this operates on DOM children, which is what\n    // selector-matching operates on.\n    fn invalidate_dom_descendants_of(\n        &mut self,\n        parent: E::ConcreteNode,\n        invalidations: &[Invalidation<'b>],\n    ) -> bool {\n        let mut any_descendant = false;\n\n        let mut sibling_invalidations = InvalidationVector::new();\n        for child in parent.dom_children() {\n            let child = match child.as_element() {\n                Some(e) => e,\n                None => continue,\n            };\n\n            any_descendant |= self.invalidate_child(\n                child,\n                invalidations,\n                &mut sibling_invalidations,\n                DescendantInvalidationKind::Dom,\n            );\n        }\n\n        any_descendant\n    }\n\n    fn invalidate_parts_in_shadow_tree(\n        &mut self,\n        shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,\n        invalidations: &[Invalidation<'b>],\n    ) -> bool {\n        debug_assert!(!invalidations.is_empty());\n\n        let mut any = false;\n        let mut sibling_invalidations = InvalidationVector::new();\n\n        for node in shadow.as_node().dom_descendants() {\n            let element = match node.as_element() {\n                Some(e) => e,\n                None => continue,\n            };\n\n            if element.has_part_attr() {\n                any |= self.invalidate_child(\n                    element,\n                    invalidations,\n                    &mut sibling_invalidations,\n                    DescendantInvalidationKind::Part,\n                );\n                debug_assert!(\n                    sibling_invalidations.is_empty(),\n                    \"::part() shouldn't have sibling combinators to the right, \\\n                     this makes no sense! {:?}\",\n                    sibling_invalidations\n                );\n            }\n\n            if let Some(shadow) = element.shadow_root() {\n                if element.exports_any_part() {\n                    any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)\n                }\n            }\n        }\n\n        any\n    }\n\n    fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {\n        if invalidations.is_empty() {\n            return false;\n        }\n\n        let shadow = match self.element.shadow_root() {\n            Some(s) => s,\n            None => return false,\n        };\n\n        self.invalidate_parts_in_shadow_tree(shadow, invalidations)\n    }\n\n    fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {\n        if invalidations.is_empty() {\n            return false;\n        }\n\n        let slot = self.element;\n        self.invalidate_slotted_elements_in_slot(slot, invalidations)\n    }\n\n    fn invalidate_slotted_elements_in_slot(\n        &mut self,\n        slot: E,\n        invalidations: &[Invalidation<'b>],\n    ) -> bool {\n        let mut any = false;\n\n        let mut sibling_invalidations = InvalidationVector::new();\n        for node in slot.slotted_nodes() {\n            let element = match node.as_element() {\n                Some(e) => e,\n                None => continue,\n            };\n\n            if element.is_html_slot_element() {\n                any |= self.invalidate_slotted_elements_in_slot(element, invalidations);\n            } else {\n                any |= self.invalidate_child(\n                    element,\n                    invalidations,\n                    &mut sibling_invalidations,\n                    DescendantInvalidationKind::Slotted,\n                );\n            }\n\n            debug_assert!(\n                sibling_invalidations.is_empty(),\n                \"::slotted() shouldn't have sibling combinators to the right, \\\n                 this makes no sense! {:?}\",\n                sibling_invalidations\n            );\n        }\n\n        any\n    }\n\n    fn invalidate_non_slotted_descendants(&mut self, invalidations: &[Invalidation<'b>]) -> bool {\n        if invalidations.is_empty() {\n            return false;\n        }\n\n        if self.processor.light_tree_only() {\n            let node = self.element.as_node();\n            return self.invalidate_dom_descendants_of(node, invalidations);\n        }\n\n        let mut any_descendant = false;\n\n        // NOTE(emilio): This is only needed for Shadow DOM to invalidate\n        // correctly on :host(..) changes. Instead of doing this, we could add\n        // a third kind of invalidation list that walks shadow root children,\n        // but it's not clear it's worth it.\n        //\n        // Also, it's needed as of right now for document state invalidation,\n        // where we rely on iterating every element that ends up in the composed\n        // doc, but we could fix that invalidating per subtree.\n        if let Some(root) = self.element.shadow_root() {\n            any_descendant |= self.invalidate_dom_descendants_of(root.as_node(), invalidations);\n        }\n\n        any_descendant |= self.invalidate_dom_descendants_of(self.element.as_node(), invalidations);\n\n        any_descendant |= self.invalidate_nac(invalidations);\n\n        any_descendant\n    }\n\n    /// Given the descendant invalidation lists, go through the current\n    /// element's descendants, and invalidate style on them.\n    fn invalidate_descendants(&mut self, invalidations: &DescendantInvalidationLists<'b>) -> bool {\n        if invalidations.is_empty() {\n            return false;\n        }\n\n        debug!(\n            \"StyleTreeInvalidator::invalidate_descendants({:?})\",\n            self.element\n        );\n        debug!(\" > {:?}\", invalidations);\n\n        let should_process = self.processor.should_process_descendants(self.element);\n\n        if !should_process {\n            return false;\n        }\n\n        if let Some(checker) = self.stack_limit_checker {\n            if checker.limit_exceeded() {\n                self.processor.recursion_limit_exceeded(self.element);\n                return true;\n            }\n        }\n\n        let mut any_descendant = false;\n\n        any_descendant |= self.invalidate_non_slotted_descendants(&invalidations.dom_descendants);\n        any_descendant |= self.invalidate_slotted_elements(&invalidations.slotted_descendants);\n        any_descendant |= self.invalidate_parts(&invalidations.parts);\n\n        any_descendant\n    }\n\n    /// Process the given sibling invalidations coming from our previous\n    /// sibling.\n    ///\n    /// The sibling invalidations are somewhat special because they can be\n    /// modified on the fly. New invalidations may be added and removed.\n    ///\n    /// In particular, all descendants get the same set of invalidations from\n    /// the parent, but the invalidations from a given sibling depend on the\n    /// ones we got from the previous one.\n    ///\n    /// Returns whether invalidated the current element's style.\n    fn process_sibling_invalidations(\n        &mut self,\n        descendant_invalidations: &mut DescendantInvalidationLists<'b>,\n        sibling_invalidations: &mut InvalidationVector<'b>,\n    ) -> bool {\n        let mut i = 0;\n        let mut new_sibling_invalidations = InvalidationVector::new();\n        let mut invalidated_self = false;\n\n        while i < sibling_invalidations.len() {\n            let result = self.process_invalidation(\n                &sibling_invalidations[i],\n                descendant_invalidations,\n                &mut new_sibling_invalidations,\n                InvalidationKind::Sibling,\n            );\n\n            invalidated_self |= result.invalidated_self;\n            sibling_invalidations[i].matched_by_any_previous |= result.matched;\n            if sibling_invalidations[i].effective_for_next() {\n                i += 1;\n            } else {\n                sibling_invalidations.remove(i);\n            }\n        }\n\n        sibling_invalidations.extend(new_sibling_invalidations.drain(..));\n        invalidated_self\n    }\n\n    /// Process a given invalidation list coming from our parent,\n    /// adding to `descendant_invalidations` and `sibling_invalidations` as\n    /// needed.\n    ///\n    /// Returns whether our style was invalidated as a result.\n    fn process_descendant_invalidations(\n        &mut self,\n        invalidations: &[Invalidation<'b>],\n        descendant_invalidations: &mut DescendantInvalidationLists<'b>,\n        sibling_invalidations: &mut InvalidationVector<'b>,\n        descendant_invalidation_kind: DescendantInvalidationKind,\n    ) -> bool {\n        let mut invalidated = false;\n\n        for invalidation in invalidations {\n            let result = self.process_invalidation(\n                invalidation,\n                descendant_invalidations,\n                sibling_invalidations,\n                InvalidationKind::Descendant(descendant_invalidation_kind),\n            );\n\n            invalidated |= result.invalidated_self;\n            if invalidation.effective_for_next() {\n                let mut invalidation = invalidation.clone();\n                invalidation.matched_by_any_previous |= result.matched;\n                debug_assert_eq!(\n                    descendant_invalidation_kind,\n                    DescendantInvalidationKind::Dom,\n                    \"Slotted or part invalidations don't propagate.\"\n                );\n                descendant_invalidations.dom_descendants.push(invalidation);\n            }\n        }\n\n        invalidated\n    }\n\n    #[inline(always)]\n    fn handle_fully_matched(\n        &mut self,\n        invalidation: &Invalidation<'b>,\n    ) -> (ProcessInvalidationResult, SmallVec<[Invalidation<'b>; 1]>) {\n        debug!(\" > Invalidation matched completely\");\n        // We matched completely. If we're an inner selector now we need\n        // to go outside our selector and carry on invalidating.\n        let mut to_process: SmallVec<[&Dependency; 1]> = SmallVec::from([invalidation.dependency]);\n        let mut next_invalidations: SmallVec<[Invalidation; 1]> = SmallVec::new();\n        let mut result = ProcessInvalidationResult {\n            invalidated_self: false,\n            matched: false,\n        };\n\n        while !to_process.is_empty() {\n            let mut next_dependencies: SmallVec<[&Dependency; 1]> = SmallVec::new();\n\n            while let Some(dependency) = to_process.pop() {\n                if let DependencyInvalidationKind::Scope(scope_kind) =\n                    dependency.invalidation_kind()\n                {\n                    if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {\n                        if let Some(ref deps) = dependency.next {\n                            for dep in deps.as_ref().slice() {\n                                let invalidation =\n                                    Invalidation::new_always_effective_for_next_descendant(\n                                        dep,\n                                        invalidation.host,\n                                        invalidation.scope,\n                                    );\n                                next_invalidations.push(invalidation);\n                            }\n                        }\n                        continue;\n                    }\n\n                    let force_add = any_next_has_scope_in_negation(dependency);\n                    if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {\n                        let invalidations = note_scope_dependency_force_at_subject(\n                            dependency,\n                            invalidation.host,\n                            invalidation.scope,\n                            force_add,\n                        );\n\n                        next_invalidations.extend(invalidations);\n\n                        continue;\n                    }\n                }\n\n                match dependency.next {\n                    None => {\n                        result.invalidated_self = true;\n                        result.matched = true;\n                    },\n                    Some(ref deps) => {\n                        for n in deps.as_ref().slice() {\n                            let invalidation_kind = n.invalidation_kind();\n                            match invalidation_kind {\n                                DependencyInvalidationKind::FullSelector => unreachable!(),\n                                DependencyInvalidationKind::Normal(_) => next_dependencies.push(n),\n                                //TODO(descalente, bug 1934061): Add specific handling for implicit scopes.\n                                DependencyInvalidationKind::Scope(_) => {\n                                    next_dependencies.push(n);\n                                },\n                                DependencyInvalidationKind::Relative(kind) => {\n                                    self.processor.found_relative_selector_invalidation(\n                                        self.element,\n                                        kind,\n                                        n,\n                                    );\n                                    result.matched = true;\n                                },\n                            }\n                        }\n                    },\n                };\n            }\n\n            for cur_dependency in next_dependencies.as_ref() {\n                let scope = matches!(\n                    invalidation.dependency.invalidation_kind(),\n                    DependencyInvalidationKind::Scope(_)\n                )\n                .then(|| self.element.opaque());\n                debug!(\" > Checking outer dependency {:?}\", cur_dependency);\n\n                // The inner selector changed, now check if the full\n                // previous part of the selector did, before keeping\n                // checking for descendants.\n                if !self\n                    .processor\n                    .check_outer_dependency(cur_dependency, self.element, scope)\n                {\n                    // Dependency is not relevant, do not note it down\n                    continue;\n                }\n\n                let invalidation_kind = cur_dependency.invalidation_kind();\n                if matches!(\n                    invalidation_kind,\n                    DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)\n                ) || (matches!(invalidation_kind, DependencyInvalidationKind::Scope(_))\n                    && cur_dependency\n                        .selector\n                        .is_rightmost(cur_dependency.selector_offset))\n                {\n                    // Add to dependency stack to process its next dependencies.\n                    to_process.push(cur_dependency);\n                    continue;\n                }\n\n                debug!(\" > Generating invalidation\");\n                next_invalidations.push(Invalidation::new(\n                    cur_dependency,\n                    invalidation.host,\n                    scope,\n                ));\n            }\n        }\n        return (result, next_invalidations);\n    }\n\n    /// Processes a given invalidation, potentially invalidating the style of\n    /// the current element.\n    ///\n    /// Returns whether invalidated the style of the element, and whether the\n    /// invalidation should be effective to subsequent siblings or descendants\n    /// down in the tree.\n    fn process_invalidation(\n        &mut self,\n        invalidation: &Invalidation<'b>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'b>,\n        sibling_invalidations: &mut InvalidationVector<'b>,\n        invalidation_kind: InvalidationKind,\n    ) -> ProcessInvalidationResult {\n        debug!(\n            \"TreeStyleInvalidator::process_invalidation({:?}, {:?}, {:?})\",\n            self.element, invalidation, invalidation_kind\n        );\n\n        let matching_result = {\n            let context = self.processor.matching_context();\n            context.current_host = invalidation.host;\n\n            context.nest_for_scope_condition(invalidation.scope, |ctx| {\n                matches_compound_selector_from(\n                    &invalidation.dependency.selector,\n                    invalidation.offset,\n                    ctx,\n                    &self.element,\n                )\n            })\n        };\n\n        let (mut result, next_invalidations) = match matching_result {\n            CompoundSelectorMatchingResult::NotMatched => {\n                return ProcessInvalidationResult {\n                    invalidated_self: false,\n                    matched: false,\n                }\n            },\n            CompoundSelectorMatchingResult::FullyMatched => self.handle_fully_matched(invalidation),\n            CompoundSelectorMatchingResult::Matched {\n                next_combinator_offset,\n            } => (\n                ProcessInvalidationResult {\n                    invalidated_self: false,\n                    matched: true,\n                },\n                smallvec![Invalidation {\n                    dependency: invalidation.dependency,\n                    host: invalidation.host,\n                    scope: invalidation.scope,\n                    offset: next_combinator_offset + 1,\n                    matched_by_any_previous: false,\n                    always_effective_for_next_descendant: invalidation\n                        .always_effective_for_next_descendant,\n                }],\n            ),\n        };\n\n        for next_invalidation in next_invalidations {\n            let next_invalidation_kind = if next_invalidation.always_effective_for_next_descendant {\n                InvalidationKind::Descendant(DescendantInvalidationKind::Dom)\n            } else {\n                debug_assert_ne!(\n                    next_invalidation.offset, 0,\n                    \"Rightmost selectors shouldn't generate more invalidations\",\n                );\n\n                let next_combinator = next_invalidation\n                    .dependency\n                    .selector\n                    .combinator_at_parse_order(next_invalidation.offset - 1);\n\n                if matches!(next_combinator, Combinator::PseudoElement)\n                    && self.processor.invalidates_on_pseudo_element()\n                {\n                    // We need to invalidate the element whenever pseudos change, for\n                    // two reasons:\n                    //\n                    //  * Eager pseudo styles are stored as part of the originating\n                    //    element's computed style.\n                    //\n                    //  * Lazy pseudo-styles might be cached on the originating\n                    //    element's pseudo-style cache.\n                    //\n                    // This could be more fine-grained (perhaps with a RESTYLE_PSEUDOS\n                    // hint?).\n                    //\n                    // Note that we'll also restyle the pseudo-element because it would\n                    // match this invalidation.\n                    result.invalidated_self = true;\n\n                    // For highlight pseudos (::selection, ::highlight, ::target-text),\n                    // we also need to trigger a repaint since their styles are resolved\n                    // lazily during painting.\n                    if next_invalidation\n                        .dependency\n                        .selector\n                        .pseudo_element()\n                        .is_some_and(|p| p.is_lazy_painted_highlight_pseudo())\n                    {\n                        self.processor.invalidated_highlight_pseudo(self.element);\n                    }\n                }\n\n                debug!(\n                    \" > Invalidation matched, next: {:?}, ({:?})\",\n                    next_invalidation, next_combinator\n                );\n\n                next_invalidation.kind()\n            };\n\n            // We can skip pushing under some circumstances, and we should\n            // because otherwise the invalidation list could grow\n            // exponentially.\n            //\n            //  * First of all, both invalidations need to be of the same\n            //    kind. This is because of how we propagate them going to\n            //    the right of the tree for sibling invalidations and going\n            //    down the tree for children invalidations. A sibling\n            //    invalidation that ends up generating a children\n            //    invalidation ends up (correctly) in five different lists,\n            //    not in the same list five different times.\n            //\n            //  * Then, the invalidation needs to be matched by a previous\n            //    ancestor/sibling, in order to know that this invalidation\n            //    has been generated already.\n            //\n            //  * Finally, the new invalidation needs to be\n            //    `effective_for_next()`, in order for us to know that it is\n            //    still in the list, since we remove the dependencies that\n            //    aren't from the lists for our children / siblings.\n            //\n            // To go through an example, let's imagine we are processing a\n            // dom subtree like:\n            //\n            //   <div><address><div><div/></div></address></div>\n            //\n            // And an invalidation list with a single invalidation like:\n            //\n            //   [div div div]\n            //\n            // When we process the invalidation list for the outer div, we\n            // match it, and generate a `div div` invalidation, so for the\n            // <address> child we have:\n            //\n            //   [div div div, div div]\n            //\n            // With the first of them marked as `matched`.\n            //\n            // When we process the <address> child, we don't match any of\n            // them, so both invalidations go untouched to our children.\n            //\n            // When we process the second <div>, we match _both_\n            // invalidations.\n            //\n            // However, when matching the first, we can tell it's been\n            // matched, and not push the corresponding `div div`\n            // invalidation, since we know it's necessarily already on the\n            // list.\n            //\n            // Thus, without skipping the push, we'll arrive to the\n            // innermost <div> with:\n            //\n            //   [div div div, div div, div div, div]\n            //\n            // While skipping it, we won't arrive here with duplicating\n            // dependencies:\n            //\n            //   [div div div, div div, div]\n            //\n            let can_skip_pushing = next_invalidation_kind == invalidation_kind\n                && invalidation.matched_by_any_previous\n                && next_invalidation.effective_for_next();\n\n            if can_skip_pushing {\n                debug!(\n                    \" > Can avoid push, since the invalidation had \\\n                    already been matched before\"\n                );\n            } else {\n                match next_invalidation_kind {\n                    InvalidationKind::Descendant(DescendantInvalidationKind::Dom) => {\n                        descendant_invalidations\n                            .dom_descendants\n                            .push(next_invalidation);\n                    },\n                    InvalidationKind::Descendant(DescendantInvalidationKind::Part) => {\n                        descendant_invalidations.parts.push(next_invalidation);\n                    },\n                    InvalidationKind::Descendant(DescendantInvalidationKind::Slotted) => {\n                        descendant_invalidations\n                            .slotted_descendants\n                            .push(next_invalidation);\n                    },\n                    InvalidationKind::Sibling => {\n                        sibling_invalidations.push(next_invalidation);\n                    },\n                }\n            }\n        }\n\n        result\n    }\n}\n\n/// Note the child dependencies of a scope end selector\n/// This is necessary because the scope end selector is not bound to :scope\n///\n/// e.g @scope to (.b) {:scope .a .c {...}}\n/// in the case of the following:\n/// <div class=a><div id=x class=b><div class=c></div></div></div>\n///\n/// If we toggle class \"b\" in x, we would have to go up to find .a\n/// if we wanted to invalidate correctly. However, this is costly.\n/// Instead we just invalidate to the subject of the selector .c\npub fn note_scope_dependency_force_at_subject<'selectors>(\n    dependency: &'selectors Dependency,\n    current_host: Option<OpaqueElement>,\n    scope: Option<OpaqueElement>,\n    traversed_non_subject: bool,\n) -> Vec<Invalidation<'selectors>> {\n    let mut invalidations: Vec<Invalidation> = Vec::new();\n    if let Some(next) = dependency.next.as_ref() {\n        for dep in next.slice() {\n            if dep.selector.is_rightmost(dep.selector_offset) && !traversed_non_subject {\n                continue;\n            }\n\n            // Follow the normal dependencies as far as we can, leaving\n            // other kinds to their own invalidation mechanisms elsewhere\n            if dep.next.is_some()\n                && matches!(\n                    dep.invalidation_kind(),\n                    DependencyInvalidationKind::Normal(_)\n                )\n            {\n                invalidations.extend(note_scope_dependency_force_at_subject(\n                    dep,\n                    current_host,\n                    scope,\n                    // Force add from now on because we\n                    // passed through a non-subject compound\n                    true,\n                ));\n            } else {\n                let invalidation = Invalidation::new_subject_invalidation(dep, current_host, scope);\n\n                invalidations.push(invalidation);\n            }\n        }\n    }\n    invalidations\n}\n"
  },
  {
    "path": "style/invalidation/element/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Invalidation of element styles due to attribute or style changes.\n\npub mod document_state;\npub mod element_wrapper;\npub mod invalidation_map;\npub mod invalidator;\npub mod relative_selector;\npub mod restyle_hints;\npub mod state_and_attributes;\n"
  },
  {
    "path": "style/invalidation/element/relative_selector.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Invalidation of element styles relative selectors.\n\nuse crate::data::ElementData;\nuse crate::dom::{TElement, TNode};\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::ServoElementSnapshotTable;\nuse crate::invalidation::element::element_wrapper::ElementWrapper;\nuse crate::invalidation::element::invalidation_map::{\n    AdditionalRelativeSelectorInvalidationMap, Dependency, DependencyInvalidationKind,\n    InvalidationMap, NormalDependencyInvalidationKind, RelativeDependencyInvalidationKind,\n    ScopeDependencyInvalidationKind, TSStateForInvalidation,\n};\nuse crate::invalidation::element::invalidator::{\n    note_scope_dependency_force_at_subject, DescendantInvalidationLists, Invalidation,\n    InvalidationProcessor, InvalidationResult, InvalidationVector, SiblingTraversalMap,\n    TreeStyleInvalidator,\n};\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::invalidation::element::state_and_attributes::{\n    check_dependency, dependency_may_be_relevant, invalidated_descendants, invalidated_self,\n    invalidated_sibling, push_invalidation, should_process_descendants,\n};\n#[cfg(feature = \"servo\")]\nuse crate::selector_parser::SnapshotMap as ServoElementSnapshotTable;\nuse crate::stylist::{CascadeData, Stylist};\nuse dom::ElementState;\nuse rustc_hash::FxHashMap;\nuse selectors::matching::{\n    early_reject_by_local_name, matches_selector, ElementSelectorFlags, MatchingContext,\n    MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, QuirksMode, SelectorCaches,\n    VisitedHandlingMode,\n};\nuse selectors::parser::SelectorKey;\nuse selectors::OpaqueElement;\nuse smallvec::{smallvec, SmallVec};\nuse std::ops::DerefMut;\n\n/// Kind of DOM mutation this relative selector invalidation is being carried out in.\n#[derive(Clone, Copy)]\npub enum DomMutationOperation {\n    /// Insertion operation, can cause side effect, but presumed already happened.\n    Insert,\n    /// Append operation, cannot cause side effect.\n    Append,\n    /// Removal operation, can cause side effect, but presumed already happened. Sibling relationships are destroyed.\n    Remove,\n    /// Invalidating for side effect of a DOM operation, for the previous sibling.\n    SideEffectPrevSibling,\n    /// Invalidating for side effect of a DOM operation, for the next sibling.\n    SideEffectNextSibling,\n}\n\nimpl DomMutationOperation {\n    fn accept<E: TElement>(&self, d: &Dependency, e: E) -> bool {\n        match self {\n            Self::Insert | Self::Append | Self::Remove => {\n                !e.relative_selector_search_direction().is_empty()\n            },\n            // `:has(+ .a + .b)` with `.anchor + .a + .remove + .b` - `.a` would be present\n            // in the search path.\n            Self::SideEffectPrevSibling => {\n                !e.relative_selector_search_direction().is_empty()\n                    && d.right_combinator_is_next_sibling()\n            },\n            // If an element is being removed and would cause next-sibling match to happen,\n            // e.g. `:has(+ .a)` with `.anchor + .remove + .a`, `.a` isn't yet searched\n            // for relative selector matching.\n            Self::SideEffectNextSibling => d.dependency_is_relative_with_single_next_sibling(),\n        }\n    }\n\n    fn is_side_effect(&self) -> bool {\n        match self {\n            Self::Insert | Self::Append | Self::Remove => false,\n            Self::SideEffectPrevSibling | Self::SideEffectNextSibling => true,\n        }\n    }\n}\n\n/// Context required to try and optimize away relative dependencies.\nstruct OptimizationContext<'a, E: TElement> {\n    sibling_traversal_map: &'a SiblingTraversalMap<E>,\n    quirks_mode: QuirksMode,\n    operation: DomMutationOperation,\n}\n\nimpl<'a, E: TElement> OptimizationContext<'a, E> {\n    fn can_be_ignored(\n        &self,\n        is_subtree: bool,\n        element: E,\n        host: Option<OpaqueElement>,\n        dependency: &Dependency,\n        leftmost_collapse_offset: usize,\n    ) -> bool {\n        if is_subtree {\n            // Subtree elements don't have unaffected sibling to look at.\n            return false;\n        }\n        debug_assert!(\n            matches!(\n                dependency.invalidation_kind(),\n                DependencyInvalidationKind::Relative(..)\n            ),\n            \"Non-relative selector being evaluated for optimization\"\n        );\n        // This optimization predecates on the fact that there may be a sibling that can readily\n        // \"take over\" this element.\n        let sibling = match self.sibling_traversal_map.prev_sibling_for(&element) {\n            None => {\n                if matches!(self.operation, DomMutationOperation::Append) {\n                    return false;\n                }\n                match self.sibling_traversal_map.next_sibling_for(&element) {\n                    Some(s) => s,\n                    None => return false,\n                }\n            },\n            Some(s) => s,\n        };\n        {\n            // Run through the affected compund.\n            let mut iter = dependency.selector.iter_from(dependency.selector_offset);\n            while let Some(c) = iter.next() {\n                if c.has_indexed_selector_in_subject() {\n                    // We do not calculate indices during invalidation as they're wasteful - as a side effect,\n                    // such selectors always return true, breaking this optimization. Note that we only check\n                    // this compound only because the check to skip compares against this element's sibling.\n                    // i.e. Given `:has(:nth-child(2) .foo)`, we'd try to find `.foo`'s sibling, which\n                    // shares `:nth-child` up the selector.\n                    return false;\n                }\n            }\n        }\n        let dependency_is_rightmost = dependency.selector_offset == 0;\n        if !dependency_is_rightmost {\n            let combinator = dependency\n                .selector\n                .combinator_at_match_order(dependency.selector_offset - 1);\n            if combinator.is_ancestor() {\n                // We can safely ignore these, since we're about to traverse the\n                // rest of the affected tree anyway to find the rightmost invalidated element.\n                return true;\n            }\n            if combinator.is_sibling() && matches!(self.operation, DomMutationOperation::Append) {\n                // If we're at the top of the DOM tree being mutated, we can ignore it if the\n                // operation is append - we know we'll cover all the later siblings and their descendants.\n                return true;\n            }\n        }\n\n        // We have a situation like `:has(.item .item + .item + .item)`, where the first element in the sibling\n        // chain position (i.e. The element matched by the second `.item` from the left) mutates. By the time we\n        // get here, we've collapsed the 4 dependencies for each of `.item` position into one at the rightmost\n        // position. Before we look for a standin, we need to find which `.item` this element matches - Doing\n        // that would generate more work than it saves.\n        if dependency_is_rightmost\n            && leftmost_collapse_offset != dependency.selector_offset\n            && self\n                .sibling_traversal_map\n                .next_sibling_for(&element)\n                .is_some()\n        {\n            return false;\n        }\n\n        let mut caches = SelectorCaches::default();\n        let mut matching_context = MatchingContext::new(\n            MatchingMode::Normal,\n            None,\n            &mut caches,\n            self.quirks_mode,\n            NeedsSelectorFlags::No,\n            MatchingForInvalidation::Yes,\n        );\n        matching_context.current_host = host;\n        let sibling_matches = matches_selector(\n            &dependency.selector,\n            dependency.selector_offset,\n            None,\n            &sibling,\n            &mut matching_context,\n        );\n        if sibling_matches {\n            // Remember that at this point, we know that the combinator to the right of this\n            // compound is a sibling combinator. Effectively, we've found a standin for the\n            // element we're mutating.\n            // e.g. Given `:has(... .a ~ .b ...)`, we're the mutating element matching `... .a`,\n            // if we find a sibling that matches the `... .a`, it can stand in for us.\n            debug_assert!(\n                dependency.next.is_some(),\n                \"No relative selector outer dependency?\"\n            );\n            return dependency.next.as_ref().map_or(false, |deps| {\n                // ... However, if the standin sibling can be the anchor, we can't skip it, since\n                // that sibling should be invlidated to become the anchor.\n                let next = &deps.as_ref().slice()[0];\n                !matches_selector(\n                    &next.selector,\n                    next.selector_offset,\n                    None,\n                    &sibling,\n                    &mut matching_context,\n                )\n            });\n        }\n        // Ok, there's no standin element - but would this element have matched the upstream\n        // selector anyway? If we don't, either the match exists somewhere far from us\n        // (In which case our mutation doesn't really matter), or it doesn't exist at all,\n        // so we can just skip the invalidation.\n        let (combinator, prev_offset) = {\n            let mut iter = dependency.selector.iter_from(dependency.selector_offset);\n            let mut o = dependency.selector_offset;\n            while iter.next().is_some() {\n                o += 1;\n            }\n            let combinator = iter.next_sequence();\n            o += 1;\n            debug_assert!(\n                combinator.is_some(),\n                \"Should at least see a relative combinator\"\n            );\n            (combinator.unwrap(), o)\n        };\n        if combinator.is_sibling() && prev_offset >= dependency.selector.len() - 1 {\n            // Hit the relative combinator - we don't have enough information to\n            // see if there's going to be a downstream match.\n            return false;\n        }\n        !matches_selector(\n            &dependency.selector,\n            dependency.selector_offset,\n            None,\n            &element,\n            &mut matching_context,\n        )\n    }\n}\n\n/// Overall invalidator for handling relative selector invalidations.\npub struct RelativeSelectorInvalidator<'a, 'b, E>\nwhere\n    E: TElement + 'a,\n{\n    /// Element triggering the invalidation.\n    pub element: E,\n    /// Quirks mode of the current invalidation.\n    pub quirks_mode: QuirksMode,\n    /// Snapshot containing changes to invalidate against.\n    /// Can be None if it's a DOM mutation.\n    pub snapshot_table: Option<&'b ServoElementSnapshotTable>,\n    /// Callback to trigger when the subject element is invalidated.\n    pub invalidated: fn(E, &InvalidationResult),\n    /// The traversal map that should be used to process invalidations.\n    pub sibling_traversal_map: SiblingTraversalMap<E>,\n    /// Marker for 'a lifetime.\n    pub _marker: ::std::marker::PhantomData<&'a ()>,\n}\n\nstruct RelativeSelectorInvalidation<'a> {\n    host: Option<OpaqueElement>,\n    kind: RelativeDependencyInvalidationKind,\n    dependency: &'a Dependency,\n}\n\ntype ElementDependencies<'a> = SmallVec<[(Option<OpaqueElement>, &'a Dependency); 1]>;\ntype Dependencies<'a, E> = SmallVec<[(E, ElementDependencies<'a>); 1]>;\ntype AlreadyInvalidated<'a, E> = SmallVec<[AlreadyInvalidatedEntry<'a, E>; 2]>;\n\nstruct AlreadyInvalidatedEntry<'a, E>\nwhere\n    E: TElement + 'a,\n{\n    /// Element where the invalidation will begin.\n    element: E,\n    /// The current shadow host.\n    host: Option<OpaqueElement>,\n    /// Dependency chain for this invalidation.\n    dependency: &'a Dependency,\n    /// The offset, of the leftmost dependencies that this\n    /// invalidation collapsed. See the `update()` function\n    /// for more information.\n    leftmost_collapse_offset: usize,\n}\n\nimpl<'a, E> AlreadyInvalidatedEntry<'a, E>\nwhere\n    E: TElement + 'a,\n{\n    fn new(element: E, host: Option<OpaqueElement>, dependency: &'a Dependency) -> Self {\n        Self {\n            element,\n            host,\n            dependency,\n            leftmost_collapse_offset: dependency.selector_offset,\n        }\n    }\n\n    /// Update this invalidation with a new invalidation that may collapse with it.\n    fn update(&mut self, element: E, host: Option<OpaqueElement>, dependency: &'a Dependency) {\n        // This dependency should invalidate the same way - Collapse the invalidation\n        // to a more general case so we don't do duplicate work.\n        // e.g. For `:has(.item .item + .item + .item)`, since the anchor would be located\n        // in the ancestor chain for any invalidation triggered by any `.item` compound,\n        // 4 entries can collapse into one - but keep track of the leftmost offset.\n        if self.dependency.selector_offset > dependency.selector_offset {\n            *self = Self {\n                element,\n                host,\n                dependency,\n                leftmost_collapse_offset: self.leftmost_collapse_offset,\n            };\n        } else if self.leftmost_collapse_offset < dependency.selector_offset {\n            self.leftmost_collapse_offset = dependency.selector_offset;\n        }\n    }\n}\n\n/// Interface for collecting relative selector dependencies.\npub struct RelativeSelectorDependencyCollector<'a, E>\nwhere\n    E: TElement,\n{\n    /// Dependencies that need to run through the normal invalidation that may generate\n    /// a relative selector invalidation.\n    dependencies: FxHashMap<E, ElementDependencies<'a>>,\n    /// Dependencies that created an invalidation right away.\n    invalidations: AlreadyInvalidated<'a, E>,\n    /// The top element in the subtree being invalidated.\n    top: E,\n    /// Optional context that will be used to try and skip invalidations\n    /// by running selector matches.\n    optimization_context: Option<OptimizationContext<'a, E>>,\n}\n\ntype Invalidations<'a> = SmallVec<[RelativeSelectorInvalidation<'a>; 1]>;\ntype InnerInvalidations<'a, E> = SmallVec<[(E, RelativeSelectorInvalidation<'a>); 1]>;\n\nstruct ToInvalidate<'a, E: TElement + 'a> {\n    /// Dependencies to run through normal invalidator.\n    dependencies: Dependencies<'a, E>,\n    /// Dependencies already invalidated.\n    invalidations: Invalidations<'a>,\n}\n\nimpl<'a, E: TElement + 'a> Default for ToInvalidate<'a, E> {\n    fn default() -> Self {\n        Self {\n            dependencies: Dependencies::default(),\n            invalidations: Invalidations::default(),\n        }\n    }\n}\n\nfn invalidation_can_collapse(\n    a: &Dependency,\n    b: &Dependency,\n    allow_indexed_selectors: bool,\n) -> bool {\n    // We want to detect identical dependencies that occur at different\n    // compounds but has the identical compound in the same selector,\n    // e.g. :has(.item .item).\n\n    // If they trigger different invalidations, they shouldn't be collapsed.\n    if a.relative_invalidation_kind() != b.relative_invalidation_kind() {\n        return false;\n    }\n\n    // Not in the same selector, trivially skipped.\n    if SelectorKey::new(&a.selector) != SelectorKey::new(&b.selector) {\n        return false;\n    }\n\n    // Check that this is in the same nesting.\n    // TODO(dshin): @scope probably brings more subtleties...\n    let mut a_next = a.next.as_ref();\n    let mut b_next = b.next.as_ref();\n    while let (Some(a_deps), Some(b_deps)) = (a_next, b_next) {\n        // This is a bit subtle - but we don't need to do the checks we do at higher levels.\n        // Cases like `:is(.item .foo) :is(.item .foo)` where `.item` invalidates would\n        // point to different dependencies, pointing to the same outer selector, but\n        // differing in selector offset.\n        let a_nexts = a_deps.as_ref().slice();\n        let b_nexts = b_deps.as_ref().slice();\n        if a_nexts.is_empty() || b_nexts.is_empty() {\n            // Can happen when we get out to empty @scope() rules.\n            // If they're both empty, we can just do nothing for both.\n            return a_nexts.is_empty() == b_nexts.is_empty();\n        }\n        let a_n = &a_nexts[0];\n        let b_n = &b_nexts[0];\n        if SelectorKey::new(&a_n.selector) != SelectorKey::new(&b_n.selector) {\n            return false;\n        }\n        a_next = a_n.next.as_ref();\n        b_next = b_n.next.as_ref();\n    }\n    if a_next.is_some() || b_next.is_some() {\n        return false;\n    }\n\n    // Ok, now, do the compounds actually match?\n    // This can get expensive quickly, but we're assuming that:\n    //\n    //   * In most cases, authors don't generally duplicate compounds in a selector, so\n    //     this fails quickly\n    //   * In cases where compounds are duplicated, reducing the number of invalidations\n    //     has a payoff that offsets the comparison cost\n    //\n    // Note, `.a.b` != `.b.a` - doesn't affect correctness, though.\n    // TODO(dshin): Caching this may be worth it as well?\n    let mut a_iter = a.selector.iter_from(a.selector_offset);\n    let mut b_iter = b.selector.iter_from(b.selector_offset);\n    loop {\n        let a_component = a_iter.next();\n        let b_component = b_iter.next();\n\n        if a_component != b_component {\n            return false;\n        }\n        let Some(component) = a_component else {\n            return true;\n        };\n        if !allow_indexed_selectors && component.has_indexed_selector_in_subject() {\n            // The element's positioning matters, so can't collapse.\n            return false;\n        }\n    }\n}\n\nimpl<'a, E> RelativeSelectorDependencyCollector<'a, E>\nwhere\n    E: TElement,\n{\n    fn new(top: E, optimization_context: Option<OptimizationContext<'a, E>>) -> Self {\n        Self {\n            dependencies: FxHashMap::default(),\n            invalidations: AlreadyInvalidated::default(),\n            top,\n            optimization_context,\n        }\n    }\n\n    fn insert_invalidation(\n        &mut self,\n        element: E,\n        dependency: &'a Dependency,\n        host: Option<OpaqueElement>,\n    ) {\n        let in_subtree = element != self.top;\n        if let Some(entry) = self.invalidations.iter_mut().find(|entry| {\n            // If we're in the subtree of DOM manipulation - worrying the about positioning of this element\n            // is irrelevant, because the DOM structure is either completely new or about to go away.\n            let both_in_subtree = in_subtree && entry.element != self.top;\n            // If we're considering the same element for invalidation, their evaluation of the indexed selector\n            // is identical by definition.\n            let same_element = element == entry.element;\n            invalidation_can_collapse(\n                dependency,\n                entry.dependency,\n                both_in_subtree || same_element,\n            )\n        }) {\n            entry.update(element, host, dependency)\n        } else {\n            self.invalidations\n                .push(AlreadyInvalidatedEntry::new(element, host, dependency));\n        }\n    }\n\n    /// Add this dependency, if it is unique (i.e. Different outer dependency or same outer dependency\n    /// but requires a different invalidation traversal).\n    pub fn add_dependency(\n        &mut self,\n        dependency: &'a Dependency,\n        element: E,\n        host: Option<OpaqueElement>,\n    ) {\n        match dependency.invalidation_kind() {\n            DependencyInvalidationKind::FullSelector => unreachable!(),\n            DependencyInvalidationKind::Normal(..) | DependencyInvalidationKind::Scope(..) => {\n                self.dependencies\n                    .entry(element)\n                    .and_modify(|v| v.push((host, dependency)))\n                    .or_default()\n                    .push((host, dependency));\n            },\n            DependencyInvalidationKind::Relative(kind) => {\n                debug_assert!(\n                    dependency.next.is_some(),\n                    \"Orphaned inner relative selector?\"\n                );\n                if element != self.top\n                    && matches!(\n                        kind,\n                        RelativeDependencyInvalidationKind::Parent\n                            | RelativeDependencyInvalidationKind::PrevSibling\n                            | RelativeDependencyInvalidationKind::EarlierSibling\n                    )\n                {\n                    return;\n                }\n                if early_reject_by_local_name(\n                    &dependency.selector,\n                    dependency.selector_offset,\n                    &element,\n                ) {\n                    return;\n                }\n                self.insert_invalidation(element, dependency, host);\n            },\n        };\n    }\n\n    /// Get the dependencies in a list format.\n    fn get(self) -> ToInvalidate<'a, E> {\n        let mut result = ToInvalidate::default();\n        for invalidation in self.invalidations {\n            match invalidation.dependency.invalidation_kind() {\n                DependencyInvalidationKind::FullSelector => unreachable!(),\n                DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_) => {\n                    unreachable!(\"Inner selector in invalidation?\")\n                },\n                DependencyInvalidationKind::Relative(kind) => {\n                    if let Some(context) = self.optimization_context.as_ref() {\n                        if context.can_be_ignored(\n                            invalidation.element != self.top,\n                            invalidation.element,\n                            invalidation.host,\n                            invalidation.dependency,\n                            invalidation.leftmost_collapse_offset,\n                        ) {\n                            continue;\n                        }\n                    }\n                    let dependency = &invalidation.dependency.next.as_ref().unwrap().slice()[0];\n                    result.invalidations.push(RelativeSelectorInvalidation {\n                        kind,\n                        host: invalidation.host,\n                        dependency,\n                    });\n                    // We move the invalidation up to the top of the subtree to avoid unnecessary traveral, but\n                    // this means that we need to take ancestor-earlier sibling invalidations into account, as\n                    // they'd look into earlier siblings of the top of the subtree as well.\n                    if invalidation.element != self.top\n                        && matches!(\n                            kind,\n                            RelativeDependencyInvalidationKind::AncestorEarlierSibling\n                                | RelativeDependencyInvalidationKind::AncestorPrevSibling\n                        )\n                    {\n                        result.invalidations.push(RelativeSelectorInvalidation {\n                            kind: if matches!(\n                                kind,\n                                RelativeDependencyInvalidationKind::AncestorPrevSibling\n                            ) {\n                                RelativeDependencyInvalidationKind::PrevSibling\n                            } else {\n                                RelativeDependencyInvalidationKind::EarlierSibling\n                            },\n                            host: invalidation.host,\n                            dependency,\n                        });\n                    }\n                },\n            };\n        }\n        for (key, element_dependencies) in self.dependencies {\n            // At least for now, we don't try to optimize away dependencies emitted from nested selectors.\n            result.dependencies.push((key, element_dependencies));\n        }\n        result\n    }\n\n    fn collect_all_dependencies_for_element(\n        &mut self,\n        element: E,\n        scope: Option<OpaqueElement>,\n        quirks_mode: QuirksMode,\n        map: &'a InvalidationMap,\n        additional_relative_selector_invalidation_map: &'a AdditionalRelativeSelectorInvalidationMap,\n        operation: DomMutationOperation,\n    ) {\n        element\n            .id()\n            .map(|v| match map.id_to_selector.get(v, quirks_mode) {\n                Some(v) => {\n                    for dependency in v {\n                        if !operation.accept(dependency, element) {\n                            continue;\n                        }\n                        self.add_dependency(dependency, element, scope);\n                    }\n                },\n                None => (),\n            });\n        element.each_class(|v| match map.class_to_selector.get(v, quirks_mode) {\n            Some(v) => {\n                for dependency in v {\n                    if !operation.accept(dependency, element) {\n                        continue;\n                    }\n                    self.add_dependency(dependency, element, scope);\n                }\n            },\n            None => (),\n        });\n        element.each_custom_state(|v| match map.custom_state_affecting_selectors.get(v) {\n            Some(v) => {\n                for dependency in v {\n                    if !operation.accept(dependency, element) {\n                        continue;\n                    }\n                    self.add_dependency(dependency, element, scope);\n                }\n            },\n            None => (),\n        });\n        element.each_attr_name(|v| match map.other_attribute_affecting_selectors.get(v) {\n            Some(v) => {\n                for dependency in v {\n                    if !operation.accept(dependency, element) {\n                        continue;\n                    }\n                    self.add_dependency(dependency, element, scope);\n                }\n            },\n            None => (),\n        });\n        let state = element.state();\n        map.state_affecting_selectors.lookup_with_additional(\n            element,\n            quirks_mode,\n            None,\n            &[],\n            ElementState::empty(),\n            |dependency| {\n                if !dependency.state.intersects(state) {\n                    return true;\n                }\n                if !operation.accept(&dependency.dep, element) {\n                    return true;\n                }\n                self.add_dependency(&dependency.dep, element, scope);\n                true\n            },\n        );\n\n        additional_relative_selector_invalidation_map\n            .ts_state_to_selector\n            .lookup_with_additional(\n                element,\n                quirks_mode,\n                None,\n                &[],\n                ElementState::empty(),\n                |dependency| {\n                    if !operation.accept(&dependency.dep, element) {\n                        return true;\n                    }\n                    // This section contain potential optimization for not running full invalidation -\n                    // consult documentation in `TSStateForInvalidation`.\n                    if dependency.state.may_be_optimized() {\n                        if operation.is_side_effect() {\n                            // Side effect operations act on element not being mutated, so they can't\n                            // change the match outcome of these optimizable pseudoclasses.\n                            return true;\n                        }\n                        debug_assert!(\n                            self.optimization_context.is_some(),\n                            \"Optimization context not available for DOM mutation?\"\n                        );\n                        if dependency.state.contains(TSStateForInvalidation::EMPTY)\n                            && element.first_element_child().is_some()\n                        {\n                            return true;\n                        }\n\n                        let sibling_traversal_map = self\n                            .optimization_context\n                            .as_ref()\n                            .unwrap()\n                            .sibling_traversal_map;\n                        if dependency\n                            .state\n                            .contains(TSStateForInvalidation::NTH_EDGE_FIRST)\n                            && sibling_traversal_map.prev_sibling_for(&element).is_some()\n                        {\n                            return true;\n                        }\n\n                        if dependency\n                            .state\n                            .contains(TSStateForInvalidation::NTH_EDGE_LAST)\n                            && sibling_traversal_map.next_sibling_for(&element).is_some()\n                        {\n                            return true;\n                        }\n                    }\n                    self.add_dependency(&dependency.dep, element, scope);\n                    true\n                },\n            );\n\n        if let Some(v) = additional_relative_selector_invalidation_map\n            .type_to_selector\n            .get(element.local_name())\n        {\n            for dependency in v {\n                if !operation.accept(dependency, element) {\n                    continue;\n                }\n                self.add_dependency(dependency, element, scope);\n            }\n        }\n\n        for dependency in &additional_relative_selector_invalidation_map.any_to_selector {\n            if !operation.accept(dependency, element) {\n                continue;\n            }\n            self.add_dependency(dependency, element, scope);\n        }\n    }\n\n    fn is_empty(&self) -> bool {\n        self.invalidations.is_empty() && self.dependencies.is_empty()\n    }\n}\n\nimpl<'a, 'b, E> RelativeSelectorInvalidator<'a, 'b, E>\nwhere\n    E: TElement + 'a,\n{\n    /// Gather relative selector dependencies for the given element, and invalidate as necessary.\n    #[inline(never)]\n    pub fn invalidate_relative_selectors_for_this<F>(\n        self,\n        stylist: &'a Stylist,\n        mut gather_dependencies: F,\n    ) where\n        F: FnMut(\n            &E,\n            Option<OpaqueElement>,\n            &'a CascadeData,\n            QuirksMode,\n            &mut RelativeSelectorDependencyCollector<'a, E>,\n        ),\n    {\n        let mut collector = RelativeSelectorDependencyCollector::new(self.element, None);\n        stylist.for_each_cascade_data_with_scope(self.element, |data, scope| {\n            let map = data.relative_invalidation_map_attributes();\n            if !map.used {\n                return;\n            }\n            gather_dependencies(\n                &self.element,\n                scope.map(|e| e.opaque()),\n                data,\n                self.quirks_mode,\n                &mut collector,\n            );\n        });\n        if collector.is_empty() {\n            return;\n        }\n        self.invalidate_from_dependencies(collector.get());\n    }\n\n    /// Gather relative selector dependencies for the given element (And its subtree) that mutated, and invalidate as necessary.\n    #[inline(never)]\n    pub fn invalidate_relative_selectors_for_dom_mutation(\n        self,\n        subtree: bool,\n        stylist: &'a Stylist,\n        inherited_search_path: ElementSelectorFlags,\n        operation: DomMutationOperation,\n    ) {\n        let mut collector = RelativeSelectorDependencyCollector::new(\n            self.element,\n            if operation.is_side_effect() {\n                None\n            } else {\n                Some(OptimizationContext {\n                    sibling_traversal_map: &self.sibling_traversal_map,\n                    quirks_mode: self.quirks_mode,\n                    operation,\n                })\n            },\n        );\n        let mut traverse_subtree = false;\n        self.element.apply_selector_flags(inherited_search_path);\n        stylist.for_each_cascade_data_with_scope(self.element, |data, scope| {\n            let map_attributes = data.relative_invalidation_map_attributes();\n            if !map_attributes.used {\n                return;\n            }\n            let map = data.relative_selector_invalidation_map();\n            traverse_subtree |= map_attributes.needs_ancestors_traversal;\n            collector.collect_all_dependencies_for_element(\n                self.element,\n                scope.map(|e| e.opaque()),\n                self.quirks_mode,\n                map,\n                map_attributes,\n                operation,\n            );\n        });\n\n        if subtree && traverse_subtree {\n            for node in self.element.as_node().dom_descendants() {\n                let descendant = match node.as_element() {\n                    Some(e) => e,\n                    None => continue,\n                };\n                descendant.apply_selector_flags(inherited_search_path);\n                stylist.for_each_cascade_data_with_scope(descendant, |data, scope| {\n                    let map_attributes = data.relative_invalidation_map_attributes();\n                    if !map_attributes.used {\n                        return;\n                    }\n                    let map = data.relative_selector_invalidation_map();\n                    collector.collect_all_dependencies_for_element(\n                        descendant,\n                        scope.map(|e| e.opaque()),\n                        self.quirks_mode,\n                        map,\n                        map_attributes,\n                        operation,\n                    );\n                });\n            }\n        }\n        if collector.is_empty() {\n            return;\n        }\n        self.invalidate_from_dependencies(collector.get());\n    }\n\n    /// Carry out complete invalidation triggered by a relative selector invalidation.\n    fn invalidate_from_dependencies(&self, to_invalidate: ToInvalidate<'a, E>) {\n        for (element, dependencies) in to_invalidate.dependencies {\n            let mut selector_caches = SelectorCaches::default();\n            let mut processor = RelativeSelectorInnerInvalidationProcessor::new(\n                self.quirks_mode,\n                self.snapshot_table,\n                &dependencies,\n                &mut selector_caches,\n                &self.sibling_traversal_map,\n            );\n            TreeStyleInvalidator::new(element, None, &mut processor).invalidate();\n            for (element, invalidation) in processor.take_invalidations() {\n                self.invalidate_upwards(element, &invalidation);\n            }\n        }\n        for invalidation in to_invalidate.invalidations {\n            self.invalidate_upwards(self.element, &invalidation);\n        }\n    }\n\n    fn invalidate_upwards(&self, element: E, invalidation: &RelativeSelectorInvalidation<'a>) {\n        // This contains the main reason for why relative selector invalidation is handled\n        // separately - It travels ancestor and/or earlier sibling direction.\n        match invalidation.kind {\n            RelativeDependencyInvalidationKind::Parent => {\n                element.parent_element().map(|e| {\n                    if !Self::in_search_direction(\n                        &e,\n                        ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n                    ) {\n                        return;\n                    }\n                    self.handle_anchor(e, invalidation.dependency, invalidation.host);\n                });\n            },\n            RelativeDependencyInvalidationKind::Ancestors => {\n                let mut parent = element.parent_element();\n                while let Some(par) = parent {\n                    if !Self::in_search_direction(\n                        &par,\n                        ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n                    ) {\n                        return;\n                    }\n                    self.handle_anchor(par, invalidation.dependency, invalidation.host);\n                    parent = par.parent_element();\n                }\n            },\n            RelativeDependencyInvalidationKind::PrevSibling => {\n                self.sibling_traversal_map\n                    .prev_sibling_for(&element)\n                    .map(|e| {\n                        if !Self::in_search_direction(\n                            &e,\n                            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,\n                        ) {\n                            return;\n                        }\n                        self.handle_anchor(e, invalidation.dependency, invalidation.host);\n                    });\n            },\n            RelativeDependencyInvalidationKind::AncestorPrevSibling => {\n                let mut parent = element.parent_element();\n                while let Some(par) = parent {\n                    if !Self::in_search_direction(\n                        &par,\n                        ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n                    ) {\n                        return;\n                    }\n                    par.prev_sibling_element().map(|e| {\n                        if !Self::in_search_direction(\n                            &e,\n                            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,\n                        ) {\n                            return;\n                        }\n                        self.handle_anchor(e, invalidation.dependency, invalidation.host);\n                    });\n                    parent = par.parent_element();\n                }\n            },\n            RelativeDependencyInvalidationKind::EarlierSibling => {\n                let mut sibling = self.sibling_traversal_map.prev_sibling_for(&element);\n                while let Some(sib) = sibling {\n                    if !Self::in_search_direction(\n                        &sib,\n                        ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,\n                    ) {\n                        return;\n                    }\n                    self.handle_anchor(sib, invalidation.dependency, invalidation.host);\n                    sibling = sib.prev_sibling_element();\n                }\n            },\n            RelativeDependencyInvalidationKind::AncestorEarlierSibling => {\n                let mut parent = element.parent_element();\n                while let Some(par) = parent {\n                    if !Self::in_search_direction(\n                        &par,\n                        ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,\n                    ) {\n                        return;\n                    }\n                    let mut sibling = par.prev_sibling_element();\n                    while let Some(sib) = sibling {\n                        if !Self::in_search_direction(\n                            &sib,\n                            ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING,\n                        ) {\n                            return;\n                        }\n                        self.handle_anchor(sib, invalidation.dependency, invalidation.host);\n                        sibling = sib.prev_sibling_element();\n                    }\n                    parent = par.parent_element();\n                }\n            },\n        }\n    }\n\n    /// Is this element in the direction of the given relative selector search path?\n    fn in_search_direction(element: &E, desired: ElementSelectorFlags) -> bool {\n        element\n            .relative_selector_search_direction()\n            .intersects(desired)\n    }\n\n    /// Handle a potential relative selector anchor.\n    fn handle_anchor(\n        &self,\n        element: E,\n        outer_dependency: &Dependency,\n        host: Option<OpaqueElement>,\n    ) {\n        let is_rightmost = Self::is_subject(outer_dependency);\n        if (is_rightmost\n            && !element.has_selector_flags(ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR))\n            || (!is_rightmost\n                && !element.has_selector_flags(\n                    ElementSelectorFlags::ANCHORS_RELATIVE_SELECTOR_NON_SUBJECT,\n                ))\n        {\n            // If it was never a relative selector anchor, don't bother.\n            return;\n        }\n        let mut selector_caches = SelectorCaches::default();\n        let matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(\n            MatchingMode::Normal,\n            None,\n            &mut selector_caches,\n            VisitedHandlingMode::AllLinksVisitedAndUnvisited,\n            self.quirks_mode,\n            NeedsSelectorFlags::No,\n            MatchingForInvalidation::Yes,\n        );\n        let mut data = match element.mutate_data() {\n            Some(data) => data,\n            None => return,\n        };\n        let mut processor = RelativeSelectorOuterInvalidationProcessor {\n            element,\n            host,\n            data: data.deref_mut(),\n            dependency: &*outer_dependency,\n            matching_context,\n            traversal_map: &self.sibling_traversal_map,\n        };\n        let result = TreeStyleInvalidator::new(element, None, &mut processor).invalidate();\n        (self.invalidated)(element, &result);\n    }\n\n    /// Does this relative selector dependency have its relative selector in the subject position?\n    fn is_subject(outer_dependency: &Dependency) -> bool {\n        debug_assert!(\n            matches!(\n                outer_dependency.invalidation_kind(),\n                DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_)\n            ),\n            \"Outer selector of relative selector is relative?\"\n        );\n\n        if let Some(x) = outer_dependency.next.as_ref() {\n            // We only care to ensure that we're the subject in the outermost selector of the current\n            // selector - Crossing over a scope invalidation would mean moving into a selector inside\n            // the current scope block\n            if matches!(\n                outer_dependency.invalidation_kind(),\n                DependencyInvalidationKind::Normal(..)\n            ) {\n                if !Self::is_subject(&x.as_ref().slice()[0]) {\n                    // Not subject in outer selector.\n                    return false;\n                }\n            }\n        }\n        outer_dependency\n            .selector\n            .is_rightmost(outer_dependency.selector_offset)\n    }\n}\n\n/// Blindly invalidate everything outside of a relative selector.\n/// Consider `:is(.a :has(.b) .c ~ .d) ~ .e .f`, where .b gets deleted.\n/// Since the tree mutated, we cannot rely on snapshots.\npub struct RelativeSelectorOuterInvalidationProcessor<'a, 'b, E: TElement> {\n    /// Element being invalidated.\n    pub element: E,\n    /// The current shadow host, if any.\n    pub host: Option<OpaqueElement>,\n    /// Data for the element being invalidated.\n    pub data: &'a mut ElementData,\n    /// Dependency to be processed.\n    pub dependency: &'b Dependency,\n    /// Matching context to use for invalidation.\n    pub matching_context: MatchingContext<'a, E::Impl>,\n    /// Traversal map for this invalidation.\n    pub traversal_map: &'a SiblingTraversalMap<E>,\n}\n\nimpl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'b, 'a, E>\n    for RelativeSelectorOuterInvalidationProcessor<'a, 'b, E>\nwhere\n    E: TElement,\n{\n    fn invalidates_on_pseudo_element(&self) -> bool {\n        true\n    }\n\n    fn check_outer_dependency(\n        &mut self,\n        _dependency: &Dependency,\n        _element: E,\n        _: Option<OpaqueElement>,\n    ) -> bool {\n        // At this point, we know a relative selector invalidated, and are ignoring them.\n        true\n    }\n\n    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {\n        &mut self.matching_context\n    }\n\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {\n        self.traversal_map\n    }\n\n    fn collect_invalidations(\n        &mut self,\n        element: E,\n        _self_invalidations: &mut InvalidationVector<'b>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'b>,\n        sibling_invalidations: &mut InvalidationVector<'b>,\n    ) -> bool {\n        debug_assert_eq!(element, self.element);\n        debug_assert!(\n            self.matching_context.matching_for_invalidation(),\n            \"Not matching for invalidation?\"\n        );\n\n        // Ok, this element can potentially an anchor to the given dependency.\n        // Before we do the potentially-costly ancestor/earlier sibling traversal,\n        // See if it can actuall be an anchor by trying to match the \"rest\" of the selector\n        // outside and to the left of `:has` in question.\n        // e.g. Element under consideration can only be the anchor to `:has` in\n        // `.foo .bar ~ .baz:has()`, iff it matches `.foo .bar ~ .baz`.\n        let invalidated_self = {\n            let mut invalidated = false;\n            let mut dependencies_to_invalidate: SmallVec<[&Dependency; 1]> =\n                smallvec![self.dependency];\n            while let Some(d) = dependencies_to_invalidate.pop() {\n                debug_assert!(\n                    matches!(\n                        d.invalidation_kind(),\n                        DependencyInvalidationKind::Normal(_)\n                            | DependencyInvalidationKind::Scope(_)\n                    ),\n                    \"Unexpected dependency kind\"\n                );\n                if !dependency_may_be_relevant(d, &element, false) {\n                    continue;\n                }\n                if !matches_selector(\n                    &d.selector,\n                    d.selector_offset,\n                    None,\n                    &element,\n                    self.matching_context(),\n                ) {\n                    continue;\n                }\n\n                let invalidation_kind = d.invalidation_kind();\n\n                if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {\n                    if d.selector.is_rightmost(d.selector_offset) {\n                        if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd {\n                            let invalidations = note_scope_dependency_force_at_subject(\n                                d,\n                                self.matching_context.current_host.clone(),\n                                self.matching_context.scope_element,\n                                false,\n                            );\n                            descendant_invalidations\n                                .dom_descendants\n                                .extend(invalidations);\n\n                            invalidated |= true;\n                        } else if let Some(ref next) = d.next {\n                            dependencies_to_invalidate.extend(next.as_ref().slice());\n                        }\n                        continue;\n                    }\n                }\n\n                if matches!(\n                    invalidation_kind,\n                    DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)\n                ) {\n                    if let Some(ref deps) = d.next {\n                        // Normal dependencies should only have one next\n                        dependencies_to_invalidate.push(&deps.as_ref().slice()[0]);\n                        continue;\n                    }\n                    invalidated |= true;\n                    continue;\n                }\n                debug_assert_ne!(d.selector_offset, 0);\n                debug_assert_ne!(d.selector_offset, d.selector.len());\n                let invalidation = Invalidation::new(&d, self.host, None);\n                invalidated |= push_invalidation(\n                    invalidation,\n                    d.invalidation_kind(),\n                    descendant_invalidations,\n                    sibling_invalidations,\n                );\n            }\n            invalidated\n        };\n\n        if invalidated_self {\n            self.data.hint.insert(RestyleHint::RESTYLE_SELF);\n        }\n        invalidated_self\n    }\n\n    fn should_process_descendants(&mut self, element: E) -> bool {\n        if element == self.element {\n            return should_process_descendants(&self.data);\n        }\n\n        match element.borrow_data() {\n            Some(d) => should_process_descendants(&d),\n            None => return false,\n        }\n    }\n\n    fn recursion_limit_exceeded(&mut self, _element: E) {\n        unreachable!(\"Unexpected recursion limit\");\n    }\n\n    fn invalidated_descendants(&mut self, element: E, child: E) {\n        invalidated_descendants(element, child)\n    }\n\n    fn invalidated_self(&mut self, element: E) {\n        debug_assert_ne!(element, self.element);\n        invalidated_self(element);\n    }\n\n    fn invalidated_sibling(&mut self, element: E, of: E) {\n        debug_assert_ne!(element, self.element);\n        invalidated_sibling(element, of);\n    }\n}\n\n/// Invalidation for the selector(s) inside a relative selector.\npub struct RelativeSelectorInnerInvalidationProcessor<'a, 'b, 'c, E>\nwhere\n    E: TElement + 'a,\n{\n    /// Matching context to be used.\n    matching_context: MatchingContext<'b, E::Impl>,\n    /// Table of snapshots.\n    snapshot_table: Option<&'c ServoElementSnapshotTable>,\n    /// Incoming dependencies to be processed.\n    dependencies: &'c ElementDependencies<'a>,\n    /// Generated invalidations.\n    invalidations: InnerInvalidations<'a, E>,\n    /// Traversal map for this invalidation.\n    traversal_map: &'b SiblingTraversalMap<E>,\n}\n\nimpl<'a, 'b, 'c, E> RelativeSelectorInnerInvalidationProcessor<'a, 'b, 'c, E>\nwhere\n    E: TElement + 'a,\n{\n    fn new(\n        quirks_mode: QuirksMode,\n        snapshot_table: Option<&'c ServoElementSnapshotTable>,\n        dependencies: &'c ElementDependencies<'a>,\n        selector_caches: &'b mut SelectorCaches,\n        traversal_map: &'b SiblingTraversalMap<E>,\n    ) -> Self {\n        let matching_context = MatchingContext::new_for_visited(\n            MatchingMode::Normal,\n            None,\n            selector_caches,\n            VisitedHandlingMode::AllLinksVisitedAndUnvisited,\n            quirks_mode,\n            NeedsSelectorFlags::No,\n            MatchingForInvalidation::Yes,\n        );\n        Self {\n            matching_context,\n            snapshot_table,\n            dependencies,\n            invalidations: InnerInvalidations::default(),\n            traversal_map,\n        }\n    }\n\n    fn note_dependency(\n        &mut self,\n        element: E,\n        host: Option<OpaqueElement>,\n        dependency: &'a Dependency,\n        descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n        sibling_invalidations: &mut InvalidationVector<'a>,\n    ) {\n        match dependency.invalidation_kind() {\n            DependencyInvalidationKind::FullSelector => unreachable!(),\n            DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_) => (),\n            DependencyInvalidationKind::Relative(kind) => {\n                self.found_relative_selector_invalidation(element, kind, dependency);\n                return;\n            },\n        }\n        if matches!(\n            dependency.normal_invalidation_kind(),\n            NormalDependencyInvalidationKind::Element\n        ) {\n            // Ok, keep heading outwards.\n            debug_assert!(\n                dependency.next.is_some(),\n                \"Orphaned inner selector dependency?\"\n            );\n            if let Some(next) = dependency.next.as_ref() {\n                self.note_dependency(\n                    element,\n                    host,\n                    &next.as_ref().slice()[0],\n                    descendant_invalidations,\n                    sibling_invalidations,\n                );\n            }\n            return;\n        }\n        let invalidation = Invalidation::new(&dependency, None, None);\n        match dependency.normal_invalidation_kind() {\n            NormalDependencyInvalidationKind::Descendants => {\n                // Descendant invalidations are simplified due to pseudo-elements not being available within the relative selector.\n                descendant_invalidations.dom_descendants.push(invalidation)\n            },\n            NormalDependencyInvalidationKind::Siblings => sibling_invalidations.push(invalidation),\n            // Note(dshin, bug 1940212): Nesting can enabling stuffing pseudo-elements into :has, like `::marker { :has(&) }`.\n            // Ideally, we can just not insert the dependency into the invalidation map, but the necessary selector information\n            // for this (i.e. `HAS_PSEUDO`) is filtered out in `replace_parent_selector` through\n            // `SelectorFlags::forbidden_for_nesting`, so just ignoring such dependencies here is the best we can do.\n            _ => (),\n        }\n    }\n\n    /// Take the generated invalidations.\n    fn take_invalidations(self) -> InnerInvalidations<'a, E> {\n        self.invalidations\n    }\n}\n\nimpl<'a, 'b, 'c, E> InvalidationProcessor<'a, 'b, E>\n    for RelativeSelectorInnerInvalidationProcessor<'a, 'b, 'c, E>\nwhere\n    E: TElement + 'a,\n{\n    fn check_outer_dependency(\n        &mut self,\n        dependency: &Dependency,\n        element: E,\n        _: Option<OpaqueElement>,\n    ) -> bool {\n        if let Some(snapshot_table) = self.snapshot_table {\n            let wrapper = ElementWrapper::new(element, snapshot_table);\n            return check_dependency(\n                dependency,\n                &element,\n                &wrapper,\n                &mut self.matching_context,\n                None,\n            );\n        }\n        // Just invalidate if we don't have a snapshot.\n        true\n    }\n\n    fn matching_context(&mut self) -> &mut MatchingContext<'b, E::Impl> {\n        return &mut self.matching_context;\n    }\n\n    fn collect_invalidations(\n        &mut self,\n        element: E,\n        _self_invalidations: &mut InvalidationVector<'a>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n        sibling_invalidations: &mut InvalidationVector<'a>,\n    ) -> bool {\n        for (scope, dependency) in self.dependencies {\n            self.note_dependency(\n                element,\n                *scope,\n                dependency,\n                descendant_invalidations,\n                sibling_invalidations,\n            )\n        }\n        false\n    }\n\n    fn should_process_descendants(&mut self, _element: E) -> bool {\n        true\n    }\n\n    fn recursion_limit_exceeded(&mut self, _element: E) {\n        unreachable!(\"Unexpected recursion limit\");\n    }\n\n    // Don't do anything for normal invalidations.\n    fn invalidated_self(&mut self, _element: E) {}\n    fn invalidated_sibling(&mut self, _sibling: E, _of: E) {}\n    fn invalidated_descendants(&mut self, _element: E, _child: E) {}\n\n    fn found_relative_selector_invalidation(\n        &mut self,\n        element: E,\n        kind: RelativeDependencyInvalidationKind,\n        dep: &'a Dependency,\n    ) {\n        debug_assert!(dep.next.is_some(), \"Orphaned inners selector?\");\n        if element.relative_selector_search_direction().is_empty() {\n            return;\n        }\n        self.invalidations.push((\n            element,\n            RelativeSelectorInvalidation {\n                host: self.matching_context.current_host,\n                kind,\n                dependency: &dep.next.as_ref().unwrap().as_ref().slice()[0],\n            },\n        ));\n    }\n\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {\n        &self.traversal_map\n    }\n}\n"
  },
  {
    "path": "style/invalidation/element/restyle_hints.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Restyle hints: an optimization to avoid unnecessarily matching selectors.\n\nuse crate::traversal_flags::TraversalFlags;\n\nbitflags! {\n    /// The kind of restyle we need to do for a given element.\n    #[repr(C)]\n    #[derive(Clone, Copy, Debug)]\n    pub struct RestyleHint: u16 {\n        /// Do a selector match of the element.\n        const RESTYLE_SELF = 1 << 0;\n\n        /// Do a selector match of the element's pseudo-elements. Always to be combined with\n        /// RESTYLE_SELF.\n        const RESTYLE_PSEUDOS = 1 << 1;\n\n        /// Do a selector match if the element is a pseudo-element.\n        const RESTYLE_SELF_IF_PSEUDO = 1 << 2;\n\n        /// Do a selector match of the element's descendants.\n        const RESTYLE_DESCENDANTS = 1 << 3;\n\n        /// Recascade the current element.\n        const RECASCADE_SELF = 1 << 4;\n\n        /// Recascade the current element if it inherits any reset style.\n        const RECASCADE_SELF_IF_INHERIT_RESET_STYLE = 1 << 5;\n\n        /// Recascade all descendant elements.\n        const RECASCADE_DESCENDANTS = 1 << 6;\n\n        /// Replace the style data coming from CSS transitions without updating\n        /// any other style data. This hint is only processed in animation-only\n        /// traversal which is prior to normal traversal.\n        const RESTYLE_CSS_TRANSITIONS = 1 << 7;\n\n        /// Replace the style data coming from CSS animations without updating\n        /// any other style data. This hint is only processed in animation-only\n        /// traversal which is prior to normal traversal.\n        const RESTYLE_CSS_ANIMATIONS = 1 << 8;\n\n        /// Don't re-run selector-matching on the element, only the style\n        /// attribute has changed, and this change didn't have any other\n        /// dependencies.\n        const RESTYLE_STYLE_ATTRIBUTE = 1 << 9;\n\n        /// Replace the style data coming from SMIL animations without updating\n        /// any other style data. This hint is only processed in animation-only\n        /// traversal which is prior to normal traversal.\n        const RESTYLE_SMIL = 1 << 10;\n\n        /// Match self if this element is dependent on a style query.\n        const RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES = 1 << 11;\n\n        /// Match self or descendants if dependent on a named style query.\n        const RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER = 1 << 12;\n\n        /// Do a selector match of the element if it depends on an ancestor's\n        /// font metrics.\n        const RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS = 1 << 13;\n    }\n}\n\nimpl RestyleHint {\n    /// Creates a new `RestyleHint` indicating that the current element and all\n    /// its descendants must be fully restyled.\n    #[inline]\n    pub fn restyle_subtree() -> Self {\n        RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS\n    }\n\n    /// Creates a new `RestyleHint` indicating that the current element and all\n    /// its descendants must be recascaded.\n    #[inline]\n    pub fn recascade_subtree() -> Self {\n        RestyleHint::RECASCADE_SELF | RestyleHint::RECASCADE_DESCENDANTS\n    }\n\n    /// Returns whether this hint invalidates the element and all its\n    /// descendants.\n    #[inline]\n    pub fn contains_subtree(&self) -> bool {\n        self.contains(Self::restyle_subtree())\n    }\n\n    /// Returns whether we'll recascade all of the descendants.\n    #[inline]\n    pub fn will_recascade_subtree(&self) -> bool {\n        self.contains_subtree() || self.contains(Self::recascade_subtree())\n    }\n\n    /// Returns whether we need to restyle this element.\n    pub fn has_non_animation_invalidations(&self) -> bool {\n        !(*self & !Self::for_animations()).is_empty()\n    }\n\n    /// Propagates this restyle hint to a child element.\n    pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {\n        use std::mem;\n\n        // In the middle of an animation only restyle, we don't need to\n        // propagate any restyle hints, and we need to remove ourselves.\n        if traversal_flags.for_animation_only() {\n            self.remove_animation_hints();\n            return Self::empty();\n        }\n\n        debug_assert!(\n            !self.has_animation_hint(),\n            \"There should not be any animation restyle hints \\\n             during normal traversal\"\n        );\n\n        // Else we should clear ourselves, and return the propagated hint.\n        mem::replace(self, Self::empty()).propagate_for_non_animation_restyle()\n    }\n\n    /// Returns a new `RestyleHint` appropriate for children of the current element.\n    fn propagate_for_non_animation_restyle(&self) -> Self {\n        if self.contains(RestyleHint::RESTYLE_DESCENDANTS) {\n            return Self::restyle_subtree();\n        }\n        let mut result = Self::empty();\n        if self.contains(RestyleHint::RESTYLE_PSEUDOS) {\n            result |= Self::RESTYLE_SELF_IF_PSEUDO;\n        }\n        if self.contains(RestyleHint::RECASCADE_DESCENDANTS) {\n            result |= Self::recascade_subtree();\n        }\n        if self.contains(RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS) {\n            result |= Self::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS;\n        }\n        if self.contains(RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER) {\n            // We may need to restyle further down the tree if rules are\n            // declared for a named container.\n            // e.g @container my-name {#b {...}}\n            // and <div id=a> <div> <div id=b> </div> </div> </div>\n            // If a toggles `container-name: my-name` the rules for #b\n            // also invalidate. This is why we need one hint for unnamed\n            // container and one for named containers.\n            result |= RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER;\n        }\n\n        result\n    }\n\n    /// Returns a hint that contains all the replacement hints.\n    pub fn replacements() -> Self {\n        RestyleHint::RESTYLE_STYLE_ATTRIBUTE | Self::for_animations()\n    }\n\n    /// The replacements for the animation cascade levels.\n    #[inline]\n    pub fn for_animations() -> Self {\n        RestyleHint::RESTYLE_SMIL\n            | RestyleHint::RESTYLE_CSS_ANIMATIONS\n            | RestyleHint::RESTYLE_CSS_TRANSITIONS\n    }\n\n    /// Returns whether the hint specifies that an animation cascade level must\n    /// be replaced.\n    #[inline]\n    pub fn has_animation_hint(&self) -> bool {\n        self.intersects(Self::for_animations())\n    }\n\n    /// Returns whether the hint specifies that an animation cascade level must\n    /// be replaced.\n    #[inline]\n    pub fn has_animation_hint_or_recascade(&self) -> bool {\n        self.intersects(\n            Self::for_animations()\n                | Self::RECASCADE_SELF\n                | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE,\n        )\n    }\n\n    /// Returns whether the hint specifies some restyle work other than an\n    /// animation cascade level replacement.\n    #[inline]\n    pub fn has_non_animation_hint(&self) -> bool {\n        !(*self & !Self::for_animations()).is_empty()\n    }\n\n    /// Returns whether the hint specifies that some cascade levels must be\n    /// replaced.\n    #[inline]\n    pub fn has_replacements(&self) -> bool {\n        self.intersects(Self::replacements())\n    }\n\n    /// Removes all of the animation-related hints.\n    #[inline]\n    pub fn remove_animation_hints(&mut self) {\n        self.remove(Self::for_animations());\n\n        // While RECASCADE_SELF is not animation-specific, we only ever add and process it during\n        // traversal.  If we are here, removing animation hints, then we are in an animation-only\n        // traversal, and we know that any RECASCADE_SELF flag must have been set due to changes in\n        // inherited values after restyling for animations, and thus we want to remove it so that\n        // we don't later try to restyle the element during a normal restyle.\n        // (We could have separate RECASCADE_SELF_NORMAL and RECASCADE_SELF_ANIMATIONS flags to\n        // make it clear, but this isn't currently necessary.)\n        self.remove(Self::RECASCADE_SELF | Self::RECASCADE_SELF_IF_INHERIT_RESET_STYLE);\n    }\n}\n\nimpl Default for RestyleHint {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\n#[cfg(feature = \"servo\")]\nmalloc_size_of::malloc_size_of_is_0!(RestyleHint);\n"
  },
  {
    "path": "style/invalidation/element/state_and_attributes.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! An invalidation processor for style changes due to state and attribute\n//! changes.\n\nuse crate::context::SharedStyleContext;\nuse crate::data::ElementData;\nuse crate::dom::{TElement, TNode};\nuse crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};\nuse crate::invalidation::element::invalidation_map::*;\nuse crate::invalidation::element::invalidator::{\n    any_next_has_scope_in_negation, note_scope_dependency_force_at_subject,\n    DescendantInvalidationLists, InvalidationVector, SiblingTraversalMap,\n};\nuse crate::invalidation::element::invalidator::{Invalidation, InvalidationProcessor};\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::selector_map::SelectorMap;\nuse crate::selector_parser::Snapshot;\nuse crate::stylesheets::origin::OriginSet;\nuse crate::values::AtomIdent;\nuse crate::{Atom, WeakAtom};\nuse dom::ElementState;\nuse selectors::attr::CaseSensitivity;\nuse selectors::kleene_value::KleeneValue;\nuse selectors::matching::{\n    matches_selector_kleene, MatchingContext, MatchingForInvalidation, MatchingMode,\n    NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode,\n};\nuse selectors::OpaqueElement;\nuse smallvec::SmallVec;\n\n/// The collector implementation.\nstruct Collector<'a, 'b: 'a, 'selectors: 'a, E>\nwhere\n    E: TElement,\n{\n    element: E,\n    wrapper: ElementWrapper<'b, E>,\n    snapshot: &'a Snapshot,\n    matching_context: &'a mut MatchingContext<'b, E::Impl>,\n    lookup_element: E,\n    removed_id: Option<&'a WeakAtom>,\n    added_id: Option<&'a WeakAtom>,\n    classes_removed: &'a SmallVec<[Atom; 8]>,\n    classes_added: &'a SmallVec<[Atom; 8]>,\n    custom_states_removed: &'a SmallVec<[AtomIdent; 8]>,\n    custom_states_added: &'a SmallVec<[AtomIdent; 8]>,\n    state_changes: ElementState,\n    descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>,\n    sibling_invalidations: &'a mut InvalidationVector<'selectors>,\n    invalidates_self: bool,\n}\n\n/// An invalidation processor for style changes due to state and attribute\n/// changes.\npub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> {\n    shared_context: &'a SharedStyleContext<'b>,\n    element: E,\n    data: &'a mut ElementData,\n    matching_context: MatchingContext<'a, E::Impl>,\n    traversal_map: SiblingTraversalMap<E>,\n}\n\nimpl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> {\n    /// Creates a new StateAndAttrInvalidationProcessor.\n    pub fn new(\n        shared_context: &'a SharedStyleContext<'b>,\n        element: E,\n        data: &'a mut ElementData,\n        selector_caches: &'a mut SelectorCaches,\n    ) -> Self {\n        let matching_context = MatchingContext::new_for_visited(\n            MatchingMode::Normal,\n            None,\n            selector_caches,\n            VisitedHandlingMode::AllLinksVisitedAndUnvisited,\n            shared_context.quirks_mode(),\n            NeedsSelectorFlags::No,\n            MatchingForInvalidation::Yes,\n        );\n\n        Self {\n            shared_context,\n            element,\n            data,\n            matching_context,\n            traversal_map: SiblingTraversalMap::default(),\n        }\n    }\n}\n\n/// Checks a dependency against a given element and wrapper, to see if something\n/// changed.\npub fn check_dependency<E, W>(\n    dependency: &Dependency,\n    element: &E,\n    wrapper: &W,\n    context: &mut MatchingContext<'_, E::Impl>,\n    scope: Option<OpaqueElement>,\n) -> bool\nwhere\n    E: TElement,\n    W: selectors::Element<Impl = E::Impl>,\n{\n    context.for_invalidation_comparison(|context| {\n        context.nest_for_scope_condition(scope, |context| {\n            let matches_now = matches_selector_kleene(\n                &dependency.selector,\n                dependency.selector_offset,\n                None,\n                element,\n                context,\n            );\n\n            // If the previous dependency was a scope dependency (i.e. by `scope` is set),\n            // possible change in scope element is encapsulated in `:scope`, whose\n            // matching value will not change. We instead check that the change in scope\n            // element can propagate (i.e. This selector matches).\n            if scope.is_some() && matches_now != KleeneValue::False {\n                return true;\n            }\n\n            let matched_then = matches_selector_kleene(\n                &dependency.selector,\n                dependency.selector_offset,\n                None,\n                wrapper,\n                context,\n            );\n\n            matched_then != matches_now || matches_now == KleeneValue::Unknown\n        })\n    })\n}\n\n/// Whether we should process the descendants of a given element for style\n/// invalidation.\npub fn should_process_descendants(data: &ElementData) -> bool {\n    !data.styles.is_display_none() && !data.hint.contains(RestyleHint::RESTYLE_DESCENDANTS)\n}\n\n/// Propagates the bits after invalidating a descendant child.\npub fn propagate_dirty_bit_up_to<E>(ancestor: E, child: E)\nwhere\n    E: TElement,\n{\n    // The child may not be a flattened tree child of the current element,\n    // but may be arbitrarily deep.\n    //\n    // Since we keep the traversal flags in terms of the flattened tree,\n    // we need to propagate it as appropriate.\n    let mut current = child.traversal_parent();\n    while let Some(parent) = current.take() {\n        unsafe { parent.set_dirty_descendants() };\n        current = parent.traversal_parent();\n\n        if parent == ancestor {\n            return;\n        }\n    }\n    debug_assert!(\n        false,\n        \"Should've found {:?} as an ancestor of {:?}\",\n        ancestor, child\n    );\n}\n\n/// Propagates the bits after invalidating a descendant child, if needed.\npub fn invalidated_descendants<E>(element: E, child: E)\nwhere\n    E: TElement,\n{\n    if !child.has_data() {\n        return;\n    }\n    propagate_dirty_bit_up_to(element, child)\n}\n\n/// Sets the appropriate restyle hint after invalidating the style of a given\n/// element.\npub fn invalidated_self<E>(element: E) -> bool\nwhere\n    E: TElement,\n{\n    let mut data = match element.mutate_data() {\n        Some(data) => data,\n        None => return false,\n    };\n    data.hint.insert(RestyleHint::RESTYLE_SELF);\n    true\n}\n\n/// Sets the appropriate hint after invalidating the style of a sibling.\npub fn invalidated_sibling<E>(element: E, of: E)\nwhere\n    E: TElement,\n{\n    debug_assert_eq!(\n        element.as_node().parent_node(),\n        of.as_node().parent_node(),\n        \"Should be siblings\"\n    );\n    if !invalidated_self(element) {\n        return;\n    }\n    if element.traversal_parent() != of.traversal_parent() {\n        let parent = element.as_node().parent_element_or_host();\n        debug_assert!(\n            parent.is_some(),\n            \"How can we have siblings without parent nodes?\"\n        );\n        if let Some(e) = parent {\n            propagate_dirty_bit_up_to(e, element)\n        }\n    }\n}\n\nimpl<'a, 'b: 'a, E: 'a> InvalidationProcessor<'a, 'a, E>\n    for StateAndAttrInvalidationProcessor<'a, 'b, E>\nwhere\n    E: TElement,\n{\n    /// We need to invalidate style on pseudo-elements, in order to process\n    /// changes that could otherwise end up in ::before or ::after content being\n    /// generated, and invalidate lazy pseudo caches.\n    fn invalidates_on_pseudo_element(&self) -> bool {\n        true\n    }\n\n    fn check_outer_dependency(\n        &mut self,\n        dependency: &Dependency,\n        element: E,\n        scope: Option<OpaqueElement>,\n    ) -> bool {\n        // We cannot assert about `element` having a snapshot here (in fact it\n        // most likely won't), because it may be an arbitrary descendant or\n        // later-sibling of the element we started invalidating with.\n        let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);\n        check_dependency(\n            dependency,\n            &element,\n            &wrapper,\n            &mut self.matching_context,\n            scope,\n        )\n    }\n\n    fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> {\n        &mut self.matching_context\n    }\n\n    fn sibling_traversal_map(&self) -> &SiblingTraversalMap<E> {\n        &self.traversal_map\n    }\n\n    fn collect_invalidations(\n        &mut self,\n        element: E,\n        _self_invalidations: &mut InvalidationVector<'a>,\n        descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n        sibling_invalidations: &mut InvalidationVector<'a>,\n    ) -> bool {\n        debug_assert_eq!(element, self.element);\n        debug_assert!(element.has_snapshot(), \"Why bothering?\");\n\n        let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map);\n\n        let state_changes = wrapper.state_changes();\n        let Some(snapshot) = wrapper.snapshot() else {\n            return false;\n        };\n\n        if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() {\n            return false;\n        }\n\n        let mut classes_removed = SmallVec::<[Atom; 8]>::new();\n        let mut classes_added = SmallVec::<[Atom; 8]>::new();\n        if snapshot.class_changed() {\n            // TODO(emilio): Do this more efficiently!\n            snapshot.each_class(|c| {\n                if !element.has_class(c, CaseSensitivity::CaseSensitive) {\n                    classes_removed.push(c.0.clone())\n                }\n            });\n\n            element.each_class(|c| {\n                if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {\n                    classes_added.push(c.0.clone())\n                }\n            })\n        }\n\n        let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new();\n        let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new();\n        if snapshot.has_custom_states() {\n            snapshot.each_custom_state(|s| {\n                if !element.has_custom_state(s) {\n                    custom_states_removed.push(s.clone())\n                }\n            });\n            element.each_custom_state(|s| {\n                if !snapshot.has_custom_state(s) {\n                    custom_states_added.push(s.clone())\n                }\n            })\n        }\n\n        let mut id_removed = None;\n        let mut id_added = None;\n        if snapshot.id_changed() {\n            let old_id = snapshot.id_attr();\n            let current_id = element.id();\n\n            if old_id != current_id {\n                id_removed = old_id;\n                id_added = current_id;\n            }\n        }\n\n        if log_enabled!(::log::Level::Debug) {\n            debug!(\"Collecting changes for: {:?}\", element);\n            if !state_changes.is_empty() {\n                debug!(\" > state: {:?}\", state_changes);\n            }\n            if snapshot.id_changed() {\n                debug!(\" > id changed: +{:?} -{:?}\", id_added, id_removed);\n            }\n            if snapshot.class_changed() {\n                debug!(\n                    \" > class changed: +{:?} -{:?}\",\n                    classes_added, classes_removed\n                );\n            }\n            let mut attributes_changed = false;\n            snapshot.each_attr_changed(|_| {\n                attributes_changed = true;\n            });\n            if attributes_changed {\n                debug!(\n                    \" > attributes changed, old: {}\",\n                    snapshot.debug_list_attributes()\n                )\n            }\n        }\n\n        let lookup_element = if element.implemented_pseudo_element().is_some() {\n            element.pseudo_element_originating_element().unwrap()\n        } else {\n            element\n        };\n\n        let mut shadow_rule_datas = SmallVec::<[_; 3]>::new();\n        let matches_document_author_rules =\n            element.each_applicable_non_document_style_rule_data(|data, host| {\n                shadow_rule_datas.push((data, host.opaque()))\n            });\n\n        let invalidated_self = {\n            let mut collector = Collector {\n                wrapper,\n                lookup_element,\n                state_changes,\n                element,\n                snapshot: &snapshot,\n                matching_context: &mut self.matching_context,\n                removed_id: id_removed,\n                added_id: id_added,\n                classes_removed: &classes_removed,\n                classes_added: &classes_added,\n                custom_states_removed: &custom_states_removed,\n                custom_states_added: &custom_states_added,\n                descendant_invalidations,\n                sibling_invalidations,\n                invalidates_self: false,\n            };\n\n            let document_origins = if !matches_document_author_rules {\n                OriginSet::ORIGIN_USER_AGENT | OriginSet::ORIGIN_USER\n            } else {\n                OriginSet::all()\n            };\n\n            for (cascade_data, origin) in self.shared_context.stylist.iter_origins() {\n                if document_origins.contains(origin.into()) {\n                    collector\n                        .collect_dependencies_in_invalidation_map(cascade_data.invalidation_map());\n                }\n            }\n\n            for &(ref data, ref host) in &shadow_rule_datas {\n                collector.matching_context.current_host = Some(host.clone());\n                collector.collect_dependencies_in_invalidation_map(data.invalidation_map());\n            }\n\n            collector.invalidates_self\n        };\n\n        // If we generated a ton of descendant invalidations, it's probably not\n        // worth to go ahead and try to process them.\n        //\n        // Just restyle the descendants directly.\n        //\n        // This number is completely made-up, but the page that made us add this\n        // code generated 1960+ invalidations (bug 1420741).\n        //\n        // We don't look at slotted_descendants because those don't propagate\n        // down more than one level anyway.\n        if descendant_invalidations.dom_descendants.len() > 150 {\n            self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);\n        }\n\n        if invalidated_self {\n            self.data.hint.insert(RestyleHint::RESTYLE_SELF);\n        }\n\n        invalidated_self\n    }\n\n    fn should_process_descendants(&mut self, element: E) -> bool {\n        if element == self.element {\n            return should_process_descendants(&self.data);\n        }\n\n        match element.borrow_data() {\n            Some(d) => should_process_descendants(&d),\n            None => return false,\n        }\n    }\n\n    fn recursion_limit_exceeded(&mut self, element: E) {\n        if element == self.element {\n            self.data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);\n            return;\n        }\n\n        if let Some(mut data) = element.mutate_data() {\n            data.hint.insert(RestyleHint::RESTYLE_DESCENDANTS);\n        }\n    }\n\n    fn invalidated_descendants(&mut self, element: E, child: E) {\n        invalidated_descendants(element, child)\n    }\n\n    fn invalidated_self(&mut self, element: E) {\n        debug_assert_ne!(element, self.element);\n        invalidated_self(element);\n    }\n\n    fn invalidated_sibling(&mut self, element: E, of: E) {\n        debug_assert_ne!(element, self.element);\n        invalidated_sibling(element, of);\n    }\n\n    fn invalidated_highlight_pseudo(&mut self, element: E) {\n        element.note_highlight_pseudo_style_invalidated();\n    }\n}\n\nimpl<'a, 'b, 'selectors, E> Collector<'a, 'b, 'selectors, E>\nwhere\n    E: TElement,\n    'selectors: 'a,\n{\n    fn collect_dependencies_in_invalidation_map(&mut self, map: &'selectors InvalidationMap) {\n        let quirks_mode = self.matching_context.quirks_mode();\n        let removed_id = self.removed_id;\n        if let Some(ref id) = removed_id {\n            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {\n                for dep in deps {\n                    self.scan_dependency(dep, false);\n                }\n            }\n        }\n\n        let added_id = self.added_id;\n        if let Some(ref id) = added_id {\n            if let Some(deps) = map.id_to_selector.get(id, quirks_mode) {\n                for dep in deps {\n                    self.scan_dependency(dep, false);\n                }\n            }\n        }\n\n        for class in self.classes_added.iter().chain(self.classes_removed.iter()) {\n            if let Some(deps) = map.class_to_selector.get(class, quirks_mode) {\n                for dep in deps {\n                    self.scan_dependency(dep, false);\n                }\n            }\n        }\n\n        for state in self\n            .custom_states_added\n            .iter()\n            .chain(self.custom_states_removed.iter())\n        {\n            if let Some(deps) = map.custom_state_affecting_selectors.get(state) {\n                for dep in deps {\n                    self.scan_dependency(dep, false);\n                }\n            }\n        }\n\n        self.snapshot.each_attr_changed(|attribute| {\n            if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {\n                for dep in deps {\n                    self.scan_dependency(dep, false);\n                }\n            }\n        });\n\n        self.collect_state_dependencies(&map.state_affecting_selectors)\n    }\n\n    fn collect_state_dependencies(&mut self, map: &'selectors SelectorMap<StateDependency>) {\n        if self.state_changes.is_empty() {\n            return;\n        }\n        map.lookup_with_additional(\n            self.lookup_element,\n            self.matching_context.quirks_mode(),\n            self.removed_id,\n            self.classes_removed,\n            self.state_changes,\n            |dependency| {\n                if !dependency.state.intersects(self.state_changes) {\n                    return true;\n                }\n                self.scan_dependency(&dependency.dep, false);\n                true\n            },\n        );\n    }\n\n    /// Check whether a dependency should be taken into account.\n    #[inline]\n    fn check_dependency(&mut self, dependency: &Dependency, set_scope: bool) -> bool {\n        check_dependency(\n            dependency,\n            &self.element,\n            &self.wrapper,\n            &mut self.matching_context,\n            set_scope.then(|| self.element.opaque()),\n        )\n    }\n\n    fn scan_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {\n        debug_assert!(\n            matches!(\n                dependency.invalidation_kind(),\n                DependencyInvalidationKind::Normal(_) | DependencyInvalidationKind::Scope(_)\n            ),\n            \"Found unexpected dependency invalidation kind\"\n        );\n        debug!(\n            \"TreeStyleInvalidator::scan_dependency({:?}, {:?})\",\n            self.element, dependency\n        );\n\n        if !self.dependency_may_be_relevant(dependency) {\n            return;\n        }\n\n        if self.check_dependency(dependency, set_scope) {\n            return self.note_dependency(dependency, set_scope);\n        }\n    }\n\n    fn note_dependency(&mut self, dependency: &'selectors Dependency, set_scope: bool) {\n        debug_assert!(self.dependency_may_be_relevant(dependency));\n        let invalidation_kind = dependency.invalidation_kind();\n        if matches!(\n            invalidation_kind,\n            DependencyInvalidationKind::Normal(NormalDependencyInvalidationKind::Element)\n        ) {\n            if let Some(ref next) = dependency.next {\n                // We know something changed in the inner selector, go outwards\n                // now.\n                self.scan_dependency(&next.as_ref().slice()[0], set_scope);\n            } else {\n                self.invalidates_self = true;\n            }\n            return;\n        }\n\n        if let DependencyInvalidationKind::Scope(scope_kind) = invalidation_kind {\n            if scope_kind == ScopeDependencyInvalidationKind::ImplicitScope {\n                if let Some(ref next) = dependency.next {\n                    // When we reach an implicit scope dependency, we know there's an\n                    // element matching that implicit scope somewhere in the descendant.\n                    // We need to go find it so that we can continue the invalidation from\n                    // its next dependencies.\n                    for dep in next.as_ref().slice() {\n                        let invalidation = Invalidation::new_always_effective_for_next_descendant(\n                            dep,\n                            self.matching_context.current_host.clone(),\n                            self.matching_context.scope_element,\n                        );\n\n                        self.descendant_invalidations\n                            .dom_descendants\n                            .push(invalidation);\n                    }\n                    return;\n                }\n            }\n\n            if dependency.selector.is_rightmost(dependency.selector_offset) {\n                let force_add = any_next_has_scope_in_negation(dependency);\n                if scope_kind == ScopeDependencyInvalidationKind::ScopeEnd || force_add {\n                    let invalidations = note_scope_dependency_force_at_subject(\n                        dependency,\n                        self.matching_context.current_host.clone(),\n                        self.matching_context.scope_element,\n                        force_add,\n                    );\n                    for invalidation in invalidations {\n                        self.descendant_invalidations\n                            .dom_descendants\n                            .push(invalidation);\n                    }\n                    self.invalidates_self = true;\n                } else if let Some(ref next) = dependency.next {\n                    for dep in next.as_ref().slice() {\n                        self.scan_dependency(dep, true);\n                    }\n                }\n                return;\n            }\n        }\n\n        debug_assert_ne!(dependency.selector_offset, 0);\n        debug_assert_ne!(dependency.selector_offset, dependency.selector.len());\n\n        let invalidation = Invalidation::new(\n            &dependency,\n            self.matching_context.current_host.clone(),\n            self.matching_context.scope_element.clone(),\n        );\n\n        let invalidated_self = push_invalidation(\n            invalidation,\n            invalidation_kind,\n            self.descendant_invalidations,\n            self.sibling_invalidations,\n        );\n\n        // For highlight pseudos (::selection, ::highlight, ::target-text), we need\n        // to trigger a repaint since their styles are resolved lazily during\n        // painting rather than during the restyle traversal.\n        if invalidated_self\n            && dependency\n                .selector\n                .pseudo_element()\n                .is_some_and(|p| p.is_lazy_painted_highlight_pseudo())\n        {\n            self.element.note_highlight_pseudo_style_invalidated();\n        }\n\n        self.invalidates_self |= invalidated_self;\n    }\n\n    /// Returns whether `dependency` may cause us to invalidate the style of\n    /// more elements than what we've already invalidated.\n    fn dependency_may_be_relevant(&self, dependency: &Dependency) -> bool {\n        match dependency.invalidation_kind() {\n            DependencyInvalidationKind::FullSelector | DependencyInvalidationKind::Relative(_) => {\n                unreachable!()\n            },\n            DependencyInvalidationKind::Scope(_) => true,\n            DependencyInvalidationKind::Normal(kind) => match kind {\n                NormalDependencyInvalidationKind::Element => !self.invalidates_self,\n                NormalDependencyInvalidationKind::SlottedElements => {\n                    self.element.is_html_slot_element()\n                },\n                NormalDependencyInvalidationKind::Parts => self.element.shadow_root().is_some(),\n                NormalDependencyInvalidationKind::ElementAndDescendants\n                | NormalDependencyInvalidationKind::Siblings\n                | NormalDependencyInvalidationKind::Descendants => true,\n            },\n        }\n    }\n}\n\npub(crate) fn push_invalidation<'a>(\n    invalidation: Invalidation<'a>,\n    invalidation_kind: DependencyInvalidationKind,\n    descendant_invalidations: &mut DescendantInvalidationLists<'a>,\n    sibling_invalidations: &mut InvalidationVector<'a>,\n) -> bool {\n    match invalidation_kind {\n        DependencyInvalidationKind::FullSelector => unreachable!(),\n        DependencyInvalidationKind::Relative(_) => unreachable!(),\n        DependencyInvalidationKind::Scope(_) => {\n            // Scope invalidation kind matters only upon reaching the subject.\n            // Examine the combinator to the right of the compound.\n            let combinator = invalidation.combinator_to_right();\n            if combinator.is_sibling() {\n                sibling_invalidations.push(invalidation);\n            } else {\n                descendant_invalidations.dom_descendants.push(invalidation);\n            }\n            true\n        },\n        DependencyInvalidationKind::Normal(kind) => match kind {\n            NormalDependencyInvalidationKind::Element => unreachable!(),\n            NormalDependencyInvalidationKind::ElementAndDescendants => {\n                descendant_invalidations.dom_descendants.push(invalidation);\n                true\n            },\n            NormalDependencyInvalidationKind::Descendants => {\n                descendant_invalidations.dom_descendants.push(invalidation);\n                false\n            },\n            NormalDependencyInvalidationKind::Siblings => {\n                sibling_invalidations.push(invalidation);\n                false\n            },\n            NormalDependencyInvalidationKind::Parts => {\n                descendant_invalidations.parts.push(invalidation);\n                false\n            },\n            NormalDependencyInvalidationKind::SlottedElements => {\n                descendant_invalidations\n                    .slotted_descendants\n                    .push(invalidation);\n                false\n            },\n        },\n    }\n}\n\npub(crate) fn dependency_may_be_relevant<E: TElement>(\n    dependency: &Dependency,\n    element: &E,\n    already_invalidated_self: bool,\n) -> bool {\n    match dependency.invalidation_kind() {\n        DependencyInvalidationKind::FullSelector => unreachable!(),\n        DependencyInvalidationKind::Relative(_) => unreachable!(),\n        DependencyInvalidationKind::Scope(_) => true,\n        DependencyInvalidationKind::Normal(kind) => match kind {\n            NormalDependencyInvalidationKind::Element => !already_invalidated_self,\n            NormalDependencyInvalidationKind::SlottedElements => element.is_html_slot_element(),\n            NormalDependencyInvalidationKind::Parts => element.shadow_root().is_some(),\n            NormalDependencyInvalidationKind::ElementAndDescendants\n            | NormalDependencyInvalidationKind::Siblings\n            | NormalDependencyInvalidationKind::Descendants => true,\n        },\n    }\n}\n"
  },
  {
    "path": "style/invalidation/media_queries.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Code related to the invalidation of media-query-affected rules.\n\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::shared_lock::SharedRwLockReadGuard;\nuse crate::stylesheets::{CustomMediaMap, DocumentRule, ImportRule, MediaRule};\nuse crate::stylesheets::{NestedRuleIterationCondition, StylesheetContents, SupportsRule};\nuse rustc_hash::FxHashSet;\n\n/// A key for a given media query result.\n///\n/// NOTE: It happens to be the case that all the media lists we care about\n/// happen to have a stable address, so we can just use an opaque pointer to\n/// represent them.\n///\n/// Also, note that right now when a rule or stylesheet is removed, we do a full\n/// style flush, so there's no need to worry about other item created with the\n/// same pointer address.\n///\n/// If this changes, though, we may need to remove the item from the cache if\n/// present before it goes away.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\npub struct MediaListKey(usize);\n\nimpl MediaListKey {\n    /// Create a MediaListKey from a raw usize.\n    pub fn from_raw(k: usize) -> Self {\n        MediaListKey(k)\n    }\n}\n\n/// A trait to get a given `MediaListKey` for a given item that can hold a\n/// `MediaList`.\npub trait ToMediaListKey: Sized {\n    /// Get a `MediaListKey` for this item. This key needs to uniquely identify\n    /// the item.\n    fn to_media_list_key(&self) -> MediaListKey {\n        MediaListKey(self as *const Self as usize)\n    }\n}\n\nimpl ToMediaListKey for StylesheetContents {}\nimpl ToMediaListKey for ImportRule {}\nimpl ToMediaListKey for MediaRule {}\n\n/// A struct that holds the result of a media query evaluation pass for the\n/// media queries that evaluated successfully.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq)]\npub struct EffectiveMediaQueryResults {\n    /// The set of media lists that matched last time.\n    set: FxHashSet<MediaListKey>,\n}\n\nimpl EffectiveMediaQueryResults {\n    /// Trivially constructs an empty `EffectiveMediaQueryResults`.\n    pub fn new() -> Self {\n        Self {\n            set: FxHashSet::default(),\n        }\n    }\n\n    /// Resets the results, using an empty key.\n    pub fn clear(&mut self) {\n        self.set.clear()\n    }\n\n    /// Returns whether a given item was known to be effective when the results\n    /// were cached.\n    pub fn was_effective<T>(&self, item: &T) -> bool\n    where\n        T: ToMediaListKey,\n    {\n        self.set.contains(&item.to_media_list_key())\n    }\n\n    /// Notices that an effective item has been seen, and caches it as matching.\n    pub fn saw_effective<T>(&mut self, item: &T)\n    where\n        T: ToMediaListKey,\n    {\n        // NOTE(emilio): We can't assert that we don't cache the same item twice\n        // because of stylesheet reusing... shrug.\n        self.set.insert(item.to_media_list_key());\n    }\n}\n\n/// A filter that filters over effective rules, but allowing all potentially\n/// effective `@media` rules.\npub struct PotentiallyEffectiveMediaRules;\n\nimpl NestedRuleIterationCondition for PotentiallyEffectiveMediaRules {\n    fn process_import(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &CustomMediaMap,\n        _: &ImportRule,\n    ) -> bool {\n        true\n    }\n\n    fn process_media(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &CustomMediaMap,\n        _: &MediaRule,\n    ) -> bool {\n        true\n    }\n\n    /// Whether we should process the nested rules in a given `@-moz-document` rule.\n    fn process_document(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        rule: &DocumentRule,\n    ) -> bool {\n        use crate::stylesheets::EffectiveRules;\n        EffectiveRules::process_document(guard, device, quirks_mode, rule)\n    }\n\n    /// Whether we should process the nested rules in a given `@supports` rule.\n    fn process_supports(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        rule: &SupportsRule,\n    ) -> bool {\n        use crate::stylesheets::EffectiveRules;\n        EffectiveRules::process_supports(guard, device, quirks_mode, rule)\n    }\n}\n"
  },
  {
    "path": "style/invalidation/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Different bits of code related to invalidating style.\n\npub mod element;\npub mod media_queries;\npub mod stylesheets;\npub mod viewport_units;\n"
  },
  {
    "path": "style/invalidation/stylesheets.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A collection of invalidations due to changes in which stylesheets affect a\n//! document.\n\n#![deny(unsafe_code)]\n\nuse crate::context::QuirksMode;\nuse crate::data::ElementData;\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::dom::{TDocument, TElement, TNode};\nuse crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::selector_map::PrecomputedHashSet;\nuse crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap};\nuse crate::shared_lock::SharedRwLockReadGuard;\nuse crate::simple_buckets_map::SimpleBucketsMap;\nuse crate::stylesheets::{\n    CssRule, CssRuleRef, CustomMediaMap, EffectiveRules, EffectiveRulesIterator,\n    StylesheetInDocument,\n};\nuse crate::stylist::CascadeDataDifference;\nuse crate::values::specified::position::PositionTryFallbacksItem;\nuse crate::values::AtomIdent;\nuse crate::Atom;\nuse crate::LocalName as SelectorLocalName;\nuse selectors::parser::{Component, LocalName, Selector};\n\n/// The kind of change that happened for a given rule.\n#[repr(u32)]\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\npub enum RuleChangeKind {\n    /// Some change in the rule which we don't know about, and could have made\n    /// the rule change in any way.\n    Generic = 0,\n    /// The rule was inserted.\n    Insertion,\n    /// The rule was removed.\n    Removal,\n    /// A change in the declarations of a style rule.\n    StyleRuleDeclarations,\n    /// A change in the declarations of an @position-try rule.\n    PositionTryDeclarations,\n}\n\n/// A style sheet invalidation represents a kind of element or subtree that may\n/// need to be restyled. Whether it represents a whole subtree or just a single\n/// element is determined by the given InvalidationKind in\n/// StylesheetInvalidationSet's maps.\n#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]\nenum Invalidation {\n    /// An element with a given id.\n    ID(AtomIdent),\n    /// An element with a given class name.\n    Class(AtomIdent),\n    /// An element with a given local name.\n    LocalName {\n        name: SelectorLocalName,\n        lower_name: SelectorLocalName,\n    },\n}\n\nimpl Invalidation {\n    fn is_id(&self) -> bool {\n        matches!(*self, Invalidation::ID(..))\n    }\n\n    fn is_id_or_class(&self) -> bool {\n        matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))\n    }\n}\n\n/// Whether we should invalidate just the element, or the whole subtree within\n/// it.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]\nenum InvalidationKind {\n    None = 0,\n    Element,\n    Scope,\n}\n\nimpl std::ops::BitOrAssign for InvalidationKind {\n    #[inline]\n    fn bitor_assign(&mut self, other: Self) {\n        *self = std::cmp::max(*self, other);\n    }\n}\n\nimpl InvalidationKind {\n    #[inline]\n    fn is_scope(self) -> bool {\n        matches!(self, Self::Scope)\n    }\n\n    #[inline]\n    fn add(&mut self, other: Option<&InvalidationKind>) {\n        if let Some(other) = other {\n            *self |= *other;\n        }\n    }\n}\n\n/// A set of invalidations due to stylesheet changes.\n///\n/// TODO(emilio): We might be able to do the same analysis for media query changes too (or even\n/// selector changes?) specially now that we take the cascade data difference into account.\n#[derive(Debug, Default, MallocSizeOf)]\npub struct StylesheetInvalidationSet {\n    buckets: SimpleBucketsMap<InvalidationKind>,\n    style_fully_invalid: bool,\n    /// The difference between the old and new cascade data, incrementally collected until flush()\n    /// returns it.\n    pub cascade_data_difference: CascadeDataDifference,\n}\n\nimpl StylesheetInvalidationSet {\n    /// Create an empty `StylesheetInvalidationSet`.\n    pub fn new() -> Self {\n        Default::default()\n    }\n\n    /// Mark the DOM tree styles' as fully invalid.\n    pub fn invalidate_fully(&mut self) {\n        debug!(\"StylesheetInvalidationSet::invalidate_fully\");\n        self.buckets.clear();\n        self.style_fully_invalid = true;\n    }\n\n    /// Analyze the given stylesheet, and collect invalidations from their rules, in order to avoid\n    /// doing a full restyle when we style the document next time.\n    pub fn collect_invalidations_for<S>(\n        &mut self,\n        device: &Device,\n        custom_media: &CustomMediaMap,\n        stylesheet: &S,\n        guard: &SharedRwLockReadGuard,\n    ) where\n        S: StylesheetInDocument,\n    {\n        debug!(\"StylesheetInvalidationSet::collect_invalidations_for\");\n        if self.style_fully_invalid {\n            debug!(\" > Fully invalid already\");\n            return;\n        }\n\n        if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, custom_media, guard)\n        {\n            debug!(\" > Stylesheet was not effective\");\n            return; // Nothing to do here.\n        }\n\n        let quirks_mode = device.quirks_mode();\n        for rule in stylesheet\n            .contents(guard)\n            .effective_rules(device, custom_media, guard)\n        {\n            self.collect_invalidations_for_rule(\n                rule,\n                guard,\n                device,\n                quirks_mode,\n                /* is_generic_change = */ false,\n                // Note(dshin): Technically, the iterator should provide the ancestor chain as it\n                // traverses down, but it shouldn't make a difference.\n                &[],\n            );\n            if self.style_fully_invalid {\n                break;\n            }\n        }\n\n        debug!(\n            \" > resulting class invalidations: {:?}\",\n            self.buckets.classes\n        );\n        debug!(\" > resulting id invalidations: {:?}\", self.buckets.ids);\n        debug!(\n            \" > resulting local name invalidations: {:?}\",\n            self.buckets.local_names\n        );\n        debug!(\" > style_fully_invalid: {}\", self.style_fully_invalid);\n    }\n\n    /// Returns whether there's no invalidation to process.\n    pub fn is_empty(&self) -> bool {\n        !self.style_fully_invalid\n            && self.buckets.is_empty()\n            && self.cascade_data_difference.is_empty()\n    }\n\n    fn invalidation_kind_for<E>(\n        &self,\n        element: E,\n        snapshot: Option<&Snapshot>,\n        quirks_mode: QuirksMode,\n    ) -> InvalidationKind\n    where\n        E: TElement,\n    {\n        debug_assert!(!self.style_fully_invalid);\n\n        let mut kind = InvalidationKind::None;\n\n        if !self.buckets.classes.is_empty() {\n            element.each_class(|c| {\n                kind.add(self.buckets.classes.get(c, quirks_mode));\n            });\n\n            if kind.is_scope() {\n                return kind;\n            }\n\n            if let Some(snapshot) = snapshot {\n                snapshot.each_class(|c| {\n                    kind.add(self.buckets.classes.get(c, quirks_mode));\n                });\n\n                if kind.is_scope() {\n                    return kind;\n                }\n            }\n        }\n\n        if !self.buckets.ids.is_empty() {\n            if let Some(ref id) = element.id() {\n                kind.add(self.buckets.ids.get(id, quirks_mode));\n                if kind.is_scope() {\n                    return kind;\n                }\n            }\n\n            if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {\n                kind.add(self.buckets.ids.get(old_id, quirks_mode));\n                if kind.is_scope() {\n                    return kind;\n                }\n            }\n        }\n\n        if !self.buckets.local_names.is_empty() {\n            kind.add(self.buckets.local_names.get(element.local_name()));\n        }\n\n        kind\n    }\n\n    /// Processes the style invalidation set, invalidating elements as needed.\n    /// Returns true if any style invalidations occurred.\n    pub fn process_style<E>(&self, root: E, snapshots: Option<&SnapshotMap>) -> bool\n    where\n        E: TElement,\n    {\n        debug!(\n            \"StylesheetInvalidationSet::process_style({root:?}, snapshots: {})\",\n            snapshots.is_some()\n        );\n\n        {\n            let mut data = match root.mutate_data() {\n                Some(data) => data,\n                None => return false,\n            };\n\n            if self.style_fully_invalid {\n                debug!(\"process_invalidations: fully_invalid({:?})\", root);\n                data.hint.insert(RestyleHint::restyle_subtree());\n                return true;\n            }\n        }\n\n        if self.buckets.is_empty() {\n            debug!(\"process_invalidations: empty invalidation set\");\n            return false;\n        }\n\n        let quirks_mode = root.as_node().owner_doc().quirks_mode();\n        self.process_invalidations_in_subtree(root, snapshots, quirks_mode)\n    }\n\n    /// Process style invalidations in a given subtree. This traverses the\n    /// subtree looking for elements that match the invalidations in our hash\n    /// map members.\n    ///\n    /// Returns whether it invalidated at least one element's style.\n    #[allow(unsafe_code)]\n    fn process_invalidations_in_subtree<E>(\n        &self,\n        element: E,\n        snapshots: Option<&SnapshotMap>,\n        quirks_mode: QuirksMode,\n    ) -> bool\n    where\n        E: TElement,\n    {\n        debug!(\"process_invalidations_in_subtree({:?})\", element);\n        let mut data = match element.mutate_data() {\n            Some(data) => data,\n            None => return false,\n        };\n\n        if !data.has_styles() {\n            return false;\n        }\n\n        if data.hint.contains_subtree() {\n            debug!(\n                \"process_invalidations_in_subtree: {:?} was already invalid\",\n                element\n            );\n            return false;\n        }\n\n        let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));\n        let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());\n\n        match self.invalidation_kind_for(element, snapshot, quirks_mode) {\n            InvalidationKind::None => {},\n            InvalidationKind::Element => {\n                debug!(\n                    \"process_invalidations_in_subtree: {:?} matched self\",\n                    element\n                );\n                data.hint.insert(RestyleHint::RESTYLE_SELF);\n            },\n            InvalidationKind::Scope => {\n                debug!(\n                    \"process_invalidations_in_subtree: {:?} matched subtree\",\n                    element\n                );\n                data.hint.insert(RestyleHint::restyle_subtree());\n                return true;\n            },\n        }\n\n        let mut any_children_invalid = false;\n\n        for child in element.traversal_children() {\n            let child = match child.as_element() {\n                Some(e) => e,\n                None => continue,\n            };\n\n            any_children_invalid |=\n                self.process_invalidations_in_subtree(child, snapshots, quirks_mode);\n        }\n\n        if any_children_invalid {\n            debug!(\n                \"Children of {:?} changed, setting dirty descendants\",\n                element\n            );\n            unsafe { element.set_dirty_descendants() }\n        }\n\n        data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid\n    }\n\n    /// TODO(emilio): Reuse the bucket stuff from selectormap? That handles :is() / :where() etc.\n    fn scan_component(\n        component: &Component<SelectorImpl>,\n        invalidation: &mut Option<Invalidation>,\n    ) {\n        match *component {\n            Component::LocalName(LocalName {\n                ref name,\n                ref lower_name,\n            }) => {\n                if invalidation.is_none() {\n                    *invalidation = Some(Invalidation::LocalName {\n                        name: name.clone(),\n                        lower_name: lower_name.clone(),\n                    });\n                }\n            },\n            Component::Class(ref class) => {\n                if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {\n                    *invalidation = Some(Invalidation::Class(class.clone()));\n                }\n            },\n            Component::ID(ref id) => {\n                if invalidation.as_ref().map_or(true, |s| !s.is_id()) {\n                    *invalidation = Some(Invalidation::ID(id.clone()));\n                }\n            },\n            _ => {\n                // Ignore everything else, at least for now.\n            },\n        }\n    }\n\n    /// Collect invalidations for a given selector.\n    ///\n    /// We look at the outermost local name, class, or ID selector to the left\n    /// of an ancestor combinator, in order to restyle only a given subtree.\n    ///\n    /// If the selector has no ancestor combinator, then we do the same for\n    /// the only sequence it has, but record it as an element invalidation\n    /// instead of a subtree invalidation.\n    ///\n    /// We prefer IDs to classs, and classes to local names, on the basis\n    /// that the former should be more specific than the latter. We also\n    /// prefer to generate subtree invalidations for the outermost part\n    /// of the selector, to reduce the amount of traversal we need to do\n    /// when flushing invalidations.\n    fn collect_invalidations(\n        &mut self,\n        selector: &Selector<SelectorImpl>,\n        quirks_mode: QuirksMode,\n    ) {\n        debug!(\n            \"StylesheetInvalidationSet::collect_invalidations({:?})\",\n            selector\n        );\n\n        let mut element_invalidation: Option<Invalidation> = None;\n        let mut subtree_invalidation: Option<Invalidation> = None;\n\n        let mut scan_for_element_invalidation = true;\n        let mut scan_for_subtree_invalidation = false;\n\n        let mut iter = selector.iter();\n\n        loop {\n            for component in &mut iter {\n                if scan_for_element_invalidation {\n                    Self::scan_component(component, &mut element_invalidation);\n                } else if scan_for_subtree_invalidation {\n                    Self::scan_component(component, &mut subtree_invalidation);\n                }\n            }\n            match iter.next_sequence() {\n                None => break,\n                Some(combinator) => {\n                    scan_for_subtree_invalidation = combinator.is_ancestor();\n                },\n            }\n            scan_for_element_invalidation = false;\n        }\n\n        if let Some(s) = subtree_invalidation {\n            debug!(\" > Found subtree invalidation: {:?}\", s);\n            if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {\n                return;\n            }\n        }\n\n        if let Some(s) = element_invalidation {\n            debug!(\" > Found element invalidation: {:?}\", s);\n            if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {\n                return;\n            }\n        }\n\n        // The selector was of a form that we can't handle. Any element could\n        // match it, so let's just bail out.\n        debug!(\" > Can't handle selector or OOMd, marking fully invalid\");\n        self.invalidate_fully()\n    }\n\n    fn insert_invalidation(\n        &mut self,\n        invalidation: Invalidation,\n        kind: InvalidationKind,\n        quirks_mode: QuirksMode,\n    ) -> bool {\n        match invalidation {\n            Invalidation::Class(c) => {\n                let entry = match self.buckets.classes.try_entry(c.0, quirks_mode) {\n                    Ok(e) => e,\n                    Err(..) => return false,\n                };\n                *entry.or_insert(InvalidationKind::None) |= kind;\n            },\n            Invalidation::ID(i) => {\n                let entry = match self.buckets.ids.try_entry(i.0, quirks_mode) {\n                    Ok(e) => e,\n                    Err(..) => return false,\n                };\n                *entry.or_insert(InvalidationKind::None) |= kind;\n            },\n            Invalidation::LocalName { name, lower_name } => {\n                let insert_lower = name != lower_name;\n                if self.buckets.local_names.try_reserve(1).is_err() {\n                    return false;\n                }\n                let entry = self.buckets.local_names.entry(name);\n                *entry.or_insert(InvalidationKind::None) |= kind;\n                if insert_lower {\n                    if self.buckets.local_names.try_reserve(1).is_err() {\n                        return false;\n                    }\n                    let entry = self.buckets.local_names.entry(lower_name);\n                    *entry.or_insert(InvalidationKind::None) |= kind;\n                }\n            },\n        }\n\n        true\n    }\n\n    /// Collects invalidations for a given CSS rule, if not fully invalid already.\n    pub fn rule_changed<S>(\n        &mut self,\n        stylesheet: &S,\n        rule: &CssRule,\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media: &CustomMediaMap,\n        change_kind: RuleChangeKind,\n        ancestors: &[CssRuleRef],\n    ) where\n        S: StylesheetInDocument,\n    {\n        debug!(\"StylesheetInvalidationSet::rule_changed\");\n        if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, custom_media, guard)\n        {\n            debug!(\" > Stylesheet was not effective\");\n            return; // Nothing to do here.\n        }\n\n        if ancestors\n            .iter()\n            .any(|r| !EffectiveRules::is_effective(guard, device, quirks_mode, custom_media, r))\n        {\n            debug!(\" > Ancestor rules not effective\");\n            return;\n        }\n\n        if change_kind == RuleChangeKind::PositionTryDeclarations {\n            // @position-try declaration changes need to be dealt explicitly, since the\n            // declarations are mutable and we can't otherwise detect changes to them.\n            match *rule {\n                CssRule::PositionTry(ref pt) => {\n                    self.cascade_data_difference\n                        .changed_position_try_names\n                        .insert(pt.read_with(guard).name.0.clone());\n                },\n                _ => debug_assert!(false, \"how did position-try decls change on anything else?\"),\n            }\n            return;\n        }\n\n        if self.style_fully_invalid {\n            return;\n        }\n\n        // If the change is generic, we don't have the old rule information to know e.g., the old\n        // media condition, or the old selector text, so we might need to invalidate more\n        // aggressively. That only applies to the changed rules, for other rules we can just\n        // collect invalidations as normal.\n        let is_generic_change = change_kind == RuleChangeKind::Generic;\n        self.collect_invalidations_for_rule(\n            rule,\n            guard,\n            device,\n            quirks_mode,\n            is_generic_change,\n            ancestors,\n        );\n        if self.style_fully_invalid {\n            return;\n        }\n\n        if !is_generic_change\n            && !EffectiveRules::is_effective(guard, device, quirks_mode, custom_media, &rule.into())\n        {\n            return;\n        }\n\n        let rules = EffectiveRulesIterator::effective_children(\n            device,\n            quirks_mode,\n            custom_media,\n            guard,\n            rule,\n        );\n        for rule in rules {\n            self.collect_invalidations_for_rule(\n                rule,\n                guard,\n                device,\n                quirks_mode,\n                /* is_generic_change = */ false,\n                // Note(dshin): Technically, the iterator should provide the ancestor chain as it traverses down, which sould be appended to `ancestors`, but it shouldn't matter.\n                &[],\n            );\n            if self.style_fully_invalid {\n                break;\n            }\n        }\n    }\n\n    /// Collects invalidations for a given CSS rule.\n    fn collect_invalidations_for_rule(\n        &mut self,\n        rule: &CssRule,\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        is_generic_change: bool,\n        ancestors: &[CssRuleRef],\n    ) {\n        use crate::stylesheets::CssRule::*;\n        debug!(\"StylesheetInvalidationSet::collect_invalidations_for_rule\");\n        debug_assert!(!self.style_fully_invalid, \"Not worth being here!\");\n\n        match *rule {\n            Style(ref lock) => {\n                if is_generic_change {\n                    // TODO(emilio): We need to do this for selector / keyframe\n                    // name / font-face changes, because we don't have the old\n                    // selector / name.  If we distinguish those changes\n                    // specially, then we can at least use this invalidation for\n                    // style declaration changes.\n                    return self.invalidate_fully();\n                }\n\n                let style_rule = lock.read_with(guard);\n                for selector in style_rule.selectors.slice() {\n                    self.collect_invalidations(selector, quirks_mode);\n                    if self.style_fully_invalid {\n                        return;\n                    }\n                }\n            },\n            NestedDeclarations(..) => {\n                if ancestors.iter().any(|r| matches!(r, CssRuleRef::Scope(_))) {\n                    self.invalidate_fully();\n                }\n            },\n            Namespace(..) => {\n                // It's not clear what handling changes for this correctly would\n                // look like.\n            },\n            LayerStatement(..) => {\n                // Layer statement insertions might alter styling order, so we need to always\n                // invalidate fully.\n                return self.invalidate_fully();\n            },\n            Document(..) | Import(..) | Media(..) | Supports(..) | Container(..)\n            | LayerBlock(..) | StartingStyle(..) | AppearanceBase(..) => {\n                // Do nothing, relevant nested rules are visited as part of rule iteration.\n            },\n            FontFace(..) => {\n                // Do nothing, @font-face doesn't affect computed style information on it's own.\n                // We'll restyle when the font face loads, if needed.\n            },\n            Page(..) | Margin(..) => {\n                // Do nothing, we don't support OM mutations on print documents, and page rules\n                // can't affect anything else.\n            },\n            Keyframes(ref lock) => {\n                if is_generic_change {\n                    return self.invalidate_fully();\n                }\n                let keyframes_rule = lock.read_with(guard);\n                if device.animation_name_may_be_referenced(&keyframes_rule.name) {\n                    debug!(\n                        \" > Found @keyframes rule potentially referenced \\\n                         from the page, marking the whole tree invalid.\"\n                    );\n                    self.invalidate_fully();\n                } else {\n                    // Do nothing, this animation can't affect the style of existing elements.\n                }\n            },\n            CounterStyle(..) | Property(..) | FontFeatureValues(..) | FontPaletteValues(..) => {\n                debug!(\" > Found unsupported rule, marking the whole subtree invalid.\");\n                self.invalidate_fully();\n            },\n            Scope(..) => {\n                // Addition/removal of @scope requires re-evaluation of scope proximity to properly\n                // figure out the styling order.\n                self.invalidate_fully();\n            },\n            PositionTry(..) => {\n                // @position-try changes doesn't change style-time information (only layout\n                // information) and is handled by invalidate_position_try. So do nothing.\n            },\n            ViewTransition(..) => {\n                // @view-transition doesn't affect element styles.\n            },\n            CustomMedia(..) => {\n                // @custom-media might be referenced by other rules which we can't get a hand on in\n                // here, so we don't know which elements are affected.\n                //\n                // TODO: Maybe track referenced custom-media rules like we do for @keyframe?\n                self.invalidate_fully();\n            },\n        }\n    }\n}\n\n/// Invalidates for any absolutely positioned element that references the given @position-try fallback names.\npub fn invalidate_position_try<E>(\n    element: E,\n    changed_names: &PrecomputedHashSet<Atom>,\n    invalidate_self: &mut impl FnMut(E, &mut ElementData),\n    invalidated_descendants: &mut impl FnMut(E),\n) -> bool\nwhere\n    E: TElement,\n{\n    debug_assert!(\n        !changed_names.is_empty(),\n        \"Don't call me if there's nothing to do\"\n    );\n    let mut data = match element.mutate_data() {\n        Some(data) => data,\n        None => return false,\n    };\n\n    let mut self_invalid = false;\n    let style = data.styles.primary();\n    if style.clone_position().is_absolutely_positioned() {\n        let fallbacks = style.clone_position_try_fallbacks();\n        let referenced = fallbacks.0.iter().any(|f| match f {\n            PositionTryFallbacksItem::IdentAndOrTactic(ident_or_tactic) => {\n                changed_names.contains(&ident_or_tactic.ident.0)\n            },\n            PositionTryFallbacksItem::PositionArea(..) => false,\n        });\n\n        if referenced {\n            self_invalid = true;\n            invalidate_self(element, &mut data);\n        }\n    }\n    let mut any_children_invalid = false;\n    for child in element.traversal_children() {\n        let Some(e) = child.as_element() else {\n            continue;\n        };\n        any_children_invalid |=\n            invalidate_position_try(e, changed_names, invalidate_self, invalidated_descendants);\n    }\n    if any_children_invalid {\n        invalidated_descendants(element);\n    }\n    self_invalid || any_children_invalid\n}\n"
  },
  {
    "path": "style/invalidation/viewport_units.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Invalidates style of all elements that depend on viewport units.\n\nuse crate::data::ViewportUnitUsage;\nuse crate::dom::{TElement, TNode};\nuse crate::invalidation::element::restyle_hints::RestyleHint;\n\n/// Invalidates style of all elements that depend on viewport units.\n///\n/// Returns whether any element was invalidated.\npub fn invalidate<E>(root: E) -> bool\nwhere\n    E: TElement,\n{\n    debug!(\"invalidation::viewport_units::invalidate({:?})\", root);\n    invalidate_recursively(root)\n}\n\nfn invalidate_recursively<E>(element: E) -> bool\nwhere\n    E: TElement,\n{\n    let mut data = match element.mutate_data() {\n        Some(data) => data,\n        None => return false,\n    };\n\n    if data.hint.will_recascade_subtree() {\n        debug!(\"invalidate_recursively: {:?} was already invalid\", element);\n        return false;\n    }\n\n    let usage = data.styles.viewport_unit_usage();\n    let uses_viewport_units = usage != ViewportUnitUsage::None;\n    if uses_viewport_units {\n        debug!(\n            \"invalidate_recursively: {:?} uses viewport units {:?}\",\n            element, usage\n        );\n    }\n\n    match usage {\n        ViewportUnitUsage::None => {},\n        ViewportUnitUsage::FromQuery => {\n            data.hint.insert(RestyleHint::RESTYLE_SELF);\n        },\n        ViewportUnitUsage::FromDeclaration => {\n            data.hint.insert(RestyleHint::RECASCADE_SELF);\n        },\n    }\n\n    let mut any_children_invalid = false;\n    for child in element.traversal_children() {\n        if let Some(child) = child.as_element() {\n            any_children_invalid |= invalidate_recursively(child);\n        }\n    }\n\n    if any_children_invalid {\n        debug!(\n            \"invalidate_recursively: Children of {:?} changed, setting dirty descendants\",\n            element\n        );\n        unsafe { element.set_dirty_descendants() }\n    }\n\n    uses_viewport_units || any_children_invalid\n}\n"
  },
  {
    "path": "style/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Calculate [specified][specified] and [computed values][computed] from a\n//! tree of DOM nodes and a set of stylesheets.\n//!\n//! [computed]: https://drafts.csswg.org/css-cascade/#computed\n//! [specified]: https://drafts.csswg.org/css-cascade/#specified\n//!\n//! In particular, this crate contains the definitions of supported properties,\n//! the code to parse them into specified values and calculate the computed\n//! values based on the specified values, as well as the code to serialize both\n//! specified and computed values.\n//!\n//! The main entry point is [`recalc_style_at`][recalc_style_at].\n//!\n//! [recalc_style_at]: traversal/fn.recalc_style_at.html\n//!\n//! A list of supported style properties can be found as [docs::supported_properties]\n//!\n//! Major dependencies are the [cssparser][cssparser] and [selectors][selectors]\n//! crates.\n//!\n//! [cssparser]: ../cssparser/index.html\n//! [selectors]: ../selectors/index.html\n\n#![deny(missing_docs)]\n\npub(crate) use cssparser;\n\n#[macro_use]\nextern crate bitflags;\n#[macro_use]\n#[cfg(feature = \"gecko\")]\nextern crate gecko_profiler;\n#[cfg(feature = \"gecko\")]\n#[macro_use]\npub mod gecko_string_cache;\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde;\npub use servo_arc;\n#[cfg(feature = \"servo\")]\n#[macro_use]\nextern crate stylo_atoms;\n#[macro_use]\nextern crate static_assertions;\n\n#[macro_use]\nmod macros;\n\nmod derives {\n    pub(crate) use derive_more::{Add, AddAssign, Deref, DerefMut, From};\n    pub(crate) use malloc_size_of_derive::MallocSizeOf;\n    pub(crate) use num_derive::FromPrimitive;\n    pub(crate) use style_derive::{\n        Animate, ComputeSquaredDistance, Parse, SpecifiedValueInfo, ToAnimatedValue,\n        ToAnimatedZero, ToComputedValue, ToCss, ToResolvedValue, ToTyped,\n    };\n    pub(crate) use to_shmem_derive::ToShmem;\n}\n\npub mod applicable_declarations;\npub mod author_styles;\npub mod bezier;\npub mod bloom;\npub mod color;\n#[path = \"properties/computed_value_flags.rs\"]\npub mod computed_value_flags;\npub mod context;\npub mod counter_style;\npub mod custom_properties;\npub mod custom_properties_map;\npub mod data;\npub mod device;\npub mod dom;\npub mod dom_apis;\npub mod driver;\npub mod error_reporting;\npub mod font_face;\npub mod font_metrics;\n#[cfg(feature = \"gecko\")]\n#[allow(unsafe_code)]\npub mod gecko_bindings;\npub mod global_style_data;\npub mod invalidation;\n#[allow(missing_docs)] // TODO.\npub mod logical_geometry;\npub mod matching;\npub mod media_queries;\npub mod parallel;\npub mod parser;\npub mod piecewise_linear;\npub mod properties_and_values;\n#[macro_use]\npub mod queries;\npub mod rule_cache;\npub mod rule_collector;\npub mod rule_tree;\npub mod scoped_tls;\npub mod selector_map;\npub mod selector_parser;\npub mod shared_lock;\npub mod sharing;\nmod simple_buckets_map;\npub mod str;\npub mod style_adjuster;\npub mod style_resolver;\npub mod stylesheet_set;\npub mod stylesheets;\npub mod stylist;\npub mod thread_state;\npub mod traversal;\npub mod traversal_flags;\npub mod typed_om;\npub mod use_counters;\n\n#[macro_use]\n#[allow(non_camel_case_types)]\npub mod values;\n\n#[cfg(all(doc, feature = \"servo\"))]\n/// Documentation\npub mod docs {\n    /// The CSS properties supported by the style system.\n    /// Generated from the `properties.mako.rs` template by `build.rs`\n    pub mod supported_properties {\n        #![doc = include_str!(concat!(env!(\"OUT_DIR\"), \"/css-properties.html\"))]\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko_string_cache as string_cache;\n#[cfg(feature = \"gecko\")]\npub use crate::gecko_string_cache::Atom;\n/// The namespace prefix type for Gecko, which is just an atom.\n#[cfg(feature = \"gecko\")]\npub type Prefix = crate::values::AtomIdent;\n/// The local name of an element for Gecko, which is just an atom.\n#[cfg(feature = \"gecko\")]\npub type LocalName = crate::values::AtomIdent;\n#[cfg(feature = \"gecko\")]\npub use crate::gecko_string_cache::Namespace;\n\n#[cfg(feature = \"servo\")]\npub use stylo_atoms::Atom;\n\n#[cfg(feature = \"servo\")]\n#[allow(missing_docs)]\npub type LocalName = crate::values::GenericAtomIdent<web_atoms::LocalNameStaticSet>;\n#[cfg(feature = \"servo\")]\n#[allow(missing_docs)]\npub type Namespace = crate::values::GenericAtomIdent<web_atoms::NamespaceStaticSet>;\n#[cfg(feature = \"servo\")]\n#[allow(missing_docs)]\npub type Prefix = crate::values::GenericAtomIdent<web_atoms::PrefixStaticSet>;\n\npub use style_traits::arc_slice::ArcSlice;\npub use style_traits::owned_slice::OwnedSlice;\npub use style_traits::owned_str::OwnedStr;\n\nuse std::hash::{BuildHasher, Hash};\n\n#[cfg_attr(feature = \"servo\", macro_use)]\npub mod properties;\n\n#[cfg(feature = \"gecko\")]\n#[allow(unsafe_code)]\npub mod gecko;\n\n// uses a macro from properties\n#[cfg(feature = \"servo\")]\n#[allow(unsafe_code)]\npub mod servo;\n#[cfg(feature = \"servo\")]\npub use servo::{animation, attr};\n\nmacro_rules! reexport_computed_values {\n    ( $( { $name: ident } )+ ) => {\n        /// Types for [computed values][computed].\n        ///\n        /// [computed]: https://drafts.csswg.org/css-cascade/#computed\n        pub mod computed_values {\n            $(\n                pub use crate::properties::longhands::$name::computed_value as $name;\n            )+\n            // Don't use a side-specific name needlessly:\n            pub use crate::properties::longhands::border_top_style::computed_value as border_style;\n        }\n    }\n}\nlonghand_properties_idents!(reexport_computed_values);\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_string_cache::WeakAtom;\n#[cfg(feature = \"servo\")]\nuse stylo_atoms::Atom as WeakAtom;\n\n/// Extension methods for selectors::attr::CaseSensitivity\npub trait CaseSensitivityExt {\n    /// Return whether two atoms compare equal according to this case sensitivity.\n    fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool;\n}\n\nimpl CaseSensitivityExt for selectors::attr::CaseSensitivity {\n    #[inline]\n    fn eq_atom(self, a: &WeakAtom, b: &WeakAtom) -> bool {\n        match self {\n            selectors::attr::CaseSensitivity::CaseSensitive => a == b,\n            selectors::attr::CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b),\n        }\n    }\n}\n\n/// A trait pretty much similar to num_traits::Zero, but without the need of\n/// implementing `Add`.\npub trait Zero {\n    /// Returns the zero value.\n    fn zero() -> Self;\n\n    /// Returns whether this value is zero.\n    fn is_zero(&self) -> bool;\n}\n\nimpl<T> Zero for T\nwhere\n    T: num_traits::Zero,\n{\n    fn zero() -> Self {\n        <Self as num_traits::Zero>::zero()\n    }\n\n    fn is_zero(&self) -> bool {\n        <Self as num_traits::Zero>::is_zero(self)\n    }\n}\n\n/// A trait implementing a function to tell if the number is zero without a percent\npub trait ZeroNoPercent {\n    /// So, `0px` should return `true`, but `0%` or `1px` should return `false`\n    fn is_zero_no_percent(&self) -> bool;\n}\n\n/// A trait pretty much similar to num_traits::One, but without the need of\n/// implementing `Mul`.\npub trait One {\n    /// Reutrns the one value.\n    fn one() -> Self;\n\n    /// Returns whether this value is one.\n    fn is_one(&self) -> bool;\n}\n\nimpl<T> One for T\nwhere\n    T: num_traits::One + PartialEq,\n{\n    fn one() -> Self {\n        <Self as num_traits::One>::one()\n    }\n\n    fn is_one(&self) -> bool {\n        *self == One::one()\n    }\n}\n\n/// An allocation error.\n///\n/// TODO(emilio): Would be nice to have more information here, or for SmallVec\n/// to return the standard error type (and then we can just return that).\n///\n/// But given we use these mostly to bail out and ignore them, it's not a big\n/// deal.\n#[derive(Debug)]\npub struct AllocErr;\n\nimpl From<smallvec::CollectionAllocErr> for AllocErr {\n    #[inline]\n    fn from(_: smallvec::CollectionAllocErr) -> Self {\n        Self\n    }\n}\n\nimpl From<std::collections::TryReserveError> for AllocErr {\n    #[inline]\n    fn from(_: std::collections::TryReserveError) -> Self {\n        Self\n    }\n}\n\n/// Shrink the capacity of the collection if needed.\npub(crate) trait ShrinkIfNeeded {\n    fn shrink_if_needed(&mut self);\n}\n\n/// We shrink the capacity of a collection if we're wasting more than a 25% of\n/// its capacity, and if the collection is arbitrarily big enough\n/// (>= CAPACITY_THRESHOLD entries).\n#[inline]\nfn should_shrink(len: usize, capacity: usize) -> bool {\n    const CAPACITY_THRESHOLD: usize = 64;\n    capacity >= CAPACITY_THRESHOLD && len + capacity / 4 < capacity\n}\n\nimpl<K, V, H> ShrinkIfNeeded for std::collections::HashMap<K, V, H>\nwhere\n    K: Eq + Hash,\n    H: BuildHasher,\n{\n    fn shrink_if_needed(&mut self) {\n        if should_shrink(self.len(), self.capacity()) {\n            self.shrink_to_fit();\n        }\n    }\n}\n\nimpl<T, H> ShrinkIfNeeded for std::collections::HashSet<T, H>\nwhere\n    T: Eq + Hash,\n    H: BuildHasher,\n{\n    fn shrink_if_needed(&mut self) {\n        if should_shrink(self.len(), self.capacity()) {\n            self.shrink_to_fit();\n        }\n    }\n}\n\n// TODO(emilio): Measure and see if we're wasting a lot of memory on Vec /\n// SmallVec, and if so consider shrinking those as well.\n"
  },
  {
    "path": "style/logical_geometry.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Geometry in flow-relative space.\n\nuse crate::derives::*;\nuse crate::properties::style_structs;\nuse euclid::default::{Point2D, Rect, SideOffsets2D, Size2D};\nuse euclid::num::Zero;\nuse std::cmp::{max, min};\nuse std::fmt::{self, Debug, Error, Formatter};\nuse std::ops::{Add, Sub};\n\npub enum BlockFlowDirection {\n    TopToBottom,\n    RightToLeft,\n    LeftToRight,\n}\n\npub enum InlineBaseDirection {\n    LeftToRight,\n    RightToLeft,\n}\n\n/// The writing-mode property (different from the WritingMode enum).\n/// https://drafts.csswg.org/css-writing-modes/#block-flow\n/// Aliases come from https://drafts.csswg.org/css-writing-modes-4/#svg-writing-mode\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum WritingModeProperty {\n    #[parse(aliases = \"lr,lr-tb,rl,rl-tb\")]\n    HorizontalTb,\n    #[parse(aliases = \"tb,tb-rl\")]\n    VerticalRl,\n    VerticalLr,\n    #[cfg(feature = \"gecko\")]\n    SidewaysRl,\n    #[cfg(feature = \"gecko\")]\n    SidewaysLr,\n}\n\n// TODO: improve the readability of the WritingMode serialization, refer to the Debug:fmt()\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, Serialize)]\n#[repr(C)]\npub struct WritingMode(u8);\nbitflags!(\n    impl WritingMode: u8 {\n        /// A vertical writing mode; writing-mode is vertical-rl,\n        /// vertical-lr, sideways-lr, or sideways-rl.\n        const VERTICAL = 1 << 0;\n        /// The inline flow direction is reversed against the physical\n        /// direction (i.e. right-to-left or bottom-to-top); writing-mode is\n        /// sideways-lr or direction is rtl (but not both).\n        ///\n        /// (This bit can be derived from the others, but we store it for\n        /// convenience.)\n        const INLINE_REVERSED = 1 << 1;\n        /// A vertical writing mode whose block progression direction is left-\n        /// to-right; writing-mode is vertical-lr or sideways-lr.\n        ///\n        /// Never set without VERTICAL.\n        const VERTICAL_LR = 1 << 2;\n        /// The line-over/line-under sides are inverted with respect to the\n        /// block-start/block-end edge; writing-mode is vertical-lr.\n        ///\n        /// Never set without VERTICAL and VERTICAL_LR.\n        const LINE_INVERTED = 1 << 3;\n        /// direction is rtl.\n        const RTL = 1 << 4;\n        /// All text within a vertical writing mode is displayed sideways\n        /// and runs top-to-bottom or bottom-to-top; set in these cases:\n        ///\n        /// * writing-mode: sideways-rl;\n        /// * writing-mode: sideways-lr;\n        ///\n        /// Never set without VERTICAL.\n        const VERTICAL_SIDEWAYS = 1 << 5;\n        /// Similar to VERTICAL_SIDEWAYS, but is set via text-orientation;\n        /// set in these cases:\n        ///\n        /// * writing-mode: vertical-rl; text-orientation: sideways;\n        /// * writing-mode: vertical-lr; text-orientation: sideways;\n        ///\n        /// Never set without VERTICAL.\n        const TEXT_SIDEWAYS = 1 << 6;\n        /// Horizontal text within a vertical writing mode is displayed with each\n        /// glyph upright; set in these cases:\n        ///\n        /// * writing-mode: vertical-rl; text-orientation: upright;\n        /// * writing-mode: vertical-lr: text-orientation: upright;\n        ///\n        /// Never set without VERTICAL.\n        const UPRIGHT = 1 << 7;\n        /// Writing mode combinations that can be specified in CSS.\n        ///\n        /// * writing-mode: horizontal-tb;\n        const WRITING_MODE_HORIZONTAL_TB = 0;\n        /// * writing-mode: vertical_rl;\n        const WRITING_MODE_VERTICAL_RL = WritingMode::VERTICAL.bits();\n        /// * writing-mode: vertical-lr;\n        const WRITING_MODE_VERTICAL_LR = WritingMode::VERTICAL.bits() |\n                                         WritingMode::VERTICAL_LR.bits() |\n                                         WritingMode::LINE_INVERTED.bits();\n        /// * writing-mode: sideways-rl;\n        const WRITING_MODE_SIDEWAYS_RL = WritingMode::VERTICAL.bits() |\n                                         WritingMode::VERTICAL_SIDEWAYS.bits();\n        /// * writing-mode: sideways-lr;\n        const WRITING_MODE_SIDEWAYS_LR = WritingMode::VERTICAL.bits() |\n                                         WritingMode::VERTICAL_LR.bits() |\n                                         WritingMode::VERTICAL_SIDEWAYS.bits();\n    }\n);\n\nimpl WritingMode {\n    /// Return a WritingMode bitflags from the relevant CSS properties.\n    pub fn new(inheritedbox_style: &style_structs::InheritedBox) -> Self {\n        use crate::properties::longhands::direction::computed_value::T as Direction;\n\n        let mut flags = WritingMode::empty();\n\n        let direction = inheritedbox_style.clone_direction();\n        let writing_mode = inheritedbox_style.clone_writing_mode();\n\n        match direction {\n            Direction::Ltr => {},\n            Direction::Rtl => {\n                flags.insert(WritingMode::RTL);\n            },\n        }\n\n        match writing_mode {\n            WritingModeProperty::HorizontalTb => {\n                if direction == Direction::Rtl {\n                    flags.insert(WritingMode::INLINE_REVERSED);\n                }\n            },\n            WritingModeProperty::VerticalRl => {\n                flags.insert(WritingMode::WRITING_MODE_VERTICAL_RL);\n                if direction == Direction::Rtl {\n                    flags.insert(WritingMode::INLINE_REVERSED);\n                }\n            },\n            WritingModeProperty::VerticalLr => {\n                flags.insert(WritingMode::WRITING_MODE_VERTICAL_LR);\n                if direction == Direction::Rtl {\n                    flags.insert(WritingMode::INLINE_REVERSED);\n                }\n            },\n            #[cfg(feature = \"gecko\")]\n            WritingModeProperty::SidewaysRl => {\n                flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_RL);\n                if direction == Direction::Rtl {\n                    flags.insert(WritingMode::INLINE_REVERSED);\n                }\n            },\n            #[cfg(feature = \"gecko\")]\n            WritingModeProperty::SidewaysLr => {\n                flags.insert(WritingMode::WRITING_MODE_SIDEWAYS_LR);\n                if direction == Direction::Ltr {\n                    flags.insert(WritingMode::INLINE_REVERSED);\n                }\n            },\n        }\n\n        #[cfg(feature = \"gecko\")]\n        {\n            use crate::properties::longhands::text_orientation::computed_value::T as TextOrientation;\n\n            // text-orientation only has an effect for vertical-rl and\n            // vertical-lr values of writing-mode.\n            match writing_mode {\n                WritingModeProperty::VerticalRl | WritingModeProperty::VerticalLr => {\n                    match inheritedbox_style.clone_text_orientation() {\n                        TextOrientation::Mixed => {},\n                        TextOrientation::Upright => {\n                            flags.insert(WritingMode::UPRIGHT);\n\n                            // https://drafts.csswg.org/css-writing-modes-3/#valdef-text-orientation-upright:\n                            //\n                            // > This value causes the used value of direction\n                            // > to be ltr, and for the purposes of bidi\n                            // > reordering, causes all characters to be treated\n                            // > as strong LTR.\n                            flags.remove(WritingMode::RTL);\n                            flags.remove(WritingMode::INLINE_REVERSED);\n                        },\n                        TextOrientation::Sideways => {\n                            flags.insert(WritingMode::TEXT_SIDEWAYS);\n                        },\n                    }\n                },\n                _ => {},\n            }\n        }\n\n        flags\n    }\n\n    /// Returns the `horizontal-tb` value.\n    pub fn horizontal_tb() -> Self {\n        Self::empty()\n    }\n\n    #[inline]\n    pub fn is_vertical(&self) -> bool {\n        self.intersects(WritingMode::VERTICAL)\n    }\n\n    #[inline]\n    pub fn is_vertical_rl(&self) -> bool {\n        self.is_vertical() && !self.is_vertical_lr()\n    }\n\n    #[inline]\n    pub fn is_horizontal(&self) -> bool {\n        !self.is_vertical()\n    }\n\n    /// Assuming .is_vertical(), does the block direction go left to right?\n    #[inline]\n    pub fn is_vertical_lr(&self) -> bool {\n        self.intersects(WritingMode::VERTICAL_LR)\n    }\n\n    /// Assuming .is_vertical(), does the inline direction go top to bottom?\n    #[inline]\n    pub fn is_inline_tb(&self) -> bool {\n        // https://drafts.csswg.org/css-writing-modes-3/#logical-to-physical\n        !self.intersects(WritingMode::INLINE_REVERSED)\n    }\n\n    #[inline]\n    pub fn is_bidi_ltr(&self) -> bool {\n        !self.intersects(WritingMode::RTL)\n    }\n\n    #[inline]\n    pub fn is_sideways(&self) -> bool {\n        self.intersects(WritingMode::VERTICAL_SIDEWAYS | WritingMode::TEXT_SIDEWAYS)\n    }\n\n    #[inline]\n    pub fn is_upright(&self) -> bool {\n        self.intersects(WritingMode::UPRIGHT)\n    }\n\n    /// https://drafts.csswg.org/css-writing-modes/#logical-to-physical\n    ///\n    /// | Return  | line-left is… | line-right is… |\n    /// |---------|---------------|----------------|\n    /// | `true`  | inline-start  | inline-end     |\n    /// | `false` | inline-end    | inline-start   |\n    #[inline]\n    pub fn line_left_is_inline_start(&self) -> bool {\n        // https://drafts.csswg.org/css-writing-modes/#inline-start\n        // “For boxes with a used direction value of ltr, this means the line-left side.\n        //  For boxes with a used direction value of rtl, this means the line-right side.”\n        self.is_bidi_ltr()\n    }\n\n    #[inline]\n    pub fn inline_start_physical_side(&self) -> PhysicalSide {\n        match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {\n            (false, _, true) => PhysicalSide::Left,\n            (false, _, false) => PhysicalSide::Right,\n            (true, true, _) => PhysicalSide::Top,\n            (true, false, _) => PhysicalSide::Bottom,\n        }\n    }\n\n    #[inline]\n    pub fn inline_end_physical_side(&self) -> PhysicalSide {\n        match (self.is_vertical(), self.is_inline_tb(), self.is_bidi_ltr()) {\n            (false, _, true) => PhysicalSide::Right,\n            (false, _, false) => PhysicalSide::Left,\n            (true, true, _) => PhysicalSide::Bottom,\n            (true, false, _) => PhysicalSide::Top,\n        }\n    }\n\n    #[inline]\n    pub fn block_start_physical_side(&self) -> PhysicalSide {\n        match (self.is_vertical(), self.is_vertical_lr()) {\n            (false, _) => PhysicalSide::Top,\n            (true, true) => PhysicalSide::Left,\n            (true, false) => PhysicalSide::Right,\n        }\n    }\n\n    #[inline]\n    pub fn block_end_physical_side(&self) -> PhysicalSide {\n        match (self.is_vertical(), self.is_vertical_lr()) {\n            (false, _) => PhysicalSide::Bottom,\n            (true, true) => PhysicalSide::Right,\n            (true, false) => PhysicalSide::Left,\n        }\n    }\n\n    /// Given a physical side, flips the start on that axis, and returns the corresponding\n    /// physical side.\n    #[inline]\n    pub fn flipped_start_side(&self, side: PhysicalSide) -> PhysicalSide {\n        let bs = self.block_start_physical_side();\n        if side == bs {\n            return self.inline_start_physical_side();\n        }\n        let be = self.block_end_physical_side();\n        if side == be {\n            return self.inline_end_physical_side();\n        }\n        if side == self.inline_start_physical_side() {\n            return bs;\n        }\n        debug_assert_eq!(side, self.inline_end_physical_side());\n        be\n    }\n\n    #[inline]\n    pub fn start_start_physical_corner(&self) -> PhysicalCorner {\n        PhysicalCorner::from_sides(\n            self.block_start_physical_side(),\n            self.inline_start_physical_side(),\n        )\n    }\n\n    #[inline]\n    pub fn start_end_physical_corner(&self) -> PhysicalCorner {\n        PhysicalCorner::from_sides(\n            self.block_start_physical_side(),\n            self.inline_end_physical_side(),\n        )\n    }\n\n    #[inline]\n    pub fn end_start_physical_corner(&self) -> PhysicalCorner {\n        PhysicalCorner::from_sides(\n            self.block_end_physical_side(),\n            self.inline_start_physical_side(),\n        )\n    }\n\n    #[inline]\n    pub fn end_end_physical_corner(&self) -> PhysicalCorner {\n        PhysicalCorner::from_sides(\n            self.block_end_physical_side(),\n            self.inline_end_physical_side(),\n        )\n    }\n\n    #[inline]\n    pub fn block_flow_direction(&self) -> BlockFlowDirection {\n        match (self.is_vertical(), self.is_vertical_lr()) {\n            (false, _) => BlockFlowDirection::TopToBottom,\n            (true, true) => BlockFlowDirection::LeftToRight,\n            (true, false) => BlockFlowDirection::RightToLeft,\n        }\n    }\n\n    #[inline]\n    pub fn inline_base_direction(&self) -> InlineBaseDirection {\n        if self.intersects(WritingMode::RTL) {\n            InlineBaseDirection::RightToLeft\n        } else {\n            InlineBaseDirection::LeftToRight\n        }\n    }\n\n    #[inline]\n    /// Is the text layout vertical?\n    pub fn is_text_vertical(&self) -> bool {\n        self.is_vertical() && !self.is_sideways()\n    }\n}\n\nimpl fmt::Display for WritingMode {\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        if self.is_vertical() {\n            write!(formatter, \"V\")?;\n            if self.is_vertical_lr() {\n                write!(formatter, \" LR\")?;\n            } else {\n                write!(formatter, \" RL\")?;\n            }\n            if self.is_sideways() {\n                write!(formatter, \" Sideways\")?;\n            }\n            if self.intersects(WritingMode::LINE_INVERTED) {\n                write!(formatter, \" Inverted\")?;\n            }\n        } else {\n            write!(formatter, \"H\")?;\n        }\n        if self.is_bidi_ltr() {\n            write!(formatter, \" LTR\")\n        } else {\n            write!(formatter, \" RTL\")\n        }\n    }\n}\n\n/// Wherever logical geometry is used, the writing mode is known based on context:\n/// every method takes a `mode` parameter.\n/// However, this context is easy to get wrong.\n/// In debug builds only, logical geometry objects store their writing mode\n/// (in addition to taking it as a parameter to methods) and check it.\n/// In non-debug builds, make this storage zero-size and the checks no-ops.\n#[cfg(not(debug_assertions))]\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\nstruct DebugWritingMode;\n\n#[cfg(debug_assertions)]\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\nstruct DebugWritingMode {\n    mode: WritingMode,\n}\n\n#[cfg(not(debug_assertions))]\nimpl DebugWritingMode {\n    #[inline]\n    fn check(&self, _other: WritingMode) {}\n\n    #[inline]\n    fn check_debug(&self, _other: DebugWritingMode) {}\n\n    #[inline]\n    fn new(_mode: WritingMode) -> DebugWritingMode {\n        DebugWritingMode\n    }\n}\n\n#[cfg(debug_assertions)]\nimpl DebugWritingMode {\n    #[inline]\n    fn check(&self, other: WritingMode) {\n        assert_eq!(self.mode, other)\n    }\n\n    #[inline]\n    fn check_debug(&self, other: DebugWritingMode) {\n        assert_eq!(self.mode, other.mode)\n    }\n\n    #[inline]\n    fn new(mode: WritingMode) -> DebugWritingMode {\n        DebugWritingMode { mode }\n    }\n}\n\nimpl Debug for DebugWritingMode {\n    #[cfg(not(debug_assertions))]\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        write!(formatter, \"?\")\n    }\n\n    #[cfg(debug_assertions)]\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        write!(formatter, \"{}\", self.mode)\n    }\n}\n\n// Used to specify the logical direction.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\npub enum Direction {\n    Inline,\n    Block,\n}\n\n/// A 2D size in flow-relative dimensions\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\npub struct LogicalSize<T> {\n    pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure\n    pub block: T,  // block-size, a.k.a. logical height, a.k.a. extent\n    debug_writing_mode: DebugWritingMode,\n}\n\nimpl<T: Debug> Debug for LogicalSize<T> {\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        write!(\n            formatter,\n            \"LogicalSize({:?}, i{:?}×b{:?})\",\n            self.debug_writing_mode, self.inline, self.block\n        )\n    }\n}\n\n// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.\nimpl<T: Zero> LogicalSize<T> {\n    #[inline]\n    pub fn zero(mode: WritingMode) -> LogicalSize<T> {\n        LogicalSize {\n            inline: Zero::zero(),\n            block: Zero::zero(),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T> LogicalSize<T> {\n    #[inline]\n    pub fn new(mode: WritingMode, inline: T, block: T) -> LogicalSize<T> {\n        LogicalSize {\n            inline: inline,\n            block: block,\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n\n    #[inline]\n    pub fn from_physical(mode: WritingMode, size: Size2D<T>) -> LogicalSize<T> {\n        if mode.is_vertical() {\n            LogicalSize::new(mode, size.height, size.width)\n        } else {\n            LogicalSize::new(mode, size.width, size.height)\n        }\n    }\n}\n\nimpl<T: Clone> LogicalSize<T> {\n    #[inline]\n    pub fn width(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.block.clone()\n        } else {\n            self.inline.clone()\n        }\n    }\n\n    #[inline]\n    pub fn set_width(&mut self, mode: WritingMode, width: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.block = width\n        } else {\n            self.inline = width\n        }\n    }\n\n    #[inline]\n    pub fn height(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.inline.clone()\n        } else {\n            self.block.clone()\n        }\n    }\n\n    #[inline]\n    pub fn set_height(&mut self, mode: WritingMode, height: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.inline = height\n        } else {\n            self.block = height\n        }\n    }\n\n    #[inline]\n    pub fn to_physical(&self, mode: WritingMode) -> Size2D<T> {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            Size2D::new(self.block.clone(), self.inline.clone())\n        } else {\n            Size2D::new(self.inline.clone(), self.block.clone())\n        }\n    }\n\n    #[inline]\n    pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalSize<T> {\n        if mode_from == mode_to {\n            self.debug_writing_mode.check(mode_from);\n            self.clone()\n        } else {\n            LogicalSize::from_physical(mode_to, self.to_physical(mode_from))\n        }\n    }\n}\n\nimpl<T: Add<T, Output = T>> Add for LogicalSize<T> {\n    type Output = LogicalSize<T>;\n\n    #[inline]\n    fn add(self, other: LogicalSize<T>) -> LogicalSize<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalSize {\n            debug_writing_mode: self.debug_writing_mode,\n            inline: self.inline + other.inline,\n            block: self.block + other.block,\n        }\n    }\n}\n\nimpl<T: Sub<T, Output = T>> Sub for LogicalSize<T> {\n    type Output = LogicalSize<T>;\n\n    #[inline]\n    fn sub(self, other: LogicalSize<T>) -> LogicalSize<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalSize {\n            debug_writing_mode: self.debug_writing_mode,\n            inline: self.inline - other.inline,\n            block: self.block - other.block,\n        }\n    }\n}\n\n/// A 2D point in flow-relative dimensions\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\npub struct LogicalPoint<T> {\n    /// inline-axis coordinate\n    pub i: T,\n    /// block-axis coordinate\n    pub b: T,\n    debug_writing_mode: DebugWritingMode,\n}\n\nimpl<T: Debug> Debug for LogicalPoint<T> {\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        write!(\n            formatter,\n            \"LogicalPoint({:?} (i{:?}, b{:?}))\",\n            self.debug_writing_mode, self.i, self.b\n        )\n    }\n}\n\n// Can not implement the Zero trait: its zero() method does not have the `mode` parameter.\nimpl<T: Zero> LogicalPoint<T> {\n    #[inline]\n    pub fn zero(mode: WritingMode) -> LogicalPoint<T> {\n        LogicalPoint {\n            i: Zero::zero(),\n            b: Zero::zero(),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T: Copy> LogicalPoint<T> {\n    #[inline]\n    pub fn new(mode: WritingMode, i: T, b: T) -> LogicalPoint<T> {\n        LogicalPoint {\n            i: i,\n            b: b,\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T: Copy + Sub<T, Output = T>> LogicalPoint<T> {\n    #[inline]\n    pub fn from_physical(\n        mode: WritingMode,\n        point: Point2D<T>,\n        container_size: Size2D<T>,\n    ) -> LogicalPoint<T> {\n        if mode.is_vertical() {\n            LogicalPoint {\n                i: if mode.is_inline_tb() {\n                    point.y\n                } else {\n                    container_size.height - point.y\n                },\n                b: if mode.is_vertical_lr() {\n                    point.x\n                } else {\n                    container_size.width - point.x\n                },\n                debug_writing_mode: DebugWritingMode::new(mode),\n            }\n        } else {\n            LogicalPoint {\n                i: if mode.is_bidi_ltr() {\n                    point.x\n                } else {\n                    container_size.width - point.x\n                },\n                b: point.y,\n                debug_writing_mode: DebugWritingMode::new(mode),\n            }\n        }\n    }\n\n    #[inline]\n    pub fn x(&self, mode: WritingMode, container_size: Size2D<T>) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                self.b\n            } else {\n                container_size.width - self.b\n            }\n        } else {\n            if mode.is_bidi_ltr() {\n                self.i\n            } else {\n                container_size.width - self.i\n            }\n        }\n    }\n\n    #[inline]\n    pub fn set_x(&mut self, mode: WritingMode, x: T, container_size: Size2D<T>) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.b = if mode.is_vertical_lr() {\n                x\n            } else {\n                container_size.width - x\n            }\n        } else {\n            self.i = if mode.is_bidi_ltr() {\n                x\n            } else {\n                container_size.width - x\n            }\n        }\n    }\n\n    #[inline]\n    pub fn y(&self, mode: WritingMode, container_size: Size2D<T>) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_inline_tb() {\n                self.i\n            } else {\n                container_size.height - self.i\n            }\n        } else {\n            self.b\n        }\n    }\n\n    #[inline]\n    pub fn set_y(&mut self, mode: WritingMode, y: T, container_size: Size2D<T>) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.i = if mode.is_inline_tb() {\n                y\n            } else {\n                container_size.height - y\n            }\n        } else {\n            self.b = y\n        }\n    }\n\n    #[inline]\n    pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Point2D<T> {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            Point2D::new(\n                if mode.is_vertical_lr() {\n                    self.b\n                } else {\n                    container_size.width - self.b\n                },\n                if mode.is_inline_tb() {\n                    self.i\n                } else {\n                    container_size.height - self.i\n                },\n            )\n        } else {\n            Point2D::new(\n                if mode.is_bidi_ltr() {\n                    self.i\n                } else {\n                    container_size.width - self.i\n                },\n                self.b,\n            )\n        }\n    }\n\n    #[inline]\n    pub fn convert(\n        &self,\n        mode_from: WritingMode,\n        mode_to: WritingMode,\n        container_size: Size2D<T>,\n    ) -> LogicalPoint<T> {\n        if mode_from == mode_to {\n            self.debug_writing_mode.check(mode_from);\n            *self\n        } else {\n            LogicalPoint::from_physical(\n                mode_to,\n                self.to_physical(mode_from, container_size),\n                container_size,\n            )\n        }\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T>> LogicalPoint<T> {\n    /// This doesn’t really makes sense,\n    /// but happens when dealing with multiple origins.\n    #[inline]\n    pub fn add_point(&self, other: &LogicalPoint<T>) -> LogicalPoint<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalPoint {\n            debug_writing_mode: self.debug_writing_mode,\n            i: self.i + other.i,\n            b: self.b + other.b,\n        }\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T>> Add<LogicalSize<T>> for LogicalPoint<T> {\n    type Output = LogicalPoint<T>;\n\n    #[inline]\n    fn add(self, other: LogicalSize<T>) -> LogicalPoint<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalPoint {\n            debug_writing_mode: self.debug_writing_mode,\n            i: self.i + other.inline,\n            b: self.b + other.block,\n        }\n    }\n}\n\nimpl<T: Copy + Sub<T, Output = T>> Sub<LogicalSize<T>> for LogicalPoint<T> {\n    type Output = LogicalPoint<T>;\n\n    #[inline]\n    fn sub(self, other: LogicalSize<T>) -> LogicalPoint<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalPoint {\n            debug_writing_mode: self.debug_writing_mode,\n            i: self.i - other.inline,\n            b: self.b - other.block,\n        }\n    }\n}\n\n/// A \"margin\" in flow-relative dimensions\n/// Represents the four sides of the margins, borders, or padding of a CSS box,\n/// or a combination of those.\n/// A positive \"margin\" can be added to a rectangle to obtain a bigger rectangle.\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\npub struct LogicalMargin<T> {\n    pub block_start: T,\n    pub inline_end: T,\n    pub block_end: T,\n    pub inline_start: T,\n    debug_writing_mode: DebugWritingMode,\n}\n\nimpl<T: Debug> Debug for LogicalMargin<T> {\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        let writing_mode_string = if cfg!(debug_assertions) {\n            format!(\"{:?}, \", self.debug_writing_mode)\n        } else {\n            \"\".to_owned()\n        };\n\n        write!(\n            formatter,\n            \"LogicalMargin({}i:{:?}..{:?} b:{:?}..{:?})\",\n            writing_mode_string,\n            self.inline_start,\n            self.inline_end,\n            self.block_start,\n            self.block_end\n        )\n    }\n}\n\nimpl<T: Zero> LogicalMargin<T> {\n    #[inline]\n    pub fn zero(mode: WritingMode) -> LogicalMargin<T> {\n        LogicalMargin {\n            block_start: Zero::zero(),\n            inline_end: Zero::zero(),\n            block_end: Zero::zero(),\n            inline_start: Zero::zero(),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T> LogicalMargin<T> {\n    #[inline]\n    pub fn new(\n        mode: WritingMode,\n        block_start: T,\n        inline_end: T,\n        block_end: T,\n        inline_start: T,\n    ) -> LogicalMargin<T> {\n        LogicalMargin {\n            block_start,\n            inline_end,\n            block_end,\n            inline_start,\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n\n    #[inline]\n    pub fn from_physical(mode: WritingMode, offsets: SideOffsets2D<T>) -> LogicalMargin<T> {\n        let block_start;\n        let inline_end;\n        let block_end;\n        let inline_start;\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                block_start = offsets.left;\n                block_end = offsets.right;\n            } else {\n                block_start = offsets.right;\n                block_end = offsets.left;\n            }\n            if mode.is_inline_tb() {\n                inline_start = offsets.top;\n                inline_end = offsets.bottom;\n            } else {\n                inline_start = offsets.bottom;\n                inline_end = offsets.top;\n            }\n        } else {\n            block_start = offsets.top;\n            block_end = offsets.bottom;\n            if mode.is_bidi_ltr() {\n                inline_start = offsets.left;\n                inline_end = offsets.right;\n            } else {\n                inline_start = offsets.right;\n                inline_end = offsets.left;\n            }\n        }\n        LogicalMargin::new(mode, block_start, inline_end, block_end, inline_start)\n    }\n}\n\nimpl<T: Clone> LogicalMargin<T> {\n    #[inline]\n    pub fn new_all_same(mode: WritingMode, value: T) -> LogicalMargin<T> {\n        LogicalMargin::new(mode, value.clone(), value.clone(), value.clone(), value)\n    }\n\n    #[inline]\n    pub fn top(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_inline_tb() {\n                self.inline_start.clone()\n            } else {\n                self.inline_end.clone()\n            }\n        } else {\n            self.block_start.clone()\n        }\n    }\n\n    #[inline]\n    pub fn set_top(&mut self, mode: WritingMode, top: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_inline_tb() {\n                self.inline_start = top\n            } else {\n                self.inline_end = top\n            }\n        } else {\n            self.block_start = top\n        }\n    }\n\n    #[inline]\n    pub fn right(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                self.block_end.clone()\n            } else {\n                self.block_start.clone()\n            }\n        } else {\n            if mode.is_bidi_ltr() {\n                self.inline_end.clone()\n            } else {\n                self.inline_start.clone()\n            }\n        }\n    }\n\n    #[inline]\n    pub fn set_right(&mut self, mode: WritingMode, right: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                self.block_end = right\n            } else {\n                self.block_start = right\n            }\n        } else {\n            if mode.is_bidi_ltr() {\n                self.inline_end = right\n            } else {\n                self.inline_start = right\n            }\n        }\n    }\n\n    #[inline]\n    pub fn bottom(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_inline_tb() {\n                self.inline_end.clone()\n            } else {\n                self.inline_start.clone()\n            }\n        } else {\n            self.block_end.clone()\n        }\n    }\n\n    #[inline]\n    pub fn set_bottom(&mut self, mode: WritingMode, bottom: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_inline_tb() {\n                self.inline_end = bottom\n            } else {\n                self.inline_start = bottom\n            }\n        } else {\n            self.block_end = bottom\n        }\n    }\n\n    #[inline]\n    pub fn left(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                self.block_start.clone()\n            } else {\n                self.block_end.clone()\n            }\n        } else {\n            if mode.is_bidi_ltr() {\n                self.inline_start.clone()\n            } else {\n                self.inline_end.clone()\n            }\n        }\n    }\n\n    #[inline]\n    pub fn set_left(&mut self, mode: WritingMode, left: T) {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                self.block_start = left\n            } else {\n                self.block_end = left\n            }\n        } else {\n            if mode.is_bidi_ltr() {\n                self.inline_start = left\n            } else {\n                self.inline_end = left\n            }\n        }\n    }\n\n    #[inline]\n    pub fn to_physical(&self, mode: WritingMode) -> SideOffsets2D<T> {\n        self.debug_writing_mode.check(mode);\n        let top;\n        let right;\n        let bottom;\n        let left;\n        if mode.is_vertical() {\n            if mode.is_vertical_lr() {\n                left = self.block_start.clone();\n                right = self.block_end.clone();\n            } else {\n                right = self.block_start.clone();\n                left = self.block_end.clone();\n            }\n            if mode.is_inline_tb() {\n                top = self.inline_start.clone();\n                bottom = self.inline_end.clone();\n            } else {\n                bottom = self.inline_start.clone();\n                top = self.inline_end.clone();\n            }\n        } else {\n            top = self.block_start.clone();\n            bottom = self.block_end.clone();\n            if mode.is_bidi_ltr() {\n                left = self.inline_start.clone();\n                right = self.inline_end.clone();\n            } else {\n                right = self.inline_start.clone();\n                left = self.inline_end.clone();\n            }\n        }\n        SideOffsets2D::new(top, right, bottom, left)\n    }\n\n    #[inline]\n    pub fn convert(&self, mode_from: WritingMode, mode_to: WritingMode) -> LogicalMargin<T> {\n        if mode_from == mode_to {\n            self.debug_writing_mode.check(mode_from);\n            self.clone()\n        } else {\n            LogicalMargin::from_physical(mode_to, self.to_physical(mode_from))\n        }\n    }\n}\n\nimpl<T: PartialEq + Zero> LogicalMargin<T> {\n    #[inline]\n    pub fn is_zero(&self) -> bool {\n        self.block_start == Zero::zero()\n            && self.inline_end == Zero::zero()\n            && self.block_end == Zero::zero()\n            && self.inline_start == Zero::zero()\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T>> LogicalMargin<T> {\n    #[inline]\n    pub fn inline_start_end(&self) -> T {\n        self.inline_start + self.inline_end\n    }\n\n    #[inline]\n    pub fn block_start_end(&self) -> T {\n        self.block_start + self.block_end\n    }\n\n    #[inline]\n    pub fn start_end(&self, direction: Direction) -> T {\n        match direction {\n            Direction::Inline => self.inline_start + self.inline_end,\n            Direction::Block => self.block_start + self.block_end,\n        }\n    }\n\n    #[inline]\n    pub fn top_bottom(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.inline_start_end()\n        } else {\n            self.block_start_end()\n        }\n    }\n\n    #[inline]\n    pub fn left_right(&self, mode: WritingMode) -> T {\n        self.debug_writing_mode.check(mode);\n        if mode.is_vertical() {\n            self.block_start_end()\n        } else {\n            self.inline_start_end()\n        }\n    }\n}\n\nimpl<T: Add<T, Output = T>> Add for LogicalMargin<T> {\n    type Output = LogicalMargin<T>;\n\n    #[inline]\n    fn add(self, other: LogicalMargin<T>) -> LogicalMargin<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalMargin {\n            debug_writing_mode: self.debug_writing_mode,\n            block_start: self.block_start + other.block_start,\n            inline_end: self.inline_end + other.inline_end,\n            block_end: self.block_end + other.block_end,\n            inline_start: self.inline_start + other.inline_start,\n        }\n    }\n}\n\nimpl<T: Sub<T, Output = T>> Sub for LogicalMargin<T> {\n    type Output = LogicalMargin<T>;\n\n    #[inline]\n    fn sub(self, other: LogicalMargin<T>) -> LogicalMargin<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalMargin {\n            debug_writing_mode: self.debug_writing_mode,\n            block_start: self.block_start - other.block_start,\n            inline_end: self.inline_end - other.inline_end,\n            block_end: self.block_end - other.block_end,\n            inline_start: self.inline_start - other.inline_start,\n        }\n    }\n}\n\n/// A rectangle in flow-relative dimensions\n#[derive(Clone, Copy, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Serialize))]\npub struct LogicalRect<T> {\n    pub start: LogicalPoint<T>,\n    pub size: LogicalSize<T>,\n    debug_writing_mode: DebugWritingMode,\n}\n\nimpl<T: Debug> Debug for LogicalRect<T> {\n    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {\n        let writing_mode_string = if cfg!(debug_assertions) {\n            format!(\"{:?}, \", self.debug_writing_mode)\n        } else {\n            \"\".to_owned()\n        };\n\n        write!(\n            formatter,\n            \"LogicalRect({}i{:?}×b{:?}, @ (i{:?},b{:?}))\",\n            writing_mode_string, self.size.inline, self.size.block, self.start.i, self.start.b\n        )\n    }\n}\n\nimpl<T: Zero> LogicalRect<T> {\n    #[inline]\n    pub fn zero(mode: WritingMode) -> LogicalRect<T> {\n        LogicalRect {\n            start: LogicalPoint::zero(mode),\n            size: LogicalSize::zero(mode),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T: Copy> LogicalRect<T> {\n    #[inline]\n    pub fn new(\n        mode: WritingMode,\n        inline_start: T,\n        block_start: T,\n        inline: T,\n        block: T,\n    ) -> LogicalRect<T> {\n        LogicalRect {\n            start: LogicalPoint::new(mode, inline_start, block_start),\n            size: LogicalSize::new(mode, inline, block),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n\n    #[inline]\n    pub fn from_point_size(\n        mode: WritingMode,\n        start: LogicalPoint<T>,\n        size: LogicalSize<T>,\n    ) -> LogicalRect<T> {\n        start.debug_writing_mode.check(mode);\n        size.debug_writing_mode.check(mode);\n        LogicalRect {\n            start: start,\n            size: size,\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {\n    #[inline]\n    pub fn from_physical(\n        mode: WritingMode,\n        rect: Rect<T>,\n        container_size: Size2D<T>,\n    ) -> LogicalRect<T> {\n        let inline_start;\n        let block_start;\n        let inline;\n        let block;\n        if mode.is_vertical() {\n            inline = rect.size.height;\n            block = rect.size.width;\n            if mode.is_vertical_lr() {\n                block_start = rect.origin.x;\n            } else {\n                block_start = container_size.width - (rect.origin.x + rect.size.width);\n            }\n            if mode.is_inline_tb() {\n                inline_start = rect.origin.y;\n            } else {\n                inline_start = container_size.height - (rect.origin.y + rect.size.height);\n            }\n        } else {\n            inline = rect.size.width;\n            block = rect.size.height;\n            block_start = rect.origin.y;\n            if mode.is_bidi_ltr() {\n                inline_start = rect.origin.x;\n            } else {\n                inline_start = container_size.width - (rect.origin.x + rect.size.width);\n            }\n        }\n        LogicalRect {\n            start: LogicalPoint::new(mode, inline_start, block_start),\n            size: LogicalSize::new(mode, inline, block),\n            debug_writing_mode: DebugWritingMode::new(mode),\n        }\n    }\n\n    #[inline]\n    pub fn inline_end(&self) -> T {\n        self.start.i + self.size.inline\n    }\n\n    #[inline]\n    pub fn block_end(&self) -> T {\n        self.start.b + self.size.block\n    }\n\n    #[inline]\n    pub fn to_physical(&self, mode: WritingMode, container_size: Size2D<T>) -> Rect<T> {\n        self.debug_writing_mode.check(mode);\n        let x;\n        let y;\n        let width;\n        let height;\n        if mode.is_vertical() {\n            width = self.size.block;\n            height = self.size.inline;\n            if mode.is_vertical_lr() {\n                x = self.start.b;\n            } else {\n                x = container_size.width - self.block_end();\n            }\n            if mode.is_inline_tb() {\n                y = self.start.i;\n            } else {\n                y = container_size.height - self.inline_end();\n            }\n        } else {\n            width = self.size.inline;\n            height = self.size.block;\n            y = self.start.b;\n            if mode.is_bidi_ltr() {\n                x = self.start.i;\n            } else {\n                x = container_size.width - self.inline_end();\n            }\n        }\n        Rect {\n            origin: Point2D::new(x, y),\n            size: Size2D::new(width, height),\n        }\n    }\n\n    #[inline]\n    pub fn convert(\n        &self,\n        mode_from: WritingMode,\n        mode_to: WritingMode,\n        container_size: Size2D<T>,\n    ) -> LogicalRect<T> {\n        if mode_from == mode_to {\n            self.debug_writing_mode.check(mode_from);\n            *self\n        } else {\n            LogicalRect::from_physical(\n                mode_to,\n                self.to_physical(mode_from, container_size),\n                container_size,\n            )\n        }\n    }\n\n    pub fn translate_by_size(&self, offset: LogicalSize<T>) -> LogicalRect<T> {\n        LogicalRect {\n            start: self.start + offset,\n            ..*self\n        }\n    }\n\n    pub fn translate(&self, offset: &LogicalPoint<T>) -> LogicalRect<T> {\n        LogicalRect {\n            start: self.start\n                + LogicalSize {\n                    inline: offset.i,\n                    block: offset.b,\n                    debug_writing_mode: offset.debug_writing_mode,\n                },\n            size: self.size,\n            debug_writing_mode: self.debug_writing_mode,\n        }\n    }\n}\n\nimpl<T: Copy + Ord + Add<T, Output = T> + Sub<T, Output = T>> LogicalRect<T> {\n    #[inline]\n    pub fn union(&self, other: &LogicalRect<T>) -> LogicalRect<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n\n        let inline_start = min(self.start.i, other.start.i);\n        let block_start = min(self.start.b, other.start.b);\n        LogicalRect {\n            start: LogicalPoint {\n                i: inline_start,\n                b: block_start,\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            size: LogicalSize {\n                inline: max(self.inline_end(), other.inline_end()) - inline_start,\n                block: max(self.block_end(), other.block_end()) - block_start,\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            debug_writing_mode: self.debug_writing_mode,\n        }\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Add<LogicalMargin<T>> for LogicalRect<T> {\n    type Output = LogicalRect<T>;\n\n    #[inline]\n    fn add(self, other: LogicalMargin<T>) -> LogicalRect<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalRect {\n            start: LogicalPoint {\n                // Growing a rectangle on the start side means pushing its\n                // start point on the negative direction.\n                i: self.start.i - other.inline_start,\n                b: self.start.b - other.block_start,\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            size: LogicalSize {\n                inline: self.size.inline + other.inline_start_end(),\n                block: self.size.block + other.block_start_end(),\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            debug_writing_mode: self.debug_writing_mode,\n        }\n    }\n}\n\nimpl<T: Copy + Add<T, Output = T> + Sub<T, Output = T>> Sub<LogicalMargin<T>> for LogicalRect<T> {\n    type Output = LogicalRect<T>;\n\n    #[inline]\n    fn sub(self, other: LogicalMargin<T>) -> LogicalRect<T> {\n        self.debug_writing_mode\n            .check_debug(other.debug_writing_mode);\n        LogicalRect {\n            start: LogicalPoint {\n                // Shrinking a rectangle on the start side means pushing its\n                // start point on the positive direction.\n                i: self.start.i + other.inline_start,\n                b: self.start.b + other.block_start,\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            size: LogicalSize {\n                inline: self.size.inline - other.inline_start_end(),\n                block: self.size.block - other.block_start_end(),\n                debug_writing_mode: self.debug_writing_mode,\n            },\n            debug_writing_mode: self.debug_writing_mode,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum LogicalAxis {\n    Block = 0,\n    Inline,\n}\n\nimpl LogicalAxis {\n    #[inline]\n    pub fn to_physical(self, wm: WritingMode) -> PhysicalAxis {\n        if wm.is_horizontal() == (self == Self::Inline) {\n            PhysicalAxis::Horizontal\n        } else {\n            PhysicalAxis::Vertical\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum LogicalSide {\n    BlockStart = 0,\n    BlockEnd,\n    InlineStart,\n    InlineEnd,\n}\n\nimpl LogicalSide {\n    fn is_block(self) -> bool {\n        matches!(self, Self::BlockStart | Self::BlockEnd)\n    }\n\n    #[inline]\n    pub fn to_physical(self, wm: WritingMode) -> PhysicalSide {\n        // Block mapping depends only on vertical+vertical-lr\n        static BLOCK_MAPPING: [[PhysicalSide; 2]; 4] = [\n            [PhysicalSide::Top, PhysicalSide::Bottom], // horizontal-tb\n            [PhysicalSide::Right, PhysicalSide::Left], // vertical-rl\n            [PhysicalSide::Bottom, PhysicalSide::Top], // (horizontal-bt)\n            [PhysicalSide::Left, PhysicalSide::Right], // vertical-lr\n        ];\n\n        if self.is_block() {\n            let vertical = wm.is_vertical();\n            let lr = wm.is_vertical_lr();\n            let index = (vertical as usize) | ((lr as usize) << 1);\n            return BLOCK_MAPPING[index][self as usize];\n        }\n\n        // start = 0, end = 1\n        let edge = self as usize - 2;\n        // Inline axis sides depend on all three of writing-mode, text-orientation and direction,\n        // which are encoded in the VERTICAL, INLINE_REVERSED, VERTICAL_LR and LINE_INVERTED bits.\n        //\n        //   bit 0 = the VERTICAL value\n        //   bit 1 = the INLINE_REVERSED value\n        //   bit 2 = the VERTICAL_LR value\n        //   bit 3 = the LINE_INVERTED value\n        //\n        // Note that not all of these combinations can actually be specified via CSS: there is no\n        // horizontal-bt writing-mode, and no text-orientation value that produces \"inverted\"\n        // text. (The former 'sideways-left' value, no longer in the spec, would have produced\n        // this in vertical-rl mode.)\n        static INLINE_MAPPING: [[PhysicalSide; 2]; 16] = [\n            [PhysicalSide::Left, PhysicalSide::Right], // horizontal-tb               ltr\n            [PhysicalSide::Top, PhysicalSide::Bottom], // vertical-rl                 ltr\n            [PhysicalSide::Right, PhysicalSide::Left], // horizontal-tb               rtl\n            [PhysicalSide::Bottom, PhysicalSide::Top], // vertical-rl                 rtl\n            [PhysicalSide::Right, PhysicalSide::Left], // (horizontal-bt)  (inverted) ltr\n            [PhysicalSide::Top, PhysicalSide::Bottom], // sideways-lr                 rtl\n            [PhysicalSide::Left, PhysicalSide::Right], // (horizontal-bt)  (inverted) rtl\n            [PhysicalSide::Bottom, PhysicalSide::Top], // sideways-lr                 ltr\n            [PhysicalSide::Left, PhysicalSide::Right], // horizontal-tb    (inverted) rtl\n            [PhysicalSide::Top, PhysicalSide::Bottom], // vertical-rl      (inverted) rtl\n            [PhysicalSide::Right, PhysicalSide::Left], // horizontal-tb    (inverted) ltr\n            [PhysicalSide::Bottom, PhysicalSide::Top], // vertical-rl      (inverted) ltr\n            [PhysicalSide::Left, PhysicalSide::Right], // (horizontal-bt)             ltr\n            [PhysicalSide::Top, PhysicalSide::Bottom], // vertical-lr                 ltr\n            [PhysicalSide::Right, PhysicalSide::Left], // (horizontal-bt)             rtl\n            [PhysicalSide::Bottom, PhysicalSide::Top], // vertical-lr                 rtl\n        ];\n\n        debug_assert!(\n            WritingMode::VERTICAL.bits() == 0x01\n                && WritingMode::INLINE_REVERSED.bits() == 0x02\n                && WritingMode::VERTICAL_LR.bits() == 0x04\n                && WritingMode::LINE_INVERTED.bits() == 0x08\n        );\n        let index = (wm.bits() & 0xF) as usize;\n        INLINE_MAPPING[index][edge]\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum LogicalCorner {\n    StartStart = 0,\n    StartEnd,\n    EndStart,\n    EndEnd,\n}\n\nimpl LogicalCorner {\n    #[inline]\n    pub fn to_physical(self, wm: WritingMode) -> PhysicalCorner {\n        static CORNER_TO_SIDES: [[LogicalSide; 2]; 4] = [\n            [LogicalSide::BlockStart, LogicalSide::InlineStart],\n            [LogicalSide::BlockStart, LogicalSide::InlineEnd],\n            [LogicalSide::BlockEnd, LogicalSide::InlineStart],\n            [LogicalSide::BlockEnd, LogicalSide::InlineEnd],\n        ];\n\n        let [block, inline] = CORNER_TO_SIDES[self as usize];\n        let block = block.to_physical(wm);\n        let inline = inline.to_physical(wm);\n        PhysicalCorner::from_sides(block, inline)\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum PhysicalAxis {\n    Vertical = 0,\n    Horizontal,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum PhysicalSide {\n    Top = 0,\n    Right,\n    Bottom,\n    Left,\n}\n\nimpl PhysicalSide {\n    /// Returns whether one physical side is parallel to another.\n    pub fn parallel_to(self, other: Self) -> bool {\n        !self.orthogonal_to(other)\n    }\n\n    /// Returns whether one physical side is orthogonal to another.\n    pub fn orthogonal_to(self, other: Self) -> bool {\n        matches!(self, Self::Top | Self::Bottom) != matches!(other, Self::Top | Self::Bottom)\n    }\n\n    /// Returns the opposite side.\n    pub fn opposite_side(self) -> Self {\n        match self {\n            Self::Top => Self::Bottom,\n            Self::Right => Self::Left,\n            Self::Bottom => Self::Top,\n            Self::Left => Self::Right,\n        }\n    }\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\npub enum PhysicalCorner {\n    TopLeft = 0,\n    TopRight,\n    BottomRight,\n    BottomLeft,\n}\n\nimpl PhysicalCorner {\n    fn from_sides(a: PhysicalSide, b: PhysicalSide) -> Self {\n        debug_assert!(a.orthogonal_to(b), \"Sides should be orthogonal\");\n        // Only some of these are possible, since we expect only orthogonal values. If the two\n        // sides were to be parallel, we fall back to returning TopLeft.\n        const IMPOSSIBLE: PhysicalCorner = PhysicalCorner::TopLeft;\n        static SIDES_TO_CORNER: [[PhysicalCorner; 4]; 4] = [\n            [\n                IMPOSSIBLE,\n                PhysicalCorner::TopRight,\n                IMPOSSIBLE,\n                PhysicalCorner::TopLeft,\n            ],\n            [\n                PhysicalCorner::TopRight,\n                IMPOSSIBLE,\n                PhysicalCorner::BottomRight,\n                IMPOSSIBLE,\n            ],\n            [\n                IMPOSSIBLE,\n                PhysicalCorner::BottomRight,\n                IMPOSSIBLE,\n                PhysicalCorner::BottomLeft,\n            ],\n            [\n                PhysicalCorner::TopLeft,\n                IMPOSSIBLE,\n                PhysicalCorner::BottomLeft,\n                IMPOSSIBLE,\n            ],\n        ];\n        SIDES_TO_CORNER[a as usize][b as usize]\n    }\n}\n"
  },
  {
    "path": "style/macros.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Various macro helpers.\n\nmacro_rules! trivial_to_computed_value {\n    ($name:ty) => {\n        impl $crate::values::computed::ToComputedValue for $name {\n            type ComputedValue = $name;\n\n            fn to_computed_value(&self, _: &$crate::values::computed::Context) -> Self {\n                self.clone()\n            }\n\n            fn from_computed_value(other: &Self) -> Self {\n                other.clone()\n            }\n        }\n    };\n}\n\n/// A macro to parse an identifier, or return an `UnexpectedIdent` error\n/// otherwise.\n///\n/// FIXME(emilio): The fact that `UnexpectedIdent` is a `SelectorParseError`\n/// doesn't make a lot of sense to me.\nmacro_rules! try_match_ident_ignore_ascii_case {\n    ($input:expr, $( $match_body:tt )*) => {{\n        let location = $input.current_source_location();\n        let ident = $input.expect_ident()?;\n        ::cssparser::match_ignore_ascii_case! { &ident,\n            $( $match_body )*\n            _ => return Err(location.new_custom_error(\n                ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident.clone())\n            ))\n        }\n    }}\n}\n\n#[cfg(feature = \"servo\")]\nmacro_rules! local_name {\n    ($s:tt) => {\n        $crate::values::GenericAtomIdent(web_atoms::local_name!($s))\n    };\n}\n\n#[cfg(feature = \"servo\")]\nmacro_rules! ns {\n    () => {\n        $crate::values::GenericAtomIdent(web_atoms::ns!())\n    };\n    ($s:tt) => {\n        $crate::values::GenericAtomIdent(web_atoms::ns!($s))\n    };\n}\n\n#[cfg(feature = \"gecko\")]\nmacro_rules! local_name {\n    ($s:tt) => {\n        $crate::values::AtomIdent(atom!($s))\n    };\n}\n\n/// Asserts the size of a type at compile time.\nmacro_rules! size_of_test {\n    ($t: ty, $expected_size: expr) => {\n        #[cfg(target_pointer_width = \"64\")]\n        const_assert_eq!(std::mem::size_of::<$t>(), $expected_size);\n    };\n}\n"
  },
  {
    "path": "style/matching.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! High-level interface to CSS selector matching.\n\n#![allow(unsafe_code)]\n#![deny(missing_docs)]\n\nuse crate::computed_value_flags::ComputedValueFlags;\n#[cfg(feature = \"servo\")]\nuse crate::context::CascadeInputs;\nuse crate::context::{ElementCascadeInputs, QuirksMode};\nuse crate::context::{SharedStyleContext, StyleContext};\nuse crate::data::{ElementData, ElementStyles};\nuse crate::dom::TElement;\n#[cfg(feature = \"servo\")]\nuse crate::dom::TNode;\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::properties::longhands::display::computed_value::T as Display;\nuse crate::properties::ComputedValues;\nuse crate::properties::PropertyDeclarationBlock;\n#[cfg(feature = \"servo\")]\nuse crate::rule_tree::RuleCascadeFlags;\nuse crate::rule_tree::{CascadeLevel, CascadeOrigin, StrongRuleNode};\nuse crate::selector_parser::{PseudoElement, RestyleDamage};\nuse crate::shared_lock::Locked;\nuse crate::style_resolver::StyleResolverForElement;\nuse crate::style_resolver::{PseudoElementResolution, ResolvedElementStyles};\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse crate::stylist::RuleInclusion;\nuse crate::traversal_flags::TraversalFlags;\nuse crate::values::generics::animation::GenericAnimationTimeline;\nuse crate::values::specified::animation::Scroller;\nuse servo_arc::{Arc, ArcBorrow};\n\n/// Represents the result of comparing an element's old and new style.\n#[derive(Debug)]\npub struct StyleDifference {\n    /// The resulting damage.\n    pub damage: RestyleDamage,\n    /// Whether any styles changed.\n    pub change: StyleChange,\n}\n\n/// Represents whether or not the style of an element has changed.\n#[derive(Clone, Copy, Debug)]\npub enum StyleChange {\n    /// The style hasn't changed.\n    Unchanged,\n    /// The style has changed.\n    Changed {\n        /// Whether only reset properties have changed.\n        reset_only: bool,\n        /// Whether custom properties have changed.\n        custom_properties_changed: bool,\n    },\n}\n\n/// Determines which styles are being cascaded currently.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum CascadeVisitedMode {\n    /// Cascade the regular, unvisited styles.\n    Unvisited,\n    /// Cascade the styles used when an element's relevant link is visited.  A\n    /// \"relevant link\" is the element being matched if it is a link or the\n    /// nearest ancestor link.\n    Visited,\n}\n\ntrait PrivateMatchMethods: TElement {\n    fn replace_single_rule_node(\n        context: &SharedStyleContext,\n        level: CascadeLevel,\n        layer_order: LayerOrder,\n        pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,\n        path: &mut StrongRuleNode,\n    ) -> bool {\n        let stylist = &context.stylist;\n        let guards = &context.guards;\n\n        let mut important_rules_changed = false;\n        let new_node = stylist.rule_tree().update_rule_at_level(\n            level,\n            layer_order,\n            pdb,\n            path,\n            guards,\n            &mut important_rules_changed,\n        );\n        if let Some(n) = new_node {\n            *path = n;\n        }\n        important_rules_changed\n    }\n\n    /// Updates the rule nodes without re-running selector matching, using just\n    /// the rule tree, for a specific visited mode.\n    ///\n    /// Returns true if an !important rule was replaced.\n    fn replace_rules_internal(\n        &self,\n        replacements: RestyleHint,\n        context: &mut StyleContext<Self>,\n        cascade_visited: CascadeVisitedMode,\n        cascade_inputs: &mut ElementCascadeInputs,\n    ) -> bool {\n        debug_assert!(\n            replacements.intersects(RestyleHint::replacements())\n                && (replacements & !RestyleHint::replacements()).is_empty()\n        );\n\n        let primary_rules = match cascade_visited {\n            CascadeVisitedMode::Unvisited => cascade_inputs.primary.rules.as_mut(),\n            CascadeVisitedMode::Visited => cascade_inputs.primary.visited_rules.as_mut(),\n        };\n\n        let primary_rules = match primary_rules {\n            Some(r) => r,\n            None => return false,\n        };\n\n        if !context.shared.traversal_flags.for_animation_only() {\n            let mut result = false;\n            if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {\n                let style_attribute = self.style_attribute();\n                result |= Self::replace_single_rule_node(\n                    context.shared,\n                    CascadeLevel::same_tree_author_normal(),\n                    LayerOrder::style_attribute(),\n                    style_attribute,\n                    primary_rules,\n                );\n                result |= Self::replace_single_rule_node(\n                    context.shared,\n                    CascadeLevel::same_tree_author_important(),\n                    LayerOrder::style_attribute(),\n                    style_attribute,\n                    primary_rules,\n                );\n                // FIXME(emilio): Still a hack!\n                self.unset_dirty_style_attribute();\n            }\n            return result;\n        }\n\n        // Animation restyle hints are processed prior to other restyle\n        // hints in the animation-only traversal.\n        //\n        // Non-animation restyle hints will be processed in a subsequent\n        // normal traversal.\n        if replacements.intersects(RestyleHint::for_animations()) {\n            debug_assert!(context.shared.traversal_flags.for_animation_only());\n\n            if replacements.contains(RestyleHint::RESTYLE_SMIL) {\n                Self::replace_single_rule_node(\n                    context.shared,\n                    CascadeLevel::new(CascadeOrigin::SMILOverride),\n                    LayerOrder::root(),\n                    self.smil_override(),\n                    primary_rules,\n                );\n            }\n\n            if replacements.contains(RestyleHint::RESTYLE_CSS_TRANSITIONS) {\n                Self::replace_single_rule_node(\n                    context.shared,\n                    CascadeLevel::new(CascadeOrigin::Transitions),\n                    LayerOrder::root(),\n                    self.transition_rule(&context.shared)\n                        .as_ref()\n                        .map(|a| a.borrow_arc()),\n                    primary_rules,\n                );\n            }\n\n            if replacements.contains(RestyleHint::RESTYLE_CSS_ANIMATIONS) {\n                Self::replace_single_rule_node(\n                    context.shared,\n                    CascadeLevel::new(CascadeOrigin::Animations),\n                    LayerOrder::root(),\n                    self.animation_rule(&context.shared)\n                        .as_ref()\n                        .map(|a| a.borrow_arc()),\n                    primary_rules,\n                );\n            }\n        }\n\n        false\n    }\n\n    #[inline]\n    fn requires_animation_update_for_scroll_self(\n        old: &ComputedValues,\n        new: &ComputedValues,\n    ) -> bool {\n        // Need to specifically take care of `animation-timeline: scroll(self)` - unlike other values, it can become inactive.\n        // When we switch in and out of being scrollable, we should make sure to perform the animation update.\n        // Specifying scroll in any axis makes the other axis scrollable [1], so we need to update on either axis changing.\n        // This does not apply to `scroll(root)`, since the viewport scroller is always available, or `scroll(nearest)`,\n        // which will go up to root.\n        // [1]: https://drafts.csswg.org/css-overflow/#propdef-overflow\n        let scrollable_changed = old.clone_overflow_x().is_scrollable()\n            != new.clone_overflow_x().is_scrollable()\n            || old.clone_overflow_y().is_scrollable() != new.clone_overflow_y().is_scrollable();\n        if !scrollable_changed {\n            return false;\n        }\n        new.get_ui().animation_timeline_iter().any(|timeline| {\n            let scroll_function = match timeline {\n                GenericAnimationTimeline::Scroll(ref sf) => sf,\n                _ => return false,\n            };\n            if scroll_function.scroller != Scroller::SelfElement {\n                return false;\n            }\n            true\n        })\n    }\n\n    /// If there is no transition rule in the ComputedValues, it returns None.\n    fn after_change_style(\n        &self,\n        context: &mut StyleContext<Self>,\n        primary_style: &Arc<ComputedValues>,\n    ) -> Option<Arc<ComputedValues>> {\n        // Actually `PseudoElementResolution` doesn't really matter.\n        StyleResolverForElement::new(\n            *self,\n            context,\n            RuleInclusion::All,\n            PseudoElementResolution::IfApplicable,\n        )\n        .after_change_style(primary_style)\n    }\n\n    fn needs_animations_update(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_style: Option<&ComputedValues>,\n        new_style: &ComputedValues,\n        pseudo_element: Option<PseudoElement>,\n    ) -> bool {\n        let new_ui_style = new_style.get_ui();\n        let new_style_specifies_animations = new_ui_style.specifies_animations();\n\n        let has_animations = self.has_css_animations(&context.shared, pseudo_element);\n        if !new_style_specifies_animations && !has_animations {\n            return false;\n        }\n\n        let old_style = match old_style {\n            Some(old) => old,\n            // If we have no old style but have animations, we may be a\n            // pseudo-element which was re-created without style changes.\n            //\n            // This can happen when we reframe the pseudo-element without\n            // restyling it (due to content insertion on a flex container or\n            // such, for example). See bug 1564366.\n            //\n            // FIXME(emilio): The really right fix for this is keeping the\n            // pseudo-element itself around on reframes, but that's a bit\n            // harder. If we do that we can probably remove quite a lot of the\n            // EffectSet complexity though, since right now it's stored on the\n            // parent element for pseudo-elements given we need to keep it\n            // around...\n            None => {\n                return new_style_specifies_animations || new_style.is_pseudo_style();\n            },\n        };\n\n        let old_ui_style = old_style.get_ui();\n\n        let keyframes_could_have_changed = context\n            .shared\n            .traversal_flags\n            .contains(TraversalFlags::ForCSSRuleChanges);\n\n        // If the traversal is triggered due to changes in CSS rules changes, we\n        // need to try to update all CSS animations on the element if the\n        // element has or will have CSS animation style regardless of whether\n        // the animation is running or not.\n        //\n        // TODO: We should check which @keyframes were added/changed/deleted and\n        // update only animations corresponding to those @keyframes.\n        if keyframes_could_have_changed {\n            return true;\n        }\n\n        // If the animations changed, well...\n        if !old_ui_style.animations_equals(new_ui_style) {\n            return true;\n        }\n\n        let old_display = old_style.clone_display();\n        let new_display = new_style.clone_display();\n\n        // If we were display: none, we may need to trigger animations.\n        if old_display == Display::None && new_display != Display::None {\n            return new_style_specifies_animations;\n        }\n\n        // If we are becoming display: none, we may need to stop animations.\n        if old_display != Display::None && new_display == Display::None {\n            return has_animations;\n        }\n\n        // We might need to update animations if writing-mode or direction\n        // changed, and any of the animations contained logical properties.\n        //\n        // We may want to be more granular, but it's probably not worth it.\n        if new_style.writing_mode != old_style.writing_mode {\n            return has_animations;\n        }\n\n        if Self::requires_animation_update_for_scroll_self(old_style, new_style) {\n            return has_animations;\n        }\n\n        false\n    }\n\n    fn might_need_transitions_update(\n        &self,\n        context: &StyleContext<Self>,\n        old_style: Option<&ComputedValues>,\n        new_style: &ComputedValues,\n        pseudo_element: Option<PseudoElement>,\n    ) -> bool {\n        let old_style = match old_style {\n            Some(v) => v,\n            None => return false,\n        };\n\n        if !self.has_css_transitions(context.shared, pseudo_element)\n            && !new_style.get_ui().specifies_transitions()\n        {\n            return false;\n        }\n\n        if old_style.clone_display().is_none() {\n            return false;\n        }\n\n        return true;\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn maybe_resolve_starting_style(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_values: Option<&Arc<ComputedValues>>,\n        new_styles: &ResolvedElementStyles,\n    ) -> Option<Arc<ComputedValues>> {\n        // For both cases:\n        // If there is no transitions specified we don't have to resolve starting style.\n        let new_primary = new_styles.primary_style();\n        if !new_primary.get_ui().specifies_transitions() {\n            return None;\n        }\n\n        // We resolve starting style only if we don't have before-change-style, or we change from\n        // display:none.\n        if old_values.is_some()\n            && !new_primary.is_display_property_changed_from_none(old_values.map(|s| &**s))\n        {\n            return None;\n        }\n\n        let mut resolver = StyleResolverForElement::new(\n            *self,\n            context,\n            RuleInclusion::All,\n            PseudoElementResolution::IfApplicable,\n        );\n\n        let starting_style = resolver.resolve_starting_style(new_primary)?;\n        if starting_style.style().clone_display().is_none() {\n            return None;\n        }\n\n        Some(starting_style.0)\n    }\n\n    /// Handle CSS Transitions. Returns None if we don't need to update transitions. And it returns\n    /// the before-change style per CSS Transitions spec.\n    ///\n    /// Note: The before-change style could be the computed values of all properties on the element\n    /// as of the previous style change event, or the starting style if we don't have the valid\n    /// before-change style there.\n    #[cfg(feature = \"gecko\")]\n    fn process_transitions(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_values: Option<&Arc<ComputedValues>>,\n        new_styles: &mut ResolvedElementStyles,\n    ) -> Option<Arc<ComputedValues>> {\n        let starting_values = self.maybe_resolve_starting_style(context, old_values, new_styles);\n        let before_change_or_starting = starting_values.as_ref().or(old_values);\n        let new_values = new_styles.primary_style_mut();\n\n        if !self.might_need_transitions_update(\n            context,\n            before_change_or_starting.map(|s| &**s),\n            new_values,\n            /* pseudo_element = */ None,\n        ) {\n            return None;\n        }\n\n        let after_change_style =\n            if self.has_css_transitions(context.shared, /* pseudo_element = */ None) {\n                self.after_change_style(context, new_values)\n            } else {\n                None\n            };\n\n        // In order to avoid creating a SequentialTask for transitions which\n        // may not be updated, we check it per property to make sure Gecko\n        // side will really update transition.\n        if !self.needs_transitions_update(\n            before_change_or_starting.unwrap(),\n            after_change_style.as_ref().unwrap_or(&new_values),\n        ) {\n            return None;\n        }\n\n        if let Some(values_without_transitions) = after_change_style {\n            *new_values = values_without_transitions;\n        }\n\n        // Move the new-created starting style, or clone the old values.\n        if starting_values.is_some() {\n            starting_values\n        } else {\n            old_values.cloned()\n        }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn process_animations(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_styles: &mut ElementStyles,\n        new_styles: &mut ResolvedElementStyles,\n        important_rules_changed: bool,\n    ) {\n        use crate::context::UpdateAnimationsTasks;\n\n        let old_values = &old_styles.primary;\n        if context.shared.traversal_flags.for_animation_only() && old_values.is_some() {\n            return;\n        }\n\n        // Bug 868975: These steps should examine and update the visited styles\n        // in addition to the unvisited styles.\n\n        let mut tasks = UpdateAnimationsTasks::empty();\n\n        if old_values.as_deref().map_or_else(\n            || {\n                new_styles\n                    .primary_style()\n                    .get_ui()\n                    .specifies_timeline_scope()\n            },\n            |old| {\n                !old.get_ui()\n                    .timeline_scope_equals(new_styles.primary_style().get_ui())\n            },\n        ) {\n            tasks.insert(UpdateAnimationsTasks::TIMELINE_SCOPES);\n        }\n\n        if old_values.as_deref().map_or_else(\n            || {\n                new_styles\n                    .primary_style()\n                    .get_ui()\n                    .specifies_scroll_timelines()\n            },\n            |old| {\n                !old.get_ui()\n                    .scroll_timelines_equals(new_styles.primary_style().get_ui())\n            },\n        ) {\n            tasks.insert(UpdateAnimationsTasks::SCROLL_TIMELINES);\n        }\n\n        if old_values.as_deref().map_or_else(\n            || {\n                new_styles\n                    .primary_style()\n                    .get_ui()\n                    .specifies_view_timelines()\n            },\n            |old| {\n                !old.get_ui()\n                    .view_timelines_equals(new_styles.primary_style().get_ui())\n            },\n        ) {\n            tasks.insert(UpdateAnimationsTasks::VIEW_TIMELINES);\n        }\n\n        if self.needs_animations_update(\n            context,\n            old_values.as_deref(),\n            new_styles.primary_style(),\n            /* pseudo_element = */ None,\n        ) {\n            tasks.insert(UpdateAnimationsTasks::CSS_ANIMATIONS);\n        }\n\n        let before_change_style =\n            self.process_transitions(context, old_values.as_ref(), new_styles);\n        if before_change_style.is_some() {\n            tasks.insert(UpdateAnimationsTasks::CSS_TRANSITIONS);\n        }\n\n        if self.has_animations(&context.shared) {\n            tasks.insert(UpdateAnimationsTasks::EFFECT_PROPERTIES);\n            if important_rules_changed {\n                tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);\n            }\n            if new_styles\n                .primary_style()\n                .is_display_property_changed_from_none(old_values.as_deref())\n            {\n                tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);\n            }\n        }\n\n        if !tasks.is_empty() {\n            let task = crate::context::SequentialTask::update_animations(\n                *self,\n                before_change_style,\n                tasks,\n            );\n            context.thread_local.tasks.push(task);\n        }\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn process_animations(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_styles: &mut ElementStyles,\n        new_resolved_styles: &mut ResolvedElementStyles,\n        _important_rules_changed: bool,\n    ) {\n        use crate::animation::AnimationSetKey;\n        use crate::dom::TDocument;\n\n        let style_changed = self.process_animations_for_style(\n            context,\n            &mut old_styles.primary,\n            new_resolved_styles.primary_style_mut(),\n            /* pseudo_element = */ None,\n        );\n\n        // If we have modified animation or transitions, we recascade style for this node.\n        if style_changed {\n            let primary_style = new_resolved_styles.primary_style();\n            let mut rule_node = primary_style.rules().clone();\n            let declarations = context.shared.animations.get_all_declarations(\n                &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()),\n                context.shared.current_time_for_animations,\n                self.as_node().owner_doc().shared_lock(),\n            );\n            Self::replace_single_rule_node(\n                &context.shared,\n                CascadeLevel::new(CascadeOrigin::Transitions),\n                LayerOrder::root(),\n                declarations.transitions.as_ref().map(|a| a.borrow_arc()),\n                &mut rule_node,\n            );\n            Self::replace_single_rule_node(\n                &context.shared,\n                CascadeLevel::new(CascadeOrigin::Animations),\n                LayerOrder::root(),\n                declarations.animations.as_ref().map(|a| a.borrow_arc()),\n                &mut rule_node,\n            );\n\n            if rule_node != *primary_style.rules() {\n                let inputs = CascadeInputs {\n                    rules: Some(rule_node),\n                    visited_rules: primary_style.visited_rules().cloned(),\n                    flags: primary_style.flags.for_cascade_inputs(),\n                    included_cascade_flags: RuleCascadeFlags::empty(),\n                };\n\n                new_resolved_styles.primary.style = StyleResolverForElement::new(\n                    *self,\n                    context,\n                    RuleInclusion::All,\n                    PseudoElementResolution::IfApplicable,\n                )\n                .cascade_style_and_visited_with_default_parents(inputs);\n            }\n        }\n\n        self.process_animations_for_pseudo(\n            context,\n            old_styles,\n            new_resolved_styles,\n            PseudoElement::Before,\n        );\n        self.process_animations_for_pseudo(\n            context,\n            old_styles,\n            new_resolved_styles,\n            PseudoElement::After,\n        );\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn process_animations_for_pseudo(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_styles: &ElementStyles,\n        new_resolved_styles: &mut ResolvedElementStyles,\n        pseudo_element: PseudoElement,\n    ) {\n        use crate::animation::AnimationSetKey;\n        use crate::dom::TDocument;\n\n        let key = AnimationSetKey::new_for_pseudo(self.as_node().opaque(), pseudo_element.clone());\n        let style = match new_resolved_styles.pseudos.get(&pseudo_element) {\n            Some(style) => Arc::clone(style),\n            None => {\n                context\n                    .shared\n                    .animations\n                    .cancel_all_animations_for_key(&key);\n                return;\n            },\n        };\n\n        let old_style = old_styles.pseudos.get(&pseudo_element).cloned();\n        self.process_animations_for_style(\n            context,\n            &old_style,\n            &style,\n            Some(pseudo_element.clone()),\n        );\n\n        let declarations = context.shared.animations.get_all_declarations(\n            &key,\n            context.shared.current_time_for_animations,\n            self.as_node().owner_doc().shared_lock(),\n        );\n        if declarations.is_empty() {\n            return;\n        }\n\n        let mut rule_node = style.rules().clone();\n        Self::replace_single_rule_node(\n            &context.shared,\n            CascadeLevel::new(CascadeOrigin::Transitions),\n            LayerOrder::root(),\n            declarations.transitions.as_ref().map(|a| a.borrow_arc()),\n            &mut rule_node,\n        );\n        Self::replace_single_rule_node(\n            &context.shared,\n            CascadeLevel::new(CascadeOrigin::Animations),\n            LayerOrder::root(),\n            declarations.animations.as_ref().map(|a| a.borrow_arc()),\n            &mut rule_node,\n        );\n        if rule_node == *style.rules() {\n            return;\n        }\n\n        let inputs = CascadeInputs {\n            rules: Some(rule_node),\n            visited_rules: style.visited_rules().cloned(),\n            flags: style.flags.for_cascade_inputs(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n        };\n\n        let new_style = StyleResolverForElement::new(\n            *self,\n            context,\n            RuleInclusion::All,\n            PseudoElementResolution::IfApplicable,\n        )\n        .cascade_style_and_visited_for_pseudo_with_default_parents(\n            inputs,\n            &pseudo_element,\n            &new_resolved_styles.primary,\n        );\n\n        new_resolved_styles\n            .pseudos\n            .set(&pseudo_element, new_style.0);\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn process_animations_for_style(\n        &self,\n        context: &mut StyleContext<Self>,\n        old_values: &Option<Arc<ComputedValues>>,\n        new_values: &Arc<ComputedValues>,\n        pseudo_element: Option<PseudoElement>,\n    ) -> bool {\n        use crate::animation::{AnimationSetKey, AnimationState};\n\n        // We need to call this before accessing the `ElementAnimationSet` from the\n        // map because this call will do a RwLock::read().\n        let needs_animations_update = self.needs_animations_update(\n            context,\n            old_values.as_deref(),\n            new_values,\n            pseudo_element,\n        );\n\n        let might_need_transitions_update = self.might_need_transitions_update(\n            context,\n            old_values.as_deref(),\n            new_values,\n            pseudo_element,\n        );\n\n        let mut after_change_style = None;\n        if might_need_transitions_update {\n            after_change_style = self.after_change_style(context, new_values);\n        }\n\n        let key = AnimationSetKey::new(self.as_node().opaque(), pseudo_element);\n        let shared_context = context.shared;\n        let mut animation_set = shared_context\n            .animations\n            .sets\n            .write()\n            .remove(&key)\n            .unwrap_or_default();\n\n        // Starting animations is expensive, because we have to recalculate the style\n        // for all the keyframes. We only want to do this if we think that there's a\n        // chance that the animations really changed.\n        if needs_animations_update {\n            let mut resolver = StyleResolverForElement::new(\n                *self,\n                context,\n                RuleInclusion::All,\n                PseudoElementResolution::IfApplicable,\n            );\n\n            animation_set.update_animations_for_new_style::<Self>(\n                *self,\n                &shared_context,\n                &new_values,\n                &mut resolver,\n            );\n        }\n\n        animation_set.update_transitions_for_new_style(\n            might_need_transitions_update,\n            &shared_context,\n            old_values.as_ref(),\n            after_change_style.as_ref().unwrap_or(new_values),\n        );\n\n        // This should change the computed values in the style, so we don't need\n        // to mark this set as dirty.\n        animation_set\n            .transitions\n            .retain(|transition| transition.state != AnimationState::Finished);\n\n        animation_set\n            .animations\n            .retain(|animation| animation.state != AnimationState::Finished);\n\n        // If the ElementAnimationSet is empty, and don't store it in order to\n        // save memory and to avoid extra processing later.\n        let changed_animations = animation_set.dirty;\n        if !animation_set.is_empty() {\n            animation_set.dirty = false;\n            shared_context\n                .animations\n                .sets\n                .write()\n                .insert(key, animation_set);\n        }\n\n        changed_animations\n    }\n\n    /// Computes and applies non-redundant damage.\n    fn accumulate_damage_for(\n        &self,\n        shared_context: &SharedStyleContext,\n        damage: &mut RestyleDamage,\n        old_values: &ComputedValues,\n        new_values: &ComputedValues,\n        pseudo: Option<&PseudoElement>,\n    ) -> RestyleHint {\n        debug!(\"accumulate_damage_for: {:?}\", self);\n        debug_assert!(!shared_context\n            .traversal_flags\n            .contains(TraversalFlags::FinalAnimationTraversal));\n\n        let difference = self.compute_style_difference(old_values, new_values, pseudo);\n\n        *damage |= difference.damage;\n\n        debug!(\" > style difference: {:?}\", difference);\n\n        let mut children_hint = RestyleHint::empty();\n        if old_values.flags.maybe_inherited() != new_values.flags.maybe_inherited() {\n            // Even if the styles are otherwise equal, we need to cascade the children in order to\n            // ensure the correct propagation of inherited computed value flags.\n            debug!(\n                \" > flags changed: {:?} != {:?}\",\n                old_values.flags, new_values.flags\n            );\n            children_hint |= RestyleHint::RECASCADE_SELF;\n        } else if old_values.effective_zoom != new_values.effective_zoom {\n            // Similarly, even if styles are equal, we need to propagate zoom changes.\n            debug!(\n                \" > zoom changed: {:?} != {:?}\",\n                old_values.effective_zoom, new_values.effective_zoom\n            );\n            children_hint |= RestyleHint::RECASCADE_SELF;\n        }\n\n        let StyleChange::Changed {\n            reset_only,\n            custom_properties_changed,\n        } = difference.change\n        else {\n            return children_hint;\n        };\n\n        let new_container_name = new_values.clone_container_name();\n        if new_container_name != old_values.clone_container_name() {\n            // If we're becoming or stopped to become a named container, we need to potentially\n            // restyle children.\n            children_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER;\n        } else if custom_properties_changed {\n            // Custom property changes affect style queries. How specifically depends on whether\n            // we're a named container (more expensive, need to check the subtree) or not.\n            children_hint |= if !new_container_name.is_none() {\n                RestyleHint::RESTYLE_IF_AFFECTED_BY_NAMED_STYLE_CONTAINER\n            } else {\n                RestyleHint::RESTYLE_IF_AFFECTED_BY_STYLE_QUERIES\n            };\n        }\n\n        if reset_only {\n            // If only reset properties changed, we _might_ need to unconditionally restyle, but\n            // most likely we can get away with stopping the cascade at the next level, if our\n            // children don't inherit reset properties.\n            children_hint |=\n                if need_to_unconditionally_recascade_for_reset_change(old_values, new_values) {\n                    RestyleHint::RECASCADE_SELF\n                } else {\n                    RestyleHint::RECASCADE_SELF_IF_INHERIT_RESET_STYLE\n                };\n        } else {\n            // If inherited properties changed, we need to cascade our children.\n            children_hint |= RestyleHint::RECASCADE_SELF;\n        }\n\n        children_hint\n    }\n}\n\n/// Whether we need to recascade children for a change in non-inherited properties.\nfn need_to_unconditionally_recascade_for_reset_change(\n    old_values: &ComputedValues,\n    new_values: &ComputedValues,\n) -> bool {\n    let old_display = old_values.clone_display();\n    let new_display = new_values.clone_display();\n\n    if old_display != new_display {\n        // If we used to be a display: none element, and no longer are, our\n        // children need to be restyled because they're unstyled.\n        if old_display == Display::None {\n            return true;\n        }\n        // Blockification of children may depend on our display value, so we need to actually do the\n        // recascade. We could potentially do better, but it doesn't seem worth it.\n        if old_display.is_item_container() != new_display.is_item_container() {\n            return true;\n        }\n        // We may also need to blockify and un-blockify descendants if our display goes from / to\n        // display: contents, since the \"layout parent style\" changes.\n        if old_display.is_contents() || new_display.is_contents() {\n            return true;\n        }\n        // Line break suppression may also be affected if the display\n        // type changes from ruby to non-ruby.\n        #[cfg(feature = \"gecko\")]\n        if old_display.is_ruby_type() != new_display.is_ruby_type() {\n            return true;\n        }\n    }\n\n    // Children with justify-items: auto may depend on our\n    // justify-items property value.\n    //\n    // Similarly, we could potentially do better, but this really\n    // seems not common enough to care about.\n    #[cfg(feature = \"gecko\")]\n    {\n        use crate::values::specified::align::AlignFlags;\n\n        let old_justify_items = old_values.get_position().clone_justify_items();\n        let new_justify_items = new_values.get_position().clone_justify_items();\n\n        let was_legacy_justify_items = old_justify_items.computed.contains(AlignFlags::LEGACY);\n\n        let is_legacy_justify_items = new_justify_items.computed.contains(AlignFlags::LEGACY);\n\n        if is_legacy_justify_items != was_legacy_justify_items {\n            return true;\n        }\n\n        if was_legacy_justify_items && old_justify_items.computed != new_justify_items.computed {\n            return true;\n        }\n    }\n\n    false\n}\n\nimpl<E: TElement> PrivateMatchMethods for E {}\n\n/// The public API that elements expose for selector matching.\npub trait MatchMethods: TElement {\n    /// Returns the closest parent element that doesn't have a display: contents\n    /// style (and thus generates a box).\n    ///\n    /// This is needed to correctly handle blockification of flex and grid\n    /// items.\n    ///\n    /// Returns itself if the element has no parent. In practice this doesn't\n    /// happen because the root element is blockified per spec, but it could\n    /// happen if we decide to not blockify for roots of disconnected subtrees,\n    /// which is a kind of dubious behavior.\n    fn layout_parent(&self) -> Self {\n        let mut current = self.clone();\n        loop {\n            current = match current.traversal_parent() {\n                Some(el) => el,\n                None => return current,\n            };\n\n            let is_display_contents = current\n                .borrow_data()\n                .unwrap()\n                .styles\n                .primary()\n                .is_display_contents();\n\n            if !is_display_contents {\n                return current;\n            }\n        }\n    }\n\n    /// Updates the styles with the new ones, diffs them, and stores the restyle\n    /// damage.\n    fn finish_restyle(\n        &self,\n        context: &mut StyleContext<Self>,\n        data: &mut ElementData,\n        mut new_styles: ResolvedElementStyles,\n        important_rules_changed: bool,\n    ) -> RestyleHint {\n        self.process_animations(\n            context,\n            &mut data.styles,\n            &mut new_styles,\n            important_rules_changed,\n        );\n\n        // First of all, update the styles.\n        let old_styles = data.set_styles(new_styles);\n\n        let new_primary_style = data.styles.primary.as_ref().unwrap();\n\n        let mut child_restyle_hint = RestyleHint::empty();\n        let is_root = new_primary_style\n            .flags\n            .contains(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);\n\n        let old_style = old_styles.primary.as_ref();\n        let old_font_size = old_style.map(|s| s.get_font().clone_font_size());\n        let device = context.shared.stylist.device();\n        let new_font_size = new_primary_style.get_font().clone_font_size();\n        // For line-height, we want the fully resolved value, as `normal` also depends on other\n        // font properties.\n        let new_line_height = device\n            .calc_line_height(\n                &new_primary_style.get_font(),\n                new_primary_style.writing_mode,\n                None,\n            )\n            .0;\n        let old_line_height = old_style.map(|s| {\n            device\n                .calc_line_height(&s.get_font(), s.writing_mode, None)\n                .0\n        });\n\n        let font_size_changed = old_font_size != Some(new_font_size);\n        let line_height_changed = old_line_height != Some(new_line_height);\n\n        // Update root font-relative units. If any of these unit values changed\n        // since last time, ensure that we recascade the entire tree.\n        if is_root {\n            debug_assert!(self.owner_doc_matches_for_testing(device));\n            device.set_root_style(new_primary_style);\n\n            // Update root font size for rem units\n            if font_size_changed {\n                let size = new_font_size.computed_size();\n                device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));\n                if device.used_root_font_size() {\n                    child_restyle_hint |= RestyleHint::recascade_subtree();\n                }\n            }\n\n            // Update root line height for rlh units\n            if line_height_changed {\n                device.set_root_line_height(\n                    new_primary_style\n                        .effective_zoom\n                        .unzoom(new_line_height.px()),\n                );\n                if device.used_root_line_height() {\n                    child_restyle_hint |= RestyleHint::recascade_subtree();\n                }\n            }\n\n            // Update root font metrics for rcap, rch, rex, ric units. Since querying\n            // font metrics can be an expensive call, they are only updated if these\n            // units are used in the document.\n            if device.used_root_font_metrics() && device.update_root_font_metrics() {\n                child_restyle_hint |= RestyleHint::recascade_subtree()\n                    | RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS;\n            }\n        }\n\n        if font_size_changed || line_height_changed {\n            child_restyle_hint |= RestyleHint::RESTYLE_IF_AFFECTED_BY_ANCESTOR_FONT_METRICS;\n        }\n\n        if context.shared.stylist.quirks_mode() == QuirksMode::Quirks {\n            if self.is_html_document_body_element() {\n                // NOTE(emilio): We _could_ handle dynamic changes to it if it\n                // changes and before we reach our children the cascade stops,\n                // but we don't track right now whether we use the document body\n                // color, and nobody else handles that properly anyway.\n                let device = context.shared.stylist.device();\n\n                // Needed for the \"inherit from body\" quirk.\n                let text_color = new_primary_style.get_inherited_text().clone_color();\n                device.set_body_text_color(text_color);\n            }\n        }\n\n        // Don't accumulate damage if we're in the final animation traversal.\n        if context\n            .shared\n            .traversal_flags\n            .contains(TraversalFlags::FinalAnimationTraversal)\n        {\n            return RestyleHint::RECASCADE_SELF;\n        }\n\n        // Also, don't do anything if there was no style.\n        let old_primary_style = match old_styles.primary {\n            Some(s) => s,\n            None => return RestyleHint::RECASCADE_SELF,\n        };\n\n        let old_container_type = old_primary_style.clone_container_type();\n        let new_container_type = new_primary_style.clone_container_type();\n        if old_container_type != new_container_type && !new_container_type.is_size_container_type()\n        {\n            // Stopped being a size container. Re-evaluate container queries and units on all our descendants.\n            // Changes into and between different size containment is handled in `UpdateContainerQueryStyles`.\n            child_restyle_hint |= RestyleHint::restyle_subtree();\n        } else if old_container_type.is_size_container_type()\n            && !old_primary_style.is_display_contents()\n            && new_primary_style.is_display_contents()\n        {\n            // Also re-evaluate when a container gets 'display: contents', since size queries will now evaluate to unknown.\n            // Other displays like 'inline' will keep generating a box, so they are handled in `UpdateContainerQueryStyles`.\n            child_restyle_hint |= RestyleHint::restyle_subtree();\n        }\n\n        child_restyle_hint |= self.accumulate_damage_for(\n            context.shared,\n            &mut data.damage,\n            &old_primary_style,\n            new_primary_style,\n            None,\n        );\n\n        if data.styles.pseudos.is_empty() && old_styles.pseudos.is_empty() {\n            // This is the common case; no need to examine pseudos here.\n            return child_restyle_hint;\n        }\n\n        let pseudo_styles = old_styles\n            .pseudos\n            .as_array()\n            .iter()\n            .zip(data.styles.pseudos.as_array().iter());\n\n        for (i, (old, new)) in pseudo_styles.enumerate() {\n            match (old, new) {\n                (&Some(ref old), &Some(ref new)) => {\n                    self.accumulate_damage_for(\n                        context.shared,\n                        &mut data.damage,\n                        old,\n                        new,\n                        Some(&PseudoElement::from_eager_index(i)),\n                    );\n                },\n                (&None, &None) => {},\n                _ => {\n                    // It's possible that we're switching from not having\n                    // ::before/::after at all to having styles for them but not\n                    // actually having a useful pseudo-element.  Check for that\n                    // case.\n                    let pseudo = PseudoElement::from_eager_index(i);\n                    let new_pseudo_should_exist =\n                        new.as_ref().map_or(false, |s| pseudo.should_exist(s));\n                    let old_pseudo_should_exist =\n                        old.as_ref().map_or(false, |s| pseudo.should_exist(s));\n                    if new_pseudo_should_exist != old_pseudo_should_exist {\n                        data.damage |= RestyleDamage::reconstruct();\n                        return child_restyle_hint;\n                    }\n                },\n            }\n        }\n\n        child_restyle_hint\n    }\n\n    /// Updates the rule nodes without re-running selector matching, using just\n    /// the rule tree.\n    ///\n    /// Returns true if an !important rule was replaced.\n    fn replace_rules(\n        &self,\n        replacements: RestyleHint,\n        context: &mut StyleContext<Self>,\n        cascade_inputs: &mut ElementCascadeInputs,\n    ) -> bool {\n        let mut result = false;\n        result |= self.replace_rules_internal(\n            replacements,\n            context,\n            CascadeVisitedMode::Unvisited,\n            cascade_inputs,\n        );\n        result |= self.replace_rules_internal(\n            replacements,\n            context,\n            CascadeVisitedMode::Visited,\n            cascade_inputs,\n        );\n        result\n    }\n\n    /// Given the old and new style of this element, and whether it's a\n    /// pseudo-element, compute the restyle damage used to determine which\n    /// kind of layout or painting operations we'll need.\n    fn compute_style_difference(\n        &self,\n        old_values: &ComputedValues,\n        new_values: &ComputedValues,\n        pseudo: Option<&PseudoElement>,\n    ) -> StyleDifference {\n        debug_assert!(pseudo.map_or(true, |p| p.is_eager()));\n        #[cfg(feature = \"gecko\")]\n        {\n            RestyleDamage::compute_style_difference(old_values, new_values)\n        }\n        #[cfg(feature = \"servo\")]\n        {\n            RestyleDamage::compute_style_difference::<Self>(old_values, new_values)\n        }\n    }\n}\n\nimpl<E: TElement> MatchMethods for E {}\n"
  },
  {
    "path": "style/media_queries/media_list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A media query list:\n//!\n//! https://drafts.csswg.org/mediaqueries/#typedef-media-query-list\n\nuse super::{MediaQuery, Qualifier};\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::dom::AttributeTracker;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::ParserContext;\nuse crate::stylesheets::CustomMediaEvaluator;\nuse crate::values::computed;\nuse cssparser::{Delimiter, Parser};\nuse cssparser::{ParserInput, Token};\nuse selectors::kleene_value::KleeneValue;\n\n/// A type that encapsulates a media query list.\n#[derive(Clone, MallocSizeOf, ToCss, ToShmem)]\n#[css(comma, derive_debug)]\npub struct MediaList {\n    /// The list of media queries.\n    #[css(iterable)]\n    pub media_queries: Vec<MediaQuery>,\n}\n\nimpl MediaList {\n    /// Parse a media query list from CSS.\n    ///\n    /// Always returns a media query list. If any invalid media query is\n    /// found, the media query list is only filled with the equivalent of\n    /// \"not all\", see:\n    ///\n    /// <https://drafts.csswg.org/mediaqueries/#error-handling>\n    pub fn parse(context: &ParserContext, input: &mut Parser) -> Self {\n        if input.is_exhausted() {\n            return Self::empty();\n        }\n\n        let mut media_queries = vec![];\n        loop {\n            let start_position = input.position();\n            match input.parse_until_before(Delimiter::Comma, |i| MediaQuery::parse(context, i)) {\n                Ok(mq) => {\n                    media_queries.push(mq);\n                },\n                Err(err) => {\n                    media_queries.push(MediaQuery::never_matching());\n                    let location = err.location;\n                    let error = ContextualParseError::InvalidMediaRule(\n                        input.slice_from(start_position),\n                        err,\n                    );\n                    context.log_css_error(location, error);\n                },\n            }\n\n            match input.next() {\n                Ok(&Token::Comma) => {},\n                Ok(_) => unreachable!(),\n                Err(_) => break,\n            }\n        }\n\n        MediaList { media_queries }\n    }\n\n    /// Create an empty MediaList.\n    pub fn empty() -> Self {\n        MediaList {\n            media_queries: vec![],\n        }\n    }\n\n    /// Evaluate a whole `MediaList` against `Device`.\n    pub fn evaluate(\n        &self,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom: &mut CustomMediaEvaluator,\n    ) -> bool {\n        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {\n            self.matches(context, custom).to_bool(/* unknown = */ false)\n        })\n    }\n\n    /// Evaluate the current `MediaList` with a pre-existing context and custom-media evaluator.\n    pub fn matches(\n        &self,\n        context: &computed::Context,\n        custom: &mut CustomMediaEvaluator,\n    ) -> KleeneValue {\n        // Check if it is an empty media query list or any queries match.\n        // https://drafts.csswg.org/mediaqueries-4/#mq-list\n        if self.media_queries.is_empty() {\n            return KleeneValue::True;\n        }\n        KleeneValue::any(self.media_queries.iter(), |mq| {\n            let mut query_match = if mq.media_type.matches(context.device().media_type()) {\n                mq.condition.as_ref().map_or(KleeneValue::True, |c| {\n                    c.matches(context, custom, &mut AttributeTracker::new_dummy())\n                })\n            } else {\n                KleeneValue::False\n            };\n            // Apply the logical NOT qualifier to the result\n            if matches!(mq.qualifier, Some(Qualifier::Not)) {\n                query_match = !query_match;\n            }\n            query_match\n        })\n    }\n\n    /// Whether this `MediaList` contains no media queries.\n    pub fn is_empty(&self) -> bool {\n        self.media_queries.is_empty()\n    }\n\n    /// Whether this `MediaList` depends on the viewport size.\n    pub fn is_viewport_dependent(&self) -> bool {\n        self.media_queries.iter().any(|q| q.is_viewport_dependent())\n    }\n\n    /// Append a new media query item to the media list.\n    /// <https://drafts.csswg.org/cssom/#dom-medialist-appendmedium>\n    ///\n    /// Returns true if added, false if fail to parse the medium string.\n    pub fn append_medium(&mut self, context: &ParserContext, new_medium: &str) -> bool {\n        let mut input = ParserInput::new(new_medium);\n        let mut parser = Parser::new(&mut input);\n        let new_query = match MediaQuery::parse(&context, &mut parser) {\n            Ok(query) => query,\n            Err(_) => {\n                return false;\n            },\n        };\n        // This algorithm doesn't actually matches the current spec,\n        // but it matches the behavior of Gecko and Edge.\n        // See https://github.com/w3c/csswg-drafts/issues/697\n        self.media_queries.retain(|query| query != &new_query);\n        self.media_queries.push(new_query);\n        true\n    }\n\n    /// Delete a media query from the media list.\n    /// <https://drafts.csswg.org/cssom/#dom-medialist-deletemedium>\n    ///\n    /// Returns true if found and deleted, false otherwise.\n    pub fn delete_medium(&mut self, context: &ParserContext, old_medium: &str) -> bool {\n        let mut input = ParserInput::new(old_medium);\n        let mut parser = Parser::new(&mut input);\n        let old_query = match MediaQuery::parse(context, &mut parser) {\n            Ok(query) => query,\n            Err(_) => {\n                return false;\n            },\n        };\n        let old_len = self.media_queries.len();\n        self.media_queries.retain(|query| query != &old_query);\n        old_len != self.media_queries.len()\n    }\n}\n"
  },
  {
    "path": "style/media_queries/media_query.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A media query:\n//!\n//! https://drafts.csswg.org/mediaqueries/#typedef-media-query\n\nuse crate::derives::*;\nuse crate::parser::ParserContext;\nuse crate::queries::{FeatureFlags, FeatureType, QueryCondition};\nuse crate::str::string_as_ascii_lowercase;\nuse crate::values::CustomIdent;\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// <https://drafts.csswg.org/mediaqueries/#mq-prefix>\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\npub enum Qualifier {\n    /// Hide a media query from legacy UAs:\n    /// <https://drafts.csswg.org/mediaqueries/#mq-only>\n    Only,\n    /// Negate a media query:\n    /// <https://drafts.csswg.org/mediaqueries/#mq-not>\n    Not,\n}\n\n/// <https://drafts.csswg.org/mediaqueries/#media-types>\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub struct MediaType(pub CustomIdent);\n\nimpl MediaType {\n    /// The `screen` media type.\n    pub fn screen() -> Self {\n        MediaType(CustomIdent(atom!(\"screen\")))\n    }\n\n    /// The `print` media type.\n    pub fn print() -> Self {\n        MediaType(CustomIdent(atom!(\"print\")))\n    }\n\n    fn parse(name: &str) -> Result<Self, ()> {\n        // From https://drafts.csswg.org/mediaqueries/#mq-syntax:\n        //\n        //   The <media-type> production does not include the keywords only, not, and, or, and layer.\n        //\n        // Here we also perform the to-ascii-lowercase part of the serialization\n        // algorithm: https://drafts.csswg.org/cssom/#serializing-media-queries\n        match_ignore_ascii_case! { name,\n            \"not\" | \"or\" | \"and\" | \"only\" | \"layer\" => Err(()),\n            _ => Ok(MediaType(CustomIdent(Atom::from(string_as_ascii_lowercase(name))))),\n        }\n    }\n}\n\n/// A [media query][mq].\n///\n/// [mq]: https://drafts.csswg.org/mediaqueries/\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct MediaQuery {\n    /// The qualifier for this query.\n    pub qualifier: Option<Qualifier>,\n    /// The media type for this query, that can be known, unknown, or \"all\".\n    pub media_type: MediaQueryType,\n    /// The condition that this media query contains. This cannot have `or`\n    /// in the first level.\n    pub condition: Option<QueryCondition>,\n}\n\nimpl ToCss for MediaQuery {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if let Some(qual) = self.qualifier {\n            qual.to_css(dest)?;\n            dest.write_char(' ')?;\n        }\n\n        match self.media_type {\n            MediaQueryType::All => {\n                // We need to print \"all\" if there's a qualifier, or there's\n                // just an empty list of expressions.\n                //\n                // Otherwise, we'd serialize media queries like \"(min-width:\n                // 40px)\" in \"all (min-width: 40px)\", which is unexpected.\n                if self.qualifier.is_some() || self.condition.is_none() {\n                    dest.write_str(\"all\")?;\n                }\n            },\n            MediaQueryType::Concrete(MediaType(ref desc)) => desc.to_css(dest)?,\n        }\n\n        let condition = match self.condition {\n            Some(ref c) => c,\n            None => return Ok(()),\n        };\n\n        if self.media_type != MediaQueryType::All || self.qualifier.is_some() {\n            dest.write_str(\" and \")?;\n        }\n\n        condition.to_css(dest)\n    }\n}\n\nimpl MediaQuery {\n    /// Return a media query that never matches, used for when we fail to parse\n    /// a given media query.\n    pub fn never_matching() -> Self {\n        Self {\n            qualifier: Some(Qualifier::Not),\n            media_type: MediaQueryType::All,\n            condition: None,\n        }\n    }\n\n    /// Returns whether this media query depends on the viewport.\n    pub fn is_viewport_dependent(&self) -> bool {\n        self.condition.as_ref().map_or(false, |c| {\n            return c\n                .cumulative_flags()\n                .contains(FeatureFlags::VIEWPORT_DEPENDENT);\n        })\n    }\n\n    /// Parse a media query given css input.\n    ///\n    /// Returns an error if any of the expressions is unknown.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let (qualifier, explicit_media_type) = input\n            .try_parse(|input| -> Result<_, ()> {\n                let qualifier = input.try_parse(Qualifier::parse).ok();\n                let ident = input.expect_ident().map_err(|_| ())?;\n                let media_type = MediaQueryType::parse(&ident)?;\n                Ok((qualifier, Some(media_type)))\n            })\n            .unwrap_or_default();\n\n        let condition = if explicit_media_type.is_none() {\n            Some(QueryCondition::parse(context, input, FeatureType::Media)?)\n        } else if input.try_parse(|i| i.expect_ident_matching(\"and\")).is_ok() {\n            Some(QueryCondition::parse_disallow_or(\n                context,\n                input,\n                FeatureType::Media,\n            )?)\n        } else {\n            None\n        };\n\n        let media_type = explicit_media_type.unwrap_or(MediaQueryType::All);\n        Ok(Self {\n            qualifier,\n            media_type,\n            condition,\n        })\n    }\n}\n\n/// <http://dev.w3.org/csswg/mediaqueries-3/#media0>\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub enum MediaQueryType {\n    /// A media type that matches every device.\n    All,\n    /// A specific media type.\n    Concrete(MediaType),\n}\n\nimpl MediaQueryType {\n    fn parse(ident: &str) -> Result<Self, ()> {\n        match_ignore_ascii_case! { ident,\n            \"all\" => return Ok(MediaQueryType::All),\n            _ => (),\n        };\n\n        // If parseable, accept this type as a concrete type.\n        MediaType::parse(ident).map(MediaQueryType::Concrete)\n    }\n\n    /// Returns whether this media query type matches a MediaType.\n    pub fn matches(&self, other: MediaType) -> bool {\n        match *self {\n            MediaQueryType::All => true,\n            MediaQueryType::Concrete(ref known_type) => *known_type == other,\n        }\n    }\n}\n"
  },
  {
    "path": "style/media_queries/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [Media queries][mq].\n//!\n//! [mq]: https://drafts.csswg.org/mediaqueries/\n\nmod media_list;\nmod media_query;\n\npub use self::media_list::MediaList;\npub use self::media_query::{MediaQuery, MediaQueryType, MediaType, Qualifier};\n"
  },
  {
    "path": "style/parallel.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Implements parallel traversal over the DOM tree.\n//!\n//! This traversal is based on Rayon, and therefore its safety is largely\n//! verified by the type system.\n//!\n//! The primary trickiness and fine print for the above relates to the\n//! thread safety of the DOM nodes themselves. Accessing a DOM element\n//! concurrently on multiple threads is actually mostly \"safe\", since all\n//! the mutable state is protected by an AtomicRefCell, and so we'll\n//! generally panic if something goes wrong. Still, we try to to enforce our\n//! thread invariants at compile time whenever possible. As such, TNode and\n//! TElement are not Send, so ordinary style system code cannot accidentally\n//! share them with other threads. In the parallel traversal, we explicitly\n//! invoke |unsafe { SendNode::new(n) }| to put nodes in containers that may\n//! be sent to other threads. This occurs in only a handful of places and is\n//! easy to grep for. At the time of this writing, there is no other unsafe\n//! code in the parallel traversal.\n\n#![deny(missing_docs)]\n\nuse crate::context::{StyleContext, ThreadLocalStyleContext};\nuse crate::dom::{OpaqueNode, SendNode, TElement};\nuse crate::scoped_tls::ScopedTLS;\nuse crate::traversal::{DomTraversal, PerLevelTraversalData};\nuse std::collections::VecDeque;\n\n/// The minimum stack size for a thread in the styling pool, in kilobytes.\n#[cfg(feature = \"gecko\")]\npub const STYLE_THREAD_STACK_SIZE_KB: usize = 256;\n\n/// The minimum stack size for a thread in the styling pool, in kilobytes.\n/// Servo requires a bigger stack in debug builds.\n/// We allow configuring the size, since running with ASAN requires an even larger\n/// stack size.\n#[cfg(feature = \"servo\")]\npub const STYLE_THREAD_STACK_SIZE_KB: usize = const {\n    let default_stack_size = 512;\n    if let Some(user_def_size) = option_env!(\"SERVO_STYLE_THREAD_STACK_SIZE_KB\") {\n        if let Ok(user_def_size) = usize::from_str_radix(user_def_size, 10) {\n            user_def_size\n        } else {\n            panic!(\"SERVO_STYLE_THREAD_STACK_SIZE_KB must be a valid integer\")\n        }\n    } else {\n        default_stack_size\n    }\n};\n\n/// The stack margin. If we get this deep in the stack, we will skip recursive\n/// optimizations to ensure that there is sufficient room for non-recursive work.\n///\n/// We allocate large safety margins because certain OS calls can use very large\n/// amounts of stack space [1]. Reserving a larger-than-necessary stack costs us\n/// address space, but if we keep our safety margin big, we will generally avoid\n/// committing those extra pages, and only use them in edge cases that would\n/// otherwise cause crashes.\n///\n/// When measured with 128KB stacks and 40KB margin, we could support 53\n/// levels of recursion before the limiter kicks in, on x86_64-Linux [2]. When\n/// we doubled the stack size, we added it all to the safety margin, so we should\n/// be able to get the same amount of recursion.\n///\n/// [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1395708#c15\n/// [2] See Gecko bug 1376883 for more discussion on the measurements.\npub const STACK_SAFETY_MARGIN_KB: usize = 168;\n\n/// A callback to create our thread local context.  This needs to be\n/// out of line so we don't allocate stack space for the entire struct\n/// in the caller.\n#[inline(never)]\npub(crate) fn create_thread_local_context<'scope, E>(slot: &mut Option<ThreadLocalStyleContext<E>>)\nwhere\n    E: TElement + 'scope,\n{\n    *slot = Some(ThreadLocalStyleContext::new());\n}\n\n// Sends one chunk of work to the thread-pool.\nfn distribute_one_chunk<'a, 'scope, E, D>(\n    items: VecDeque<SendNode<E::ConcreteNode>>,\n    traversal_root: OpaqueNode,\n    work_unit_max: usize,\n    traversal_data: PerLevelTraversalData,\n    scope: &'a rayon::ScopeFifo<'scope>,\n    traversal: &'scope D,\n    tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,\n) where\n    E: TElement + 'scope,\n    D: DomTraversal<E>,\n{\n    scope.spawn_fifo(move |scope| {\n        #[cfg(feature = \"gecko\")]\n        gecko_profiler_label!(Layout, StyleComputation);\n        let mut tlc = tls.ensure(create_thread_local_context);\n        let mut context = StyleContext {\n            shared: traversal.shared_context(),\n            thread_local: &mut *tlc,\n        };\n        style_trees(\n            &mut context,\n            items,\n            traversal_root,\n            work_unit_max,\n            traversal_data,\n            Some(scope),\n            traversal,\n            tls,\n        );\n    })\n}\n\n/// Distributes all items into the thread pool, in `work_unit_max` chunks.\nfn distribute_work<'a, 'scope, E, D>(\n    mut items: impl Iterator<Item = SendNode<E::ConcreteNode>>,\n    traversal_root: OpaqueNode,\n    work_unit_max: usize,\n    traversal_data: PerLevelTraversalData,\n    scope: &'a rayon::ScopeFifo<'scope>,\n    traversal: &'scope D,\n    tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,\n) where\n    E: TElement + 'scope,\n    D: DomTraversal<E>,\n{\n    use std::iter::FromIterator;\n    loop {\n        let chunk = VecDeque::from_iter(items.by_ref().take(work_unit_max));\n        if chunk.is_empty() {\n            return;\n        }\n        distribute_one_chunk(\n            chunk,\n            traversal_root,\n            work_unit_max,\n            traversal_data,\n            scope,\n            traversal,\n            tls,\n        );\n    }\n}\n\n/// Processes `discovered` items, possibly spawning work in other threads as needed.\n#[inline]\npub fn style_trees<'a, 'scope, E, D>(\n    context: &mut StyleContext<E>,\n    mut discovered: VecDeque<SendNode<E::ConcreteNode>>,\n    traversal_root: OpaqueNode,\n    work_unit_max: usize,\n    mut traversal_data: PerLevelTraversalData,\n    scope: Option<&'a rayon::ScopeFifo<'scope>>,\n    traversal: &'scope D,\n    tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>,\n) where\n    E: TElement + 'scope,\n    D: DomTraversal<E>,\n{\n    let local_queue_size = if tls.current_thread_index() == 0 {\n        static_prefs::pref!(\"layout.css.stylo-local-work-queue.in-main-thread\")\n    } else {\n        static_prefs::pref!(\"layout.css.stylo-local-work-queue.in-worker\")\n    } as usize;\n\n    let mut nodes_remaining_at_current_depth = discovered.len();\n    while let Some(node) = discovered.pop_front() {\n        let mut children_to_process = 0isize;\n        traversal.process_preorder(&traversal_data, context, *node, |n| {\n            children_to_process += 1;\n            discovered.push_back(unsafe { SendNode::new(n) });\n        });\n\n        traversal.handle_postorder_traversal(context, traversal_root, *node, children_to_process);\n\n        nodes_remaining_at_current_depth -= 1;\n\n        // If we have enough children at the next depth in the DOM, spawn them to a different job\n        // relatively soon, while keeping always at least `local_queue_size` worth of work for\n        // ourselves.\n        let discovered_children = discovered.len() - nodes_remaining_at_current_depth;\n        if discovered_children >= work_unit_max\n            && discovered.len() >= local_queue_size + work_unit_max\n            && scope.is_some()\n        {\n            let kept_work = std::cmp::max(nodes_remaining_at_current_depth, local_queue_size);\n            let mut traversal_data_copy = traversal_data.clone();\n            traversal_data_copy.current_dom_depth += 1;\n            distribute_work(\n                discovered.range(kept_work..).cloned(),\n                traversal_root,\n                work_unit_max,\n                traversal_data_copy,\n                scope.unwrap(),\n                traversal,\n                tls,\n            );\n            discovered.truncate(kept_work);\n        }\n\n        if nodes_remaining_at_current_depth == 0 {\n            traversal_data.current_dom_depth += 1;\n            nodes_remaining_at_current_depth = discovered.len();\n        }\n    }\n}\n"
  },
  {
    "path": "style/parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The context within which CSS code is parsed.\n\nuse crate::context::QuirksMode;\nuse crate::custom_properties::{AttrTaint, AttrTaintedRange};\nuse crate::error_reporting::{ContextualParseError, ParseErrorReporter};\nuse crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};\nuse crate::use_counters::UseCounters;\nuse cssparser::{Parser, SourceLocation, UnicodeRange};\nuse selectors::parser::ParseRelative;\nuse std::borrow::Cow;\nuse style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};\n\n/// Nesting context for parsing rules.\n#[derive(Clone, Copy)]\npub struct NestingContext {\n    /// All rule types we've nested into, if any.\n    pub rule_types: CssRuleTypes,\n    /// Whether or not parsing relative selector syntax should be allowed.\n    pub parse_relative: ParseRelative,\n}\n\nimpl NestingContext {\n    fn parse_relative_for(rule_type: CssRuleType) -> ParseRelative {\n        match rule_type {\n            CssRuleType::Scope => ParseRelative::ForScope,\n            CssRuleType::Style => ParseRelative::ForNesting,\n            _ => ParseRelative::No,\n        }\n    }\n\n    /// Create a new nesting context.\n    pub fn new(rule_types: CssRuleTypes, parse_nested_rule_type: Option<CssRuleType>) -> Self {\n        Self {\n            rule_types,\n            parse_relative: parse_nested_rule_type\n                .map_or(ParseRelative::No, Self::parse_relative_for),\n        }\n    }\n\n    /// Create a new nesting context based on the given rule.\n    pub fn new_from_rule(rule_type: Option<CssRuleType>) -> Self {\n        Self {\n            rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),\n            parse_relative: rule_type\n                .map(Self::parse_relative_for)\n                .unwrap_or(ParseRelative::No),\n        }\n    }\n\n    /// Save the current nesting context.\n    pub fn save(&mut self, rule_type: CssRuleType) -> Self {\n        let old = *self;\n        self.rule_types.insert(rule_type);\n        let new_parse_relative = Self::parse_relative_for(rule_type);\n        if new_parse_relative != ParseRelative::No {\n            self.parse_relative = new_parse_relative;\n        }\n        old\n    }\n\n    /// Load the saved nesting context.\n    pub fn restore(&mut self, saved: Self) {\n        *self = saved;\n    }\n}\n\n/// The data that the parser needs from outside in order to parse a stylesheet.\npub struct ParserContext<'a> {\n    /// The `Origin` of the stylesheet, whether it's a user, author or\n    /// user-agent stylesheet.\n    pub stylesheet_origin: Origin,\n    /// The extra data we need for resolving url values.\n    pub url_data: &'a UrlExtraData,\n    /// The mode to use when parsing.\n    pub parsing_mode: ParsingMode,\n    /// The quirks mode of this stylesheet.\n    pub quirks_mode: QuirksMode,\n    /// The active error reporter, or none if error reporting is disabled.\n    error_reporter: Option<&'a dyn ParseErrorReporter>,\n    /// The currently active namespaces.\n    pub namespaces: Cow<'a, Namespaces>,\n    /// The use counters we want to record while parsing style rules, if any.\n    pub use_counters: Option<&'a UseCounters>,\n    /// Current nesting context.\n    pub nesting_context: NestingContext,\n    /// The relevant regions in the input that have been tainted by attr() if relevant.\n    pub attr_tainted_regions: AttrTaint,\n}\n\nimpl<'a> ParserContext<'a> {\n    /// Create a parser context.\n    #[inline]\n    pub fn new(\n        stylesheet_origin: Origin,\n        url_data: &'a UrlExtraData,\n        rule_type: Option<CssRuleType>,\n        parsing_mode: ParsingMode,\n        quirks_mode: QuirksMode,\n        namespaces: Cow<'a, Namespaces>,\n        error_reporter: Option<&'a dyn ParseErrorReporter>,\n        use_counters: Option<&'a UseCounters>,\n        attr_tainted_regions: AttrTaint,\n    ) -> Self {\n        Self {\n            stylesheet_origin,\n            url_data,\n            parsing_mode,\n            quirks_mode,\n            error_reporter,\n            namespaces,\n            use_counters,\n            nesting_context: NestingContext::new_from_rule(rule_type),\n            attr_tainted_regions,\n        }\n    }\n\n    /// Temporarily sets the rule_type and executes the callback function, returning its result.\n    pub fn nest_for_rule<R>(\n        &mut self,\n        rule_type: CssRuleType,\n        cb: impl FnOnce(&mut Self) -> R,\n    ) -> R {\n        let old = self.nesting_context.save(rule_type);\n        let r = cb(self);\n        self.nesting_context.restore(old);\n        r\n    }\n\n    /// Whether we're in a @page rule.\n    #[inline]\n    pub fn in_page_rule(&self) -> bool {\n        self.nesting_context.rule_types.contains(CssRuleType::Page)\n    }\n\n    /// Returns whether !important declarations are forbidden.\n    #[inline]\n    pub fn allows_important_declarations(&self) -> bool {\n        !self\n            .nesting_context\n            .rule_types\n            .intersects(CssRuleTypes::IMPORTANT_FORBIDDEN)\n    }\n\n    /// Get the rule type, which assumes that one is available.\n    pub fn rule_types(&self) -> CssRuleTypes {\n        self.nesting_context.rule_types\n    }\n\n    /// Returns whether CSS error reporting is enabled.\n    #[inline]\n    pub fn error_reporting_enabled(&self) -> bool {\n        self.error_reporter.is_some()\n    }\n\n    /// Record a CSS parse error with this context’s error reporting.\n    pub fn log_css_error(&self, location: SourceLocation, error: ContextualParseError) {\n        let error_reporter = match self.error_reporter {\n            Some(r) => r,\n            None => return,\n        };\n\n        error_reporter.report_error(self.url_data, location, error)\n    }\n\n    /// Whether we're in a user-agent stylesheet.\n    #[inline]\n    pub fn in_ua_sheet(&self) -> bool {\n        self.stylesheet_origin == Origin::UserAgent\n    }\n\n    /// Returns whether chrome-only rules should be parsed.\n    #[inline]\n    pub fn chrome_rules_enabled(&self) -> bool {\n        self.url_data.chrome_rules_enabled() || self.stylesheet_origin != Origin::Author\n    }\n\n    /// Whether the parsing mode allows units or functions that are not computationally independent.\n    #[inline]\n    pub fn allows_computational_dependence(&self) -> bool {\n        self.parsing_mode.allows_computational_dependence()\n    }\n\n    /// Whether any `<url>` over this `range` is disallowed due to attr()-tainting.\n    pub fn disallow_urls_in_range(&self, range: &AttrTaintedRange) -> bool {\n        self.attr_tainted_regions\n            .should_disallow_urls_in_range(range)\n    }\n}\n\n/// A trait to abstract parsing of a specified value given a `ParserContext` and\n/// CSS input.\n///\n/// This can be derived on keywords with `#[derive(Parse)]`.\n///\n/// The derive code understands the following attributes on each of the variants:\n///\n///  * `#[parse(aliases = \"foo,bar\")]` can be used to alias a value with another\n///    at parse-time.\n///\n///  * `#[parse(condition = \"function\")]` can be used to make the parsing of the\n///    value conditional on `function`, which needs to fulfill\n///    `fn(&ParserContext) -> bool`.\n///\n///  * `#[parse(parse_fn = \"function\")]` can be used to specify a function other than Parser::parse\n///    for a particular variant.\npub trait Parse: Sized {\n    /// Parse a value of this type.\n    ///\n    /// Returns an error on failure.\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>>;\n}\n\nimpl<T> Parse for Vec<T>\nwhere\n    T: Parse + OneOrMoreSeparated,\n    <T as OneOrMoreSeparated>::S: Separator,\n{\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        <T as OneOrMoreSeparated>::S::parse(input, |i| T::parse(context, i))\n    }\n}\n\nimpl<T> Parse for Box<T>\nwhere\n    T: Parse,\n{\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        T::parse(context, input).map(Box::new)\n    }\n}\n\nimpl Parse for crate::OwnedStr {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(input.expect_string()?.as_ref().to_owned().into())\n    }\n}\n\nimpl Parse for UnicodeRange {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(UnicodeRange::parse(input)?)\n    }\n}\n"
  },
  {
    "path": "style/piecewise_linear.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A piecewise linear function, following CSS linear easing\nuse crate::derives::*;\nuse crate::values::computed::Percentage;\nuse core::slice::Iter;\n/// draft as in https://github.com/w3c/csswg-drafts/pull/6533.\nuse euclid::approxeq::ApproxEq;\nuse itertools::Itertools;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\nuse crate::values::CSSFloat;\n\ntype ValueType = CSSFloat;\n/// a single entry in a piecewise linear function.\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToResolvedValue,\n    ToShmem,\n    Serialize,\n    Deserialize,\n)]\n#[repr(C)]\npub struct PiecewiseLinearFunctionEntry {\n    pub x: ValueType,\n    pub y: ValueType,\n}\n\nimpl ToCss for PiecewiseLinearFunctionEntry {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.y.to_css(dest)?;\n        dest.write_char(' ')?;\n        Percentage(self.x).to_css(dest)\n    }\n}\n\n/// Representation of a piecewise linear function, a series of linear functions.\n#[derive(\n    Default,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToResolvedValue,\n    ToCss,\n    ToShmem,\n    Serialize,\n    Deserialize,\n)]\n#[repr(C)]\n#[css(comma)]\npub struct PiecewiseLinearFunction {\n    #[css(iterable)]\n    #[ignore_malloc_size_of = \"Arc\"]\n    #[shmem(field_bound)]\n    entries: crate::ArcSlice<PiecewiseLinearFunctionEntry>,\n}\n\n/// Parameters to define one linear stop.\npub type PiecewiseLinearFunctionBuildParameters = (CSSFloat, Option<CSSFloat>);\n\nimpl PiecewiseLinearFunction {\n    /// Interpolate y value given x and two points. The linear function will be rooted at the asymptote.\n    fn interpolate(\n        x: ValueType,\n        prev: PiecewiseLinearFunctionEntry,\n        next: PiecewiseLinearFunctionEntry,\n        asymptote: &PiecewiseLinearFunctionEntry,\n    ) -> ValueType {\n        // Short circuit if the x is on prev or next.\n        // `next` point is preferred as per spec.\n        if x.approx_eq(&next.x) {\n            return next.y;\n        }\n        if x.approx_eq(&prev.x) {\n            return prev.y;\n        }\n        // Avoid division by zero.\n        if prev.x.approx_eq(&next.x) {\n            return next.y;\n        }\n        let slope = (next.y - prev.y) / (next.x - prev.x);\n        return slope * (x - asymptote.x) + asymptote.y;\n    }\n\n    /// Get the y value of the piecewise linear function given the x value, as per\n    /// https://drafts.csswg.org/css-easing-2/#linear-easing-function-output\n    pub fn at(&self, x: ValueType) -> ValueType {\n        if !x.is_finite() {\n            return if x > 0.0 { 1.0 } else { 0.0 };\n        }\n        if self.entries.is_empty() {\n            // Implied y = x, as per spec.\n            return x;\n        }\n        if self.entries.len() == 1 {\n            // Implied y = <constant>, as per spec.\n            return self.entries[0].y;\n        }\n        // Spec dictates the valid input domain is [0, 1]. Outside of this range, the output\n        // should be calculated as if the slopes at start and end extend to infinity. However, if the\n        // start/end have two points of the same position, the line should extend along the x-axis.\n        // The function doesn't have to cover the input domain, in which case the extension logic\n        // applies even if the input falls in the input domain.\n        // Also, we're guaranteed to have at least two elements at this point.\n        if x < self.entries[0].x {\n            return Self::interpolate(x, self.entries[0], self.entries[1], &self.entries[0]);\n        }\n        let mut rev_iter = self.entries.iter().rev();\n        let last = rev_iter.next().unwrap();\n        if x >= last.x {\n            let second_last = rev_iter.next().unwrap();\n            return Self::interpolate(x, *second_last, *last, last);\n        }\n\n        // Now we know the input sits within the domain explicitly defined by our function.\n        for (point_b, point_a) in self.entries.iter().rev().tuple_windows() {\n            // Need to let point A be the _last_ point where its x is less than the input x,\n            // hence the reverse traversal.\n            if x < point_a.x {\n                continue;\n            }\n            return Self::interpolate(x, *point_a, *point_b, point_a);\n        }\n        unreachable!(\"Input is supposed to be within the entries' min & max!\");\n    }\n\n    #[allow(missing_docs)]\n    pub fn iter(&self) -> Iter<'_, PiecewiseLinearFunctionEntry> {\n        self.entries.iter()\n    }\n}\n\n/// Entry of a piecewise linear function while building, where the calculation of x value can be deferred.\n#[derive(Clone, Copy)]\nstruct BuildEntry {\n    x: Option<ValueType>,\n    y: ValueType,\n}\n\n/// Builder object to generate a linear function.\n#[derive(Default)]\npub struct PiecewiseLinearFunctionBuilder {\n    largest_x: Option<ValueType>,\n    smallest_x: Option<ValueType>,\n    entries: Vec<BuildEntry>,\n}\n\nimpl PiecewiseLinearFunctionBuilder {\n    /// Create a builder for a known amount of linear stop entries.\n    pub fn with_capacity(len: usize) -> Self {\n        PiecewiseLinearFunctionBuilder {\n            largest_x: None,\n            smallest_x: None,\n            entries: Vec::with_capacity(len),\n        }\n    }\n\n    fn create_entry(&mut self, y: ValueType, x: Option<ValueType>) {\n        let x = match x {\n            Some(x) if x.is_finite() => x,\n            _ if self.entries.is_empty() => 0.0, // First x is 0 if not specified (Or not finite)\n            _ => {\n                self.entries.push(BuildEntry { x: None, y });\n                return;\n            },\n        };\n        // Specified x value cannot regress, as per spec.\n        let x = match self.largest_x {\n            Some(largest_x) => x.max(largest_x),\n            None => x,\n        };\n        self.largest_x = Some(x);\n        // Whatever we see the earliest is the smallest value.\n        if self.smallest_x.is_none() {\n            self.smallest_x = Some(x);\n        }\n        self.entries.push(BuildEntry { x: Some(x), y });\n    }\n\n    /// Add a new entry into the piecewise linear function with specified y value.\n    /// If the start x value is given, that is where the x value will be. Otherwise,\n    /// the x value is calculated later. If the end x value is specified, a flat segment\n    /// is generated. If start x value is not specified but end x is, it is treated as\n    /// start x.\n    pub fn push(&mut self, y: CSSFloat, x_start: Option<CSSFloat>) {\n        self.create_entry(y, x_start)\n    }\n\n    /// Finish building the piecewise linear function by resolving all undefined x values,\n    /// then return the result.\n    pub fn build(mut self) -> PiecewiseLinearFunction {\n        if self.entries.is_empty() {\n            return PiecewiseLinearFunction::default();\n        }\n        if self.entries.len() == 1 {\n            // Don't bother resolving anything.\n            return PiecewiseLinearFunction {\n                entries: crate::ArcSlice::from_iter(std::iter::once(\n                    PiecewiseLinearFunctionEntry {\n                        x: 0.,\n                        y: self.entries[0].y,\n                    },\n                )),\n            };\n        }\n        // Guaranteed at least two elements.\n        // Start element's x value should've been assigned when the first value was pushed.\n        debug_assert!(\n            self.entries[0].x.is_some(),\n            \"Expected an entry with x defined!\"\n        );\n        // Spec asserts that if the last entry does not have an x value, it is assigned the largest seen x value.\n        self.entries\n            .last_mut()\n            .unwrap()\n            .x\n            .get_or_insert(self.largest_x.filter(|x| x > &1.0).unwrap_or(1.0));\n        // Now we have at least two elements with x values, with start & end x values guaranteed.\n\n        let mut result = Vec::with_capacity(self.entries.len());\n        result.push(PiecewiseLinearFunctionEntry {\n            x: self.entries[0].x.unwrap(),\n            y: self.entries[0].y,\n        });\n        for (i, e) in self.entries.iter().enumerate().skip(1) {\n            if e.x.is_none() {\n                // Need to calculate x values by first finding an entry with the first\n                // defined x value (Guaranteed to exist as the list end has it defined).\n                continue;\n            }\n            // x is defined for this element.\n            let divisor = i - result.len() + 1;\n            // Any element(s) with undefined x to assign?\n            if divisor != 1 {\n                // Have at least one element in result at all times.\n                let start_x = result.last().unwrap().x;\n                let increment = (e.x.unwrap() - start_x) / divisor as ValueType;\n                // Grab every element with undefined x to this point, which starts at the end of the result\n                // array, and ending right before the current index. Then, assigned the evenly divided\n                // x values.\n                result.extend(\n                    self.entries[result.len()..i]\n                        .iter()\n                        .enumerate()\n                        .map(|(j, e)| {\n                            debug_assert!(e.x.is_none(), \"Expected an entry with x undefined!\");\n                            PiecewiseLinearFunctionEntry {\n                                x: increment * (j + 1) as ValueType + start_x,\n                                y: e.y,\n                            }\n                        }),\n                );\n            }\n            result.push(PiecewiseLinearFunctionEntry {\n                x: e.x.unwrap(),\n                y: e.y,\n            });\n        }\n        debug_assert_eq!(\n            result.len(),\n            self.entries.len(),\n            \"Should've mapped one-to-one!\"\n        );\n        PiecewiseLinearFunction {\n            entries: crate::ArcSlice::from_iter(result.into_iter()),\n        }\n    }\n}\n"
  },
  {
    "path": "style/properties/build.py",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nimport json\nimport os.path\nimport re\nimport sys\n\nBASE = os.path.dirname(__file__.replace(\"\\\\\", \"/\"))\nsys.path.insert(0, os.path.join(BASE, \"vendored_python\", \"mako-1.3.10-py3-none-any.whl\"))\nsys.path.insert(0, os.path.join(BASE, \"vendored_python\", \"toml-0.10.2-py2.py3-none-any.whl\"))\nsys.path.insert(0, os.path.join(BASE, \"vendored_python\")) # For importing markupsafe\nsys.path.insert(0, BASE)  # For importing `data.py`\n\nfrom mako import exceptions\nfrom mako.lookup import TemplateLookup\nfrom mako.template import Template\n\nimport data\n\nRE_PYTHON_ADDR = re.compile(r\"<.+? object at 0x[0-9a-fA-F]+>\")\n\nOUT_DIR = os.environ.get(\"OUT_DIR\", \"\")\n\n\ndef main():\n    usage = (\n        \"Usage: %s [ servo | gecko ]\"\n        % sys.argv[0]\n    )\n    if len(sys.argv) < 2:\n        abort(usage)\n    engine = sys.argv[1]\n\n    if engine not in [\"servo\", \"gecko\"]:\n        abort(usage)\n\n    properties = data.PropertiesData(engine)\n    properties_template = os.path.join(BASE, \"properties.mako.rs\")\n    properties_file = render(\n        properties_template,\n        engine=engine,\n        data=properties,\n        __file__=properties_template,\n        OUT_DIR=OUT_DIR,\n    )\n    write(OUT_DIR, \"properties.rs\", properties_file)\n\n    if engine != \"servo\":\n        return\n\n    properties_dict = {\n        kind: {\n            p.name: {\"pref\": getattr(p, \"servo_pref\")}\n            for prop in properties_list\n            if prop.enabled_in_content()\n            for p in [prop] + prop.aliases\n        }\n        for kind, properties_list in [\n            (\"longhands\", properties.longhands),\n            (\"shorthands\", properties.shorthands),\n        ]\n    }\n    as_html = render(\n        os.path.join(BASE, \"properties.html.mako\"), properties=properties_dict\n    )\n    as_json = json.dumps(properties_dict, indent=4, sort_keys=True)\n\n    # Four dotdots: /path/to/target(4)/debug(3)/build(2)/style-*(1)/out\n    # Do not ascend above the target dir, because it may not be called target\n    # or even have a parent (see CARGO_TARGET_DIR).\n    doc_servo = os.path.join(OUT_DIR, \"..\", \"..\", \"..\", \"..\", \"doc\", \"stylo\")\n\n    write(doc_servo, \"css-properties.html\", as_html)\n    write(doc_servo, \"css-properties.json\", as_json)\n    write(OUT_DIR, \"css-properties.json\", as_json)\n    write(OUT_DIR, \"css-properties.html\", as_html)\n\n\ndef abort(message):\n    print(message, file=sys.stderr)\n    sys.exit(1)\n\n\ndef render(filename, **context):\n    try:\n        lookup = TemplateLookup(\n            directories=[BASE], input_encoding=\"utf8\", strict_undefined=True\n        )\n        template = Template(\n            open(filename, \"rb\").read(),\n            filename=filename,\n            input_encoding=\"utf8\",\n            lookup=lookup,\n            strict_undefined=True,\n        )\n        # Uncomment to debug generated Python code:\n        # write(\"/tmp\", \"mako_%s.py\" % os.path.basename(filename), template.code)\n        return template.render(**context)\n    except Exception:\n        # Uncomment to see a traceback in generated Python code:\n        # raise\n        abort(exceptions.text_error_template().render())\n\n\ndef write(directory, filename, content):\n    if not os.path.exists(directory):\n        os.makedirs(directory)\n    full_path = os.path.join(directory, filename)\n    open(full_path, \"w\", encoding=\"utf-8\", newline=\"\").write(content)\n\n    python_addr = RE_PYTHON_ADDR.search(content)\n    if python_addr:\n        abort('Found \"{}\" in {} ({})'.format(python_addr.group(0), filename, full_path))\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "style/properties/cascade.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The main cascading algorithm of the style system.\n\nuse crate::applicable_declarations::{CascadePriority, RevertKind};\nuse crate::color::AbsoluteColor;\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::custom_properties::{\n    CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution,\n};\nuse crate::dom::{AttributeTracker, TElement};\n#[cfg(feature = \"gecko\")]\nuse crate::font_metrics::FontMetricsOrientation;\nuse crate::logical_geometry::WritingMode;\nuse crate::properties::{\n    CASCADE_PROPERTY, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, LonghandId,\n    LonghandIdSet, PrioritaryPropertyId, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,\n    ShorthandsWithPropertyReferencesCache, StyleBuilder, property_counts,\n};\nuse crate::rule_cache::{RuleCache, RuleCacheConditions};\nuse crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StrongRuleNode};\nuse crate::selector_parser::PseudoElement;\nuse crate::shared_lock::StylesheetGuards;\nuse crate::style_adjuster::StyleAdjuster;\nuse crate::stylesheets::container_rule::ContainerSizeQuery;\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse crate::stylist::Stylist;\n#[cfg(feature = \"gecko\")]\nuse crate::values::specified::length::FontBaseSize;\nuse crate::values::specified::position::PositionTryFallbacksTryTactic;\nuse crate::values::{computed, specified};\nuse rustc_hash::FxHashMap;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::borrow::Cow;\n\n/// Whether we're resolving a style with the purposes of reparenting for ::first-line.\n#[derive(Copy, Clone)]\n#[allow(missing_docs)]\npub enum FirstLineReparenting<'a> {\n    No,\n    Yes {\n        /// The style we're re-parenting for ::first-line. ::first-line only affects inherited\n        /// properties so we use this to avoid some work and also ensure correctness by copying the\n        /// reset structs from this style.\n        style_to_reparent: &'a ComputedValues,\n    },\n}\n\n/// Performs the CSS cascade, computing new styles for an element from its parent style.\n///\n/// The arguments are:\n///\n///   * `device`: Used to get the initial viewport and other external state.\n///\n///   * `rule_node`: The rule node in the tree that represent the CSS rules that\n///   matched.\n///\n///   * `parent_style`: The parent style, if applicable; if `None`, this is the root node.\n///\n/// Returns the computed values.\n///   * `flags`: Various flags.\n///\npub fn cascade<E>(\n    stylist: &Stylist,\n    pseudo: Option<&PseudoElement>,\n    rule_node: &StrongRuleNode,\n    guards: &StylesheetGuards,\n    parent_style: Option<&ComputedValues>,\n    layout_parent_style: Option<&ComputedValues>,\n    first_line_reparenting: FirstLineReparenting,\n    try_tactic: &PositionTryFallbacksTryTactic,\n    visited_rules: Option<&StrongRuleNode>,\n    cascade_input_flags: ComputedValueFlags,\n    included_cascade_flags: RuleCascadeFlags,\n    rule_cache: Option<&RuleCache>,\n    rule_cache_conditions: &mut RuleCacheConditions,\n    element: Option<E>,\n) -> Arc<ComputedValues>\nwhere\n    E: TElement,\n{\n    cascade_rules(\n        stylist,\n        pseudo,\n        rule_node,\n        guards,\n        parent_style,\n        layout_parent_style,\n        first_line_reparenting,\n        try_tactic,\n        CascadeMode::Unvisited { visited_rules },\n        cascade_input_flags,\n        included_cascade_flags,\n        rule_cache,\n        rule_cache_conditions,\n        element,\n    )\n}\n\nstruct DeclarationIterator<'a> {\n    // Global to the iteration.\n    guards: &'a StylesheetGuards<'a>,\n    restriction: Option<PropertyFlags>,\n    // The rule we're iterating over.\n    current_rule_node: Option<&'a StrongRuleNode>,\n    // Per rule state.\n    declarations: DeclarationImportanceIterator<'a>,\n    priority: CascadePriority,\n}\n\nimpl<'a> DeclarationIterator<'a> {\n    #[inline]\n    fn new(\n        rule_node: &'a StrongRuleNode,\n        guards: &'a StylesheetGuards,\n        pseudo: Option<&PseudoElement>,\n    ) -> Self {\n        let restriction = pseudo.and_then(|p| p.property_restriction());\n        let mut iter = Self {\n            guards,\n            current_rule_node: Some(rule_node),\n            priority: CascadePriority::new(\n                CascadeLevel::new(CascadeOrigin::UA),\n                LayerOrder::root(),\n                RuleCascadeFlags::empty(),\n            ),\n            declarations: DeclarationImportanceIterator::default(),\n            restriction,\n        };\n        iter.update_for_node(rule_node);\n        iter\n    }\n\n    fn update_for_node(&mut self, node: &'a StrongRuleNode) {\n        self.priority = node.cascade_priority();\n        let guard = self.priority.cascade_level().origin().guard(&self.guards);\n        self.declarations = match node.style_source() {\n            Some(source) => source.read(guard).declaration_importance_iter(),\n            None => DeclarationImportanceIterator::default(),\n        };\n    }\n}\n\nimpl<'a> Iterator for DeclarationIterator<'a> {\n    type Item = (&'a PropertyDeclaration, CascadePriority);\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            if let Some((decl, importance)) = self.declarations.next_back() {\n                if self.priority.cascade_level().is_important() != importance.important() {\n                    continue;\n                }\n\n                if let Some(restriction) = self.restriction {\n                    // decl.id() is either a longhand or a custom\n                    // property.  Custom properties are always allowed, but\n                    // longhands are only allowed if they have our\n                    // restriction flag set.\n                    if let PropertyDeclarationId::Longhand(id) = decl.id() {\n                        if !id.flags().contains(restriction)\n                            && self.priority.cascade_level().origin() != CascadeOrigin::UA\n                        {\n                            continue;\n                        }\n                    }\n                }\n\n                return Some((decl, self.priority));\n            }\n\n            let next_node = self.current_rule_node.take()?.parent()?;\n            self.current_rule_node = Some(next_node);\n            self.update_for_node(next_node);\n        }\n    }\n}\n\nfn cascade_rules<E>(\n    stylist: &Stylist,\n    pseudo: Option<&PseudoElement>,\n    rule_node: &StrongRuleNode,\n    guards: &StylesheetGuards,\n    parent_style: Option<&ComputedValues>,\n    layout_parent_style: Option<&ComputedValues>,\n    first_line_reparenting: FirstLineReparenting,\n    try_tactic: &PositionTryFallbacksTryTactic,\n    cascade_mode: CascadeMode,\n    cascade_input_flags: ComputedValueFlags,\n    included_cascade_flags: RuleCascadeFlags,\n    rule_cache: Option<&RuleCache>,\n    rule_cache_conditions: &mut RuleCacheConditions,\n    element: Option<E>,\n) -> Arc<ComputedValues>\nwhere\n    E: TElement,\n{\n    apply_declarations(\n        stylist,\n        pseudo,\n        rule_node,\n        guards,\n        DeclarationIterator::new(rule_node, guards, pseudo),\n        parent_style,\n        layout_parent_style,\n        first_line_reparenting,\n        try_tactic,\n        cascade_mode,\n        cascade_input_flags,\n        included_cascade_flags,\n        rule_cache,\n        rule_cache_conditions,\n        element,\n    )\n}\n\n/// Whether we're cascading for visited or unvisited styles.\n#[derive(Clone, Copy)]\npub enum CascadeMode<'a, 'b> {\n    /// We're cascading for unvisited styles.\n    Unvisited {\n        /// The visited rules that should match the visited style.\n        visited_rules: Option<&'a StrongRuleNode>,\n    },\n    /// We're cascading for visited styles.\n    Visited {\n        /// The cascade for our unvisited style.\n        unvisited_context: &'a computed::Context<'b>,\n    },\n}\n\nfn iter_declarations<'builder, 'decls: 'builder>(\n    iter: impl Iterator<Item = (&'decls PropertyDeclaration, CascadePriority)>,\n    declarations: &mut Declarations<'decls>,\n    mut custom_builder: Option<&mut CustomPropertiesBuilder<'builder, 'decls>>,\n    attribute_tracker: &mut AttributeTracker,\n) {\n    for (declaration, priority) in iter {\n        if let PropertyDeclaration::Custom(ref declaration) = *declaration {\n            if let Some(ref mut builder) = custom_builder {\n                builder.cascade(declaration, priority, attribute_tracker);\n            }\n        } else {\n            let id = declaration.id().as_longhand().unwrap();\n            declarations.note_declaration(declaration, priority, id);\n            if CustomPropertiesBuilder::might_have_non_custom_or_attr_dependency(id, declaration) {\n                if let Some(ref mut builder) = custom_builder {\n                    builder.maybe_note_non_custom_dependency(id, declaration, attribute_tracker);\n                }\n            }\n        }\n    }\n}\n\n/// NOTE: This function expects the declaration with more priority to appear\n/// first.\npub fn apply_declarations<'a, E, I>(\n    stylist: &'a Stylist,\n    pseudo: Option<&'a PseudoElement>,\n    rules: &StrongRuleNode,\n    guards: &StylesheetGuards,\n    iter: I,\n    parent_style: Option<&'a ComputedValues>,\n    layout_parent_style: Option<&ComputedValues>,\n    first_line_reparenting: FirstLineReparenting<'a>,\n    try_tactic: &'a PositionTryFallbacksTryTactic,\n    cascade_mode: CascadeMode,\n    cascade_input_flags: ComputedValueFlags,\n    included_cascade_flags: RuleCascadeFlags,\n    rule_cache: Option<&'a RuleCache>,\n    rule_cache_conditions: &'a mut RuleCacheConditions,\n    element: Option<E>,\n) -> Arc<ComputedValues>\nwhere\n    E: TElement + 'a,\n    I: Iterator<Item = (&'a PropertyDeclaration, CascadePriority)>,\n{\n    debug_assert!(layout_parent_style.is_none() || parent_style.is_some());\n    let device = stylist.device();\n    let inherited_style = parent_style.unwrap_or(device.default_computed_values());\n    let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());\n\n    let container_size_query =\n        ContainerSizeQuery::for_option_element(element, Some(inherited_style), pseudo.is_some());\n\n    let mut context = computed::Context::new(\n        // We'd really like to own the rules here to avoid refcount traffic, but\n        // animation's usage of `apply_declarations` make this tricky. See bug\n        // 1375525.\n        StyleBuilder::new(\n            device,\n            Some(stylist),\n            parent_style,\n            pseudo,\n            Some(rules.clone()),\n            is_root_element,\n        ),\n        stylist.quirks_mode(),\n        rule_cache_conditions,\n        container_size_query,\n        included_cascade_flags,\n    );\n\n    context.style().add_flags(cascade_input_flags);\n\n    let using_cached_reset_properties;\n    let ignore_colors = context.builder.device.forced_colors().is_active();\n    let mut cascade = Cascade::new(first_line_reparenting, try_tactic, ignore_colors);\n    let mut declarations = Default::default();\n    let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();\n    let mut attribute_tracker = match element {\n        Some(ref attr_provider) => AttributeTracker::new(attr_provider),\n        None => AttributeTracker::new_dummy(),\n    };\n\n    let properties_to_apply = match cascade_mode {\n        CascadeMode::Visited { unvisited_context } => {\n            context.builder.substitution_functions =\n                unvisited_context.builder.substitution_functions.clone();\n            context.builder.writing_mode = unvisited_context.builder.writing_mode;\n            context.builder.color_scheme = unvisited_context.builder.color_scheme;\n            // We never insert visited styles into the cache so we don't need to try looking it up.\n            // It also wouldn't be super-profitable, only a handful :visited properties are\n            // non-inherited.\n            using_cached_reset_properties = false;\n            // TODO(bug 1859385): If we match the same rules when visited and unvisited, we could\n            // try to avoid gathering the declarations. That'd be:\n            //      unvisited_context.builder.rules.as_ref() == Some(rules)\n            iter_declarations(iter, &mut declarations, None, &mut attribute_tracker);\n\n            LonghandIdSet::visited_dependent()\n        },\n        CascadeMode::Unvisited { visited_rules } => {\n            let deferred_custom_properties = {\n                let mut builder = CustomPropertiesBuilder::new(stylist, &mut context);\n                iter_declarations(\n                    iter,\n                    &mut declarations,\n                    Some(&mut builder),\n                    &mut attribute_tracker,\n                );\n                // Detect cycles, remove properties participating in them, and resolve properties, except:\n                // * Registered custom properties that depend on font-relative properties (Resolved)\n                //   when prioritary properties are resolved), and\n                // * Any property that, in turn, depend on properties like above.\n                builder.build(\n                    DeferFontRelativeCustomPropertyResolution::Yes,\n                    &mut attribute_tracker,\n                )\n            };\n\n            // Resolve prioritary properties - Guaranteed to not fall into a cycle with existing custom\n            // properties.\n            cascade.apply_prioritary_properties(\n                &mut context,\n                &declarations,\n                &mut shorthand_cache,\n                &mut attribute_tracker,\n            );\n\n            // Resolve the deferred custom properties.\n            if let Some(deferred) = deferred_custom_properties {\n                CustomPropertiesBuilder::build_deferred(\n                    deferred,\n                    stylist,\n                    &mut context,\n                    &mut attribute_tracker,\n                );\n            }\n\n            if let Some(visited_rules) = visited_rules {\n                cascade.compute_visited_style_if_needed(\n                    &mut context,\n                    element,\n                    parent_style,\n                    layout_parent_style,\n                    visited_rules,\n                    guards,\n                );\n            }\n\n            using_cached_reset_properties =\n                cascade.try_to_use_cached_reset_properties(&mut context, rule_cache, guards);\n\n            if using_cached_reset_properties {\n                LonghandIdSet::late_group_only_inherited()\n            } else {\n                LonghandIdSet::late_group()\n            }\n        },\n    };\n\n    cascade.apply_non_prioritary_properties(\n        &mut context,\n        &declarations.longhand_declarations,\n        &mut shorthand_cache,\n        &properties_to_apply,\n        &mut attribute_tracker,\n    );\n\n    context.builder.attribute_references = attribute_tracker.finalize();\n\n    cascade.finished_applying_properties(&mut context.builder);\n\n    context.builder.clear_modified_reset();\n\n    if matches!(cascade_mode, CascadeMode::Unvisited { .. }) {\n        StyleAdjuster::new(&mut context.builder).adjust(\n            layout_parent_style.unwrap_or(inherited_style),\n            element,\n            try_tactic,\n            &cascade.author_specified,\n        );\n    }\n\n    if context.builder.modified_reset() || using_cached_reset_properties {\n        // If we adjusted any reset structs, we can't cache this ComputedValues.\n        //\n        // Also, if we re-used existing reset structs, don't bother caching it back again. (Aside\n        // from being wasted effort, it will be wrong, since context.rule_cache_conditions won't be\n        // set appropriately if we didn't compute those reset properties.)\n        context.rule_cache_conditions.borrow_mut().set_uncacheable();\n    }\n\n    context.builder.build()\n}\n\n/// For ignored colors mode, we sometimes want to do something equivalent to\n/// \"revert-or-initial\", where we `revert` for a given origin, but then apply a\n/// given initial value if nothing in other origins did override it.\n///\n/// This is a bit of a clunky way of achieving this.\ntype DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;\n\n#[cfg(feature = \"gecko\")]\nfn is_base_appearance(context: &computed::Context) -> bool {\n    use computed::Appearance;\n    let box_style = context.builder.get_box();\n    match box_style.clone_appearance() {\n        Appearance::BaseSelect => {\n            matches!(\n                box_style.clone__moz_default_appearance(),\n                Appearance::Listbox | Appearance::Menulist\n            )\n        },\n        Appearance::Base => box_style.clone__moz_default_appearance() != Appearance::None,\n        _ => false,\n    }\n}\n\nfn tweak_when_ignoring_colors(\n    context: &computed::Context,\n    longhand_id: LonghandId,\n    origin: CascadeOrigin,\n    declaration: &mut Cow<PropertyDeclaration>,\n    declarations_to_apply_unless_overridden: &mut DeclarationsToApplyUnlessOverriden,\n) {\n    use crate::values::computed::ToComputedValue;\n    use crate::values::specified::Color;\n\n    if !longhand_id.ignored_when_document_colors_disabled() {\n        return;\n    }\n\n    let is_ua_or_user_rule = matches!(origin, CascadeOrigin::User | CascadeOrigin::UA);\n    if is_ua_or_user_rule {\n        return;\n    }\n\n    // Always honor colors if forced-color-adjust is set to none.\n    #[cfg(feature = \"gecko\")]\n    {\n        let forced = context\n            .builder\n            .get_inherited_text()\n            .clone_forced_color_adjust();\n        if forced == computed::ForcedColorAdjust::None {\n            return;\n        }\n    }\n\n    fn alpha_channel(color: &Color, context: &computed::Context) -> f32 {\n        // We assume here currentColor is opaque.\n        color\n            .to_computed_value(context)\n            .resolve_to_absolute(&AbsoluteColor::BLACK)\n            .alpha\n    }\n\n    // A few special-cases ahead.\n    match **declaration {\n        // Honor CSS-wide keywords like unset / revert / initial...\n        PropertyDeclaration::CSSWideKeyword(..) => return,\n        PropertyDeclaration::BackgroundColor(ref color) => {\n            // We honor system colors and transparent colors unconditionally.\n            //\n            // NOTE(emilio): We honor transparent unconditionally, like we do\n            // for color, even though it causes issues like bug 1625036. The\n            // reasoning is that the conditions that trigger that (having\n            // mismatched widget and default backgrounds) are both uncommon, and\n            // broken in other applications as well, and not honoring\n            // transparent makes stuff uglier or break unconditionally\n            // (bug 1666059, bug 1755713).\n            if color.honored_in_forced_colors_mode(/* allow_transparent = */ true) {\n                return;\n            }\n            // For background-color, we revert or initial-with-preserved-alpha\n            // otherwise, this is needed to preserve semi-transparent\n            // backgrounds.\n            let alpha = alpha_channel(color, context);\n            if alpha == 0.0 {\n                return;\n            }\n            let mut color = context.builder.device.default_background_color();\n            color.alpha = alpha;\n            declarations_to_apply_unless_overridden\n                .push(PropertyDeclaration::BackgroundColor(color.into()))\n        },\n        PropertyDeclaration::Color(ref color) => {\n            // We honor color: transparent and system colors.\n            if color\n                .0\n                .honored_in_forced_colors_mode(/* allow_transparent = */ true)\n            {\n                return;\n            }\n            // If the inherited color would be transparent, but we would\n            // override this with a non-transparent color, then override it with\n            // the default color. Otherwise just let it inherit through.\n            if context\n                .builder\n                .get_parent_inherited_text()\n                .clone_color()\n                .alpha\n                == 0.0\n            {\n                let color = context.builder.device.default_color();\n                declarations_to_apply_unless_overridden.push(PropertyDeclaration::Color(\n                    specified::ColorPropertyValue(color.into()),\n                ))\n            }\n        },\n        // We honor url background-images if backplating.\n        #[cfg(feature = \"gecko\")]\n        PropertyDeclaration::BackgroundImage(ref bkg) => {\n            use crate::values::generics::image::Image;\n            if static_prefs::pref!(\"browser.display.permit_backplate\") {\n                if bkg\n                    .0\n                    .iter()\n                    .all(|image| matches!(*image, Image::Url(..) | Image::None))\n                {\n                    return;\n                }\n            }\n        },\n        _ => {\n            // We honor system colors more generally for all colors.\n            //\n            // We used to honor transparent but that causes accessibility\n            // regressions like bug 1740924.\n            //\n            // NOTE(emilio): This doesn't handle caret-color and accent-color\n            // because those use a slightly different syntax (<color> | auto for\n            // example).\n            //\n            // That's probably fine though, as using a system color for\n            // caret-color doesn't make sense (using currentColor is fine), and\n            // we ignore accent-color in high-contrast-mode anyways.\n            if let Some(color) = declaration.color_value() {\n                if color.honored_in_forced_colors_mode(/* allow_transparent = */ false) {\n                    return;\n                }\n            }\n        },\n    }\n\n    *declaration.to_mut() =\n        PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);\n}\n\n/// We track the index only for prioritary properties. For other properties we can just iterate.\ntype DeclarationIndex = u16;\n\n/// \"Prioritary\" properties are properties that other properties depend on in one way or another.\n///\n/// We keep track of their position in the declaration vector, in order to be able to cascade them\n/// separately in precise order.\n#[derive(Copy, Clone)]\nstruct PrioritaryDeclarationPosition {\n    // DeclarationIndex::MAX signals no index.\n    most_important: DeclarationIndex,\n    least_important: DeclarationIndex,\n}\n\nimpl Default for PrioritaryDeclarationPosition {\n    fn default() -> Self {\n        Self {\n            most_important: DeclarationIndex::MAX,\n            least_important: DeclarationIndex::MAX,\n        }\n    }\n}\n\n#[derive(Copy, Clone)]\nstruct Declaration<'a> {\n    decl: &'a PropertyDeclaration,\n    priority: CascadePriority,\n    next_index: DeclarationIndex,\n}\n\n/// The set of property declarations from our rules.\n#[derive(Default)]\nstruct Declarations<'a> {\n    /// Whether we have any prioritary property. This is just a minor optimization.\n    has_prioritary_properties: bool,\n    /// A list of all the applicable longhand declarations.\n    longhand_declarations: SmallVec<[Declaration<'a>; 64]>,\n    /// The prioritary property position data.\n    prioritary_positions: [PrioritaryDeclarationPosition; property_counts::PRIORITARY],\n}\n\nimpl<'a> Declarations<'a> {\n    fn note_prioritary_property(&mut self, id: PrioritaryPropertyId) {\n        let new_index = self.longhand_declarations.len();\n        if new_index >= DeclarationIndex::MAX as usize {\n            // This prioritary property is past the amount of declarations we can track. Let's give\n            // up applying it to prevent getting confused.\n            return;\n        }\n\n        self.has_prioritary_properties = true;\n        let new_index = new_index as DeclarationIndex;\n        let position = &mut self.prioritary_positions[id as usize];\n        if position.most_important == DeclarationIndex::MAX {\n            // We still haven't seen this property, record the current position as the most\n            // prioritary index.\n            position.most_important = new_index;\n        } else {\n            // Let the previous item in the list know about us.\n            self.longhand_declarations[position.least_important as usize].next_index = new_index;\n        }\n        position.least_important = new_index;\n    }\n\n    fn note_declaration(\n        &mut self,\n        decl: &'a PropertyDeclaration,\n        priority: CascadePriority,\n        id: LonghandId,\n    ) {\n        if let Some(id) = PrioritaryPropertyId::from_longhand(id) {\n            self.note_prioritary_property(id);\n        }\n        self.longhand_declarations.push(Declaration {\n            decl,\n            priority,\n            next_index: 0,\n        });\n    }\n}\n\nstruct Cascade<'b> {\n    first_line_reparenting: FirstLineReparenting<'b>,\n    try_tactic: &'b PositionTryFallbacksTryTactic,\n    ignore_colors: bool,\n    seen: LonghandIdSet,\n    author_specified: LonghandIdSet,\n    reverted_set: LonghandIdSet,\n    reverted: FxHashMap<LonghandId, (CascadePriority, RevertKind)>,\n    declarations_to_apply_unless_overridden: DeclarationsToApplyUnlessOverriden,\n}\n\nimpl<'b> Cascade<'b> {\n    fn new(\n        first_line_reparenting: FirstLineReparenting<'b>,\n        try_tactic: &'b PositionTryFallbacksTryTactic,\n        ignore_colors: bool,\n    ) -> Self {\n        Self {\n            first_line_reparenting,\n            try_tactic,\n            ignore_colors,\n            seen: LonghandIdSet::default(),\n            author_specified: LonghandIdSet::default(),\n            reverted_set: Default::default(),\n            reverted: Default::default(),\n            declarations_to_apply_unless_overridden: Default::default(),\n        }\n    }\n\n    fn substitute_variables_if_needed<'cache, 'decl>(\n        &self,\n        context: &mut computed::Context,\n        shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,\n        declaration: &'decl PropertyDeclaration,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> Cow<'decl, PropertyDeclaration>\n    where\n        'cache: 'decl,\n    {\n        let declaration = match *declaration {\n            PropertyDeclaration::WithVariables(ref declaration) => declaration,\n            ref d => return Cow::Borrowed(d),\n        };\n\n        if !declaration.id.inherited() {\n            context.rule_cache_conditions.borrow_mut().set_uncacheable();\n\n            // NOTE(emilio): We only really need to add the `display` /\n            // `content` flag if the CSS variable has not been specified on our\n            // declarations, but we don't have that information at this point,\n            // and it doesn't seem like an important enough optimization to\n            // warrant it.\n            if matches!(declaration.id, LonghandId::Display | LonghandId::Content) {\n                context\n                    .builder\n                    .add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);\n            }\n        }\n\n        debug_assert!(\n            context.builder.stylist.is_some(),\n            \"Need a Stylist to substitute variables!\"\n        );\n        declaration.value.substitute_variables(\n            declaration.id,\n            &context.builder.substitution_functions(),\n            context.builder.stylist.unwrap(),\n            context,\n            shorthand_cache,\n            attribute_tracker,\n        )\n    }\n\n    fn apply_one_prioritary_property(\n        &mut self,\n        context: &mut computed::Context,\n        decls: &Declarations,\n        cache: &mut ShorthandsWithPropertyReferencesCache,\n        id: PrioritaryPropertyId,\n        attr_provider: &mut AttributeTracker,\n    ) -> bool {\n        let mut index = decls.prioritary_positions[id as usize].most_important;\n        if index == DeclarationIndex::MAX {\n            return false;\n        }\n\n        let longhand_id = id.to_longhand();\n        debug_assert!(\n            !longhand_id.is_logical(),\n            \"That could require more book-keeping\"\n        );\n        loop {\n            let decl = decls.longhand_declarations[index as usize];\n            self.apply_one_longhand(\n                context,\n                longhand_id,\n                decl.decl,\n                decl.priority,\n                cache,\n                attr_provider,\n            );\n            if self.seen.contains(longhand_id) {\n                return true; // Common case, we're done.\n            }\n            debug_assert!(\n                decl.next_index == 0 || decl.next_index > index,\n                \"should make progress! {} -> {}\",\n                index,\n                decl.next_index,\n            );\n            index = decl.next_index;\n            if index == 0 {\n                break;\n            }\n        }\n        false\n    }\n\n    fn apply_prioritary_properties(\n        &mut self,\n        context: &mut computed::Context,\n        decls: &Declarations,\n        cache: &mut ShorthandsWithPropertyReferencesCache,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        // Keeps apply_one_prioritary_property calls readable, considering the repititious\n        // arguments.\n        macro_rules! apply {\n            ($prop:ident) => {\n                self.apply_one_prioritary_property(\n                    context,\n                    decls,\n                    cache,\n                    PrioritaryPropertyId::$prop,\n                    attribute_tracker,\n                )\n            };\n        }\n\n        if !decls.has_prioritary_properties {\n            return;\n        }\n\n        #[cfg(feature = \"gecko\")]\n        apply!(MozDefaultAppearance);\n        #[cfg(feature = \"gecko\")]\n        if apply!(Appearance) && is_base_appearance(&context) {\n            context\n                .style()\n                .add_flags(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE);\n            context\n                .included_cascade_flags\n                .insert(RuleCascadeFlags::APPEARANCE_BASE);\n        }\n\n        let has_writing_mode = apply!(WritingMode) | apply!(Direction);\n        #[cfg(feature = \"gecko\")]\n        let has_writing_mode = has_writing_mode | apply!(TextOrientation);\n\n        if has_writing_mode {\n            context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box())\n        }\n\n        if apply!(Zoom) {\n            context.builder.recompute_effective_zooms();\n            if !context.builder.effective_zoom_for_inheritance.is_one() {\n                // NOTE(emilio): This is a bit of a hack, but matches the shipped WebKit and Blink\n                // behavior for now. Ideally, in the future, we have a pass over all\n                // implicitly-or-explicitly-inherited properties that can contain lengths and\n                // re-compute them properly, see https://github.com/w3c/csswg-drafts/issues/9397.\n                // TODO(emilio): we need to eagerly do this for line-height as well, probably.\n                self.recompute_font_size_for_zoom_change(&mut context.builder);\n            }\n        }\n\n        // Compute font-family.\n        let has_font_family = apply!(FontFamily);\n        let has_lang = apply!(XLang);\n        #[cfg(feature = \"gecko\")]\n        {\n            if has_lang {\n                self.recompute_initial_font_family_if_needed(&mut context.builder);\n            }\n            if has_font_family {\n                self.prioritize_user_fonts_if_needed(&mut context.builder);\n            }\n\n            // Compute font-size.\n            if apply!(XTextScale) {\n                self.unzoom_fonts_if_needed(&mut context.builder);\n            }\n            let has_font_size = apply!(FontSize);\n            let has_math_depth = apply!(MathDepth);\n            let has_min_font_size_ratio = apply!(MozMinFontSizeRatio);\n\n            if has_math_depth && has_font_size {\n                self.recompute_math_font_size_if_needed(context);\n            }\n            if has_lang || has_font_family {\n                self.recompute_keyword_font_size_if_needed(context);\n            }\n            if has_font_size || has_min_font_size_ratio || has_lang || has_font_family {\n                self.constrain_font_size_if_needed(&mut context.builder);\n            }\n        }\n\n        #[cfg(feature = \"servo\")]\n        {\n            apply!(FontSize);\n            if has_lang || has_font_family {\n                self.recompute_keyword_font_size_if_needed(context);\n            }\n        }\n\n        // Compute the rest of the first-available-font-affecting properties.\n        apply!(FontWeight);\n        apply!(FontStretch);\n        apply!(FontStyle);\n        #[cfg(feature = \"gecko\")]\n        apply!(FontSizeAdjust);\n\n        #[cfg(feature = \"gecko\")]\n        apply!(ForcedColorAdjust);\n        // color-scheme needs to be after forced-color-adjust, since it's one of the \"skipped in\n        // forced-colors-mode\" properties.\n        if apply!(ColorScheme) {\n            context.builder.color_scheme = context.builder.get_inherited_ui().color_scheme_bits();\n        }\n        apply!(LineHeight);\n    }\n\n    fn apply_non_prioritary_properties(\n        &mut self,\n        context: &mut computed::Context,\n        longhand_declarations: &[Declaration],\n        shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,\n        properties_to_apply: &LonghandIdSet,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        debug_assert!(!properties_to_apply.contains_any(LonghandIdSet::prioritary_properties()));\n        debug_assert!(self.declarations_to_apply_unless_overridden.is_empty());\n        for declaration in &*longhand_declarations {\n            let mut longhand_id = declaration.decl.id().as_longhand().unwrap();\n            if !properties_to_apply.contains(longhand_id) {\n                continue;\n            }\n            debug_assert!(PrioritaryPropertyId::from_longhand(longhand_id).is_none());\n            let is_logical = longhand_id.is_logical();\n            if is_logical {\n                let wm = context.builder.writing_mode;\n                context\n                    .rule_cache_conditions\n                    .borrow_mut()\n                    .set_writing_mode_dependency(wm);\n                longhand_id = longhand_id.to_physical(wm);\n            }\n            self.apply_one_longhand(\n                context,\n                longhand_id,\n                declaration.decl,\n                declaration.priority,\n                shorthand_cache,\n                attribute_tracker,\n            );\n        }\n        if !self.declarations_to_apply_unless_overridden.is_empty() {\n            debug_assert!(self.ignore_colors);\n            for declaration in std::mem::take(&mut self.declarations_to_apply_unless_overridden) {\n                let longhand_id = declaration.id().as_longhand().unwrap();\n                debug_assert!(!longhand_id.is_logical());\n                if !self.seen.contains(longhand_id) {\n                    unsafe {\n                        self.do_apply_declaration(context, longhand_id, &declaration);\n                    }\n                }\n            }\n        }\n\n        if !context.builder.effective_zoom_for_inheritance.is_one() {\n            self.recompute_zoom_dependent_inherited_lengths(context);\n        }\n    }\n\n    #[cold]\n    fn recompute_zoom_dependent_inherited_lengths(&self, context: &mut computed::Context) {\n        debug_assert!(self.seen.contains(LonghandId::Zoom));\n        for prop in LonghandIdSet::zoom_dependent_inherited_properties().iter() {\n            if self.seen.contains(prop) {\n                continue;\n            }\n            let declaration = PropertyDeclaration::css_wide_keyword(prop, CSSWideKeyword::Inherit);\n            unsafe {\n                self.do_apply_declaration(context, prop, &declaration);\n            }\n        }\n    }\n\n    fn apply_one_longhand(\n        &mut self,\n        context: &mut computed::Context,\n        longhand_id: LonghandId,\n        declaration: &PropertyDeclaration,\n        priority: CascadePriority,\n        cache: &mut ShorthandsWithPropertyReferencesCache,\n        attribute_tracker: &mut AttributeTracker,\n    ) {\n        debug_assert!(!longhand_id.is_logical());\n        if self.seen.contains(longhand_id) {\n            return;\n        }\n\n        if !(priority.flags() - context.included_cascade_flags).is_empty() {\n            return;\n        }\n\n        if self.reverted_set.contains(longhand_id) {\n            if let Some(&(reverted_priority, revert_kind)) = self.reverted.get(&longhand_id) {\n                if !reverted_priority.allows_when_reverted(&priority, revert_kind) {\n                    return;\n                }\n            }\n        }\n\n        let mut declaration =\n            self.substitute_variables_if_needed(context, cache, declaration, attribute_tracker);\n\n        // When document colors are disabled, do special handling of\n        // properties that are marked as ignored in that mode.\n        let origin = priority.cascade_level().origin();\n        if self.ignore_colors {\n            tweak_when_ignoring_colors(\n                context,\n                longhand_id,\n                origin,\n                &mut declaration,\n                &mut self.declarations_to_apply_unless_overridden,\n            );\n        }\n        let can_skip_apply = match declaration.get_css_wide_keyword() {\n            Some(keyword) => {\n                if let Some(revert_kind) = keyword.revert_kind() {\n                    // We intentionally don't want to insert it into `self.seen`, `reverted` takes\n                    // care of rejecting other declarations as needed.\n                    self.reverted_set.insert(longhand_id);\n                    self.reverted.insert(longhand_id, (priority, revert_kind));\n                    return;\n                }\n\n                let inherited = longhand_id.inherited();\n                let zoomed = !context.builder.effective_zoom_for_inheritance.is_one()\n                    && longhand_id.zoom_dependent();\n                match keyword {\n                    CSSWideKeyword::Revert\n                    | CSSWideKeyword::RevertLayer\n                    | CSSWideKeyword::RevertRule => unreachable!(),\n                    CSSWideKeyword::Unset => !zoomed || !inherited,\n                    CSSWideKeyword::Inherit => inherited && !zoomed,\n                    CSSWideKeyword::Initial => !inherited,\n                }\n            },\n            None => false,\n        };\n\n        self.seen.insert(longhand_id);\n        if origin.is_author_origin() {\n            self.author_specified.insert(longhand_id);\n        }\n\n        if !can_skip_apply {\n            // Set context.scope to this declaration's cascade level so that\n            // tree-scoped properties (anchor-name, position-anchor, anchor-scope)\n            // get the correct scope when converted to computed values.\n            let old_scope = context.scope;\n            let cascade_level = priority.cascade_level();\n            context.scope = cascade_level;\n            unsafe { self.do_apply_declaration(context, longhand_id, &declaration) }\n            context.scope = old_scope;\n        }\n    }\n\n    #[inline]\n    unsafe fn do_apply_declaration(\n        &self,\n        context: &mut computed::Context,\n        longhand_id: LonghandId,\n        declaration: &PropertyDeclaration,\n    ) {\n        debug_assert!(!longhand_id.is_logical());\n        // We could (and used to) use a pattern match here, but that bloats this\n        // function to over 100K of compiled code!\n        //\n        // To improve i-cache behavior, we outline the individual functions and\n        // use virtual dispatch instead.\n        (CASCADE_PROPERTY[longhand_id as usize])(&declaration, context);\n    }\n\n    fn compute_visited_style_if_needed<E>(\n        &self,\n        context: &mut computed::Context,\n        element: Option<E>,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n        visited_rules: &StrongRuleNode,\n        guards: &StylesheetGuards,\n    ) where\n        E: TElement,\n    {\n        let is_link = context.builder.pseudo.is_none() && element.unwrap().is_link();\n\n        macro_rules! visited_parent {\n            ($parent:expr) => {\n                if is_link {\n                    $parent\n                } else {\n                    $parent.map(|p| p.visited_style().unwrap_or(p))\n                }\n            };\n        }\n\n        // We could call apply_declarations directly, but that'd cause\n        // another instantiation of this function which is not great.\n        let style = cascade_rules(\n            context.builder.stylist.unwrap(),\n            context.builder.pseudo,\n            visited_rules,\n            guards,\n            visited_parent!(parent_style),\n            visited_parent!(layout_parent_style),\n            self.first_line_reparenting,\n            self.try_tactic,\n            CascadeMode::Visited {\n                unvisited_context: &*context,\n            },\n            // Cascade input flags don't matter for the visited style, they are\n            // in the main (unvisited) style.\n            Default::default(),\n            context.included_cascade_flags,\n            // The rule cache doesn't care about caching :visited\n            // styles, we cache the unvisited style instead. We still do\n            // need to set the caching dependencies properly if present\n            // though, so the cache conditions need to match.\n            None, // rule_cache\n            &mut *context.rule_cache_conditions.borrow_mut(),\n            element,\n        );\n        context.builder.visited_style = Some(style);\n    }\n\n    fn finished_applying_properties(&self, builder: &mut StyleBuilder) {\n        #[cfg(feature = \"gecko\")]\n        {\n            if let Some(bg) = builder.get_background_if_mutated() {\n                bg.fill_arrays();\n            }\n\n            if let Some(svg) = builder.get_svg_if_mutated() {\n                svg.fill_arrays();\n            }\n        }\n\n        if self\n            .author_specified\n            .contains_any(LonghandIdSet::border_background_properties())\n        {\n            builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);\n        }\n\n        if self.author_specified.contains(LonghandId::Color) {\n            builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_COLOR);\n        }\n\n        if self.author_specified.contains(LonghandId::TextShadow) {\n            builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_TEXT_SHADOW);\n        }\n\n        if self.author_specified.contains(LonghandId::GridAutoFlow) {\n            builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW);\n        }\n        #[cfg(feature = \"servo\")]\n        {\n            if let Some(font) = builder.get_font_if_mutated() {\n                font.compute_font_hash();\n            }\n        }\n    }\n\n    fn try_to_use_cached_reset_properties(\n        &self,\n        context: &mut computed::Context<'b>,\n        cache: Option<&'b RuleCache>,\n        guards: &StylesheetGuards,\n    ) -> bool {\n        let style = match self.first_line_reparenting {\n            FirstLineReparenting::Yes { style_to_reparent } => style_to_reparent,\n            FirstLineReparenting::No => {\n                let Some(cache) = cache else { return false };\n                let Some(style) = cache.find(guards, &context) else {\n                    return false;\n                };\n                style\n            },\n        };\n\n        context.builder.copy_reset_from(style);\n\n        // We're using the same reset style as another element, and we'll skip\n        // applying the relevant properties. So we need to do the relevant\n        // bookkeeping here to keep these bits correct.\n        //\n        // Note that the border/background properties are non-inherited, so we\n        // don't need to do anything else other than just copying the bits over.\n        //\n        // When using this optimization, we also need to copy whether the old\n        // style specified viewport units / used font-relative lengths, this one\n        // would as well.  It matches the same rules, so it is the right thing\n        // to do anyways, even if it's only used on inherited properties.\n        let bits_to_copy = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND\n            | ComputedValueFlags::HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW\n            | ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS\n            | ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS\n            | ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE\n            | ComputedValueFlags::USES_CONTAINER_UNITS\n            | ComputedValueFlags::USES_VIEWPORT_UNITS\n            | ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY;\n        context.builder.add_flags(style.flags & bits_to_copy);\n\n        true\n    }\n\n    /// The initial font depends on the current lang group so we may need to\n    /// recompute it if the language changed.\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    fn recompute_initial_font_family_if_needed(&self, builder: &mut StyleBuilder) {\n        use crate::gecko_bindings::bindings;\n        use crate::values::computed::font::FontFamily;\n\n        let default_font_type = {\n            let font = builder.get_font();\n\n            if !font.mFont.family.is_initial {\n                return;\n            }\n\n            let default_font_type = unsafe {\n                bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(\n                    builder.device.document(),\n                    font.mLanguage.mRawPtr,\n                )\n            };\n\n            let initial_generic = font.mFont.family.families.single_generic();\n            debug_assert!(\n                initial_generic.is_some(),\n                \"Initial font should be just one generic font\"\n            );\n            if initial_generic == Some(default_font_type) {\n                return;\n            }\n\n            default_font_type\n        };\n\n        // NOTE: Leaves is_initial untouched.\n        builder.mutate_font().mFont.family.families =\n            FontFamily::generic(default_font_type).families.clone();\n    }\n\n    /// Prioritize user fonts if needed by pref.\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    fn prioritize_user_fonts_if_needed(&self, builder: &mut StyleBuilder) {\n        use crate::gecko_bindings::bindings;\n\n        // Check the use_document_fonts setting for content, but for chrome\n        // documents they're treated as always enabled.\n        if static_prefs::pref!(\"browser.display.use_document_fonts\") != 0\n            || builder.device.chrome_rules_enabled_for_document()\n        {\n            return;\n        }\n\n        let default_font_type = {\n            let font = builder.get_font();\n\n            if font.mFont.family.is_system_font {\n                return;\n            }\n\n            if !font.mFont.family.families.needs_user_font_prioritization() {\n                return;\n            }\n\n            unsafe {\n                bindings::Gecko_nsStyleFont_ComputeFallbackFontTypeForLanguage(\n                    builder.device.document(),\n                    font.mLanguage.mRawPtr,\n                )\n            }\n        };\n\n        let font = builder.mutate_font();\n        font.mFont\n            .family\n            .families\n            .prioritize_first_generic_or_prepend(default_font_type);\n    }\n\n    /// Some keyword sizes depend on the font family and language.\n    fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) {\n        use crate::values::computed::ToComputedValue;\n\n        if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {\n            return;\n        }\n\n        let new_size = {\n            let font = context.builder.get_font();\n            let info = font.clone_font_size().keyword_info;\n            let new_size = match info.kw {\n                specified::FontSizeKeyword::None => return,\n                _ => {\n                    context.for_non_inherited_property = false;\n                    specified::FontSize::Keyword(info).to_computed_value(context)\n                },\n            };\n\n            #[cfg(feature = \"gecko\")]\n            if font.mScriptUnconstrainedSize == new_size.computed_size {\n                return;\n            }\n\n            new_size\n        };\n\n        context.builder.mutate_font().set_font_size(new_size);\n    }\n\n    /// Some properties, plus setting font-size itself, may make us go out of\n    /// our minimum font-size range.\n    #[cfg(feature = \"gecko\")]\n    fn constrain_font_size_if_needed(&self, builder: &mut StyleBuilder) {\n        use crate::gecko_bindings::bindings;\n        use crate::values::generics::NonNegative;\n\n        let min_font_size = {\n            let font = builder.get_font();\n            let min_font_size = unsafe {\n                bindings::Gecko_nsStyleFont_ComputeMinSize(&**font, builder.device.document())\n            };\n\n            if font.mFont.size.0 >= min_font_size {\n                return;\n            }\n\n            NonNegative(min_font_size)\n        };\n\n        builder.mutate_font().mFont.size = min_font_size;\n    }\n\n    /// <svg:text> is not affected by text zoom, and it uses a preshint to disable it. We fix up\n    /// the struct when this happens by unzooming its contained font values, which will have been\n    /// zoomed in the parent.\n    #[cfg(feature = \"gecko\")]\n    fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {\n        debug_assert!(self.seen.contains(LonghandId::XTextScale));\n\n        let parent_text_scale = builder.get_parent_font().clone__x_text_scale();\n        let text_scale = builder.get_font().clone__x_text_scale();\n        if parent_text_scale == text_scale {\n            return;\n        }\n        debug_assert_ne!(\n            parent_text_scale.text_zoom_enabled(),\n            text_scale.text_zoom_enabled(),\n            \"There's only one value that disables it\"\n        );\n        debug_assert!(\n            !text_scale.text_zoom_enabled(),\n            \"We only ever disable text zoom never enable it\"\n        );\n        let device = builder.device;\n        builder.mutate_font().unzoom_fonts(device);\n    }\n\n    fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {\n        debug_assert!(self.seen.contains(LonghandId::Zoom));\n        // NOTE(emilio): Intentionally not using the effective zoom here, since all the inherited\n        // zooms are already applied.\n        let old_size = builder.get_font().clone_font_size();\n        let new_size = old_size.zoom(builder.effective_zoom_for_inheritance);\n        if old_size == new_size {\n            return;\n        }\n        builder.mutate_font().set_font_size(new_size);\n    }\n\n    /// Special handling of font-size: math (used for MathML).\n    /// https://w3c.github.io/mathml-core/#the-math-script-level-property\n    /// TODO: Bug: 1548471: MathML Core also does not specify a script min size\n    /// should we unship that feature or standardize it?\n    #[cfg(feature = \"gecko\")]\n    fn recompute_math_font_size_if_needed(&self, context: &mut computed::Context) {\n        use crate::values::generics::NonNegative;\n\n        // Do not do anything if font-size: math or math-depth is not set.\n        if context.builder.get_font().clone_font_size().keyword_info.kw\n            != specified::FontSizeKeyword::Math\n        {\n            return;\n        }\n\n        const SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE: f32 = 0.71;\n\n        // Helper function that calculates the scale factor applied to font-size\n        // when math-depth goes from parent_math_depth to computed_math_depth.\n        // This function is essentially a modification of the MathML3's formula\n        // 0.71^(parent_math_depth - computed_math_depth) so that a scale factor\n        // of parent_script_percent_scale_down is applied when math-depth goes\n        // from 0 to 1 and parent_script_script_percent_scale_down is applied\n        // when math-depth goes from 0 to 2. This is also a straightforward\n        // implementation of the specification's algorithm:\n        // https://w3c.github.io/mathml-core/#the-math-script-level-property\n        fn scale_factor_for_math_depth_change(\n            parent_math_depth: i32,\n            computed_math_depth: i32,\n            parent_script_percent_scale_down: Option<f32>,\n            parent_script_script_percent_scale_down: Option<f32>,\n        ) -> f32 {\n            let mut a = parent_math_depth;\n            let mut b = computed_math_depth;\n            let c = SCALE_FACTOR_WHEN_INCREMENTING_MATH_DEPTH_BY_ONE;\n            let scale_between_0_and_1 = parent_script_percent_scale_down.unwrap_or_else(|| c);\n            let scale_between_0_and_2 =\n                parent_script_script_percent_scale_down.unwrap_or_else(|| c * c);\n            let mut s = 1.0;\n            let mut invert_scale_factor = false;\n            if a == b {\n                return s;\n            }\n            if b < a {\n                std::mem::swap(&mut a, &mut b);\n                invert_scale_factor = true;\n            }\n            let mut e = b - a;\n            if a <= 0 && b >= 2 {\n                s *= scale_between_0_and_2;\n                e -= 2;\n            } else if a == 1 {\n                s *= scale_between_0_and_2 / scale_between_0_and_1;\n                e -= 1;\n            } else if b == 1 {\n                s *= scale_between_0_and_1;\n                e -= 1;\n            }\n            s *= (c as f32).powi(e);\n            if invert_scale_factor {\n                1.0 / s.max(f32::MIN_POSITIVE)\n            } else {\n                s\n            }\n        }\n\n        let (new_size, new_unconstrained_size) = {\n            use crate::values::specified::font::QueryFontMetricsFlags;\n\n            let builder = &context.builder;\n            let font = builder.get_font();\n            let parent_font = builder.get_parent_font();\n\n            let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);\n\n            if delta == 0 {\n                return;\n            }\n\n            let mut min = parent_font.mScriptMinSize;\n            if font.mXTextScale.text_zoom_enabled() {\n                min = builder.device.zoom_text(min);\n            }\n\n            // Calculate scale factor following MathML Core's algorithm.\n            let scale = {\n                // Script scale factors are independent of orientation.\n                let font_metrics = context.query_font_metrics(\n                    FontBaseSize::InheritedStyle,\n                    FontMetricsOrientation::Horizontal,\n                    QueryFontMetricsFlags::NEEDS_MATH_SCALES,\n                );\n                scale_factor_for_math_depth_change(\n                    parent_font.mMathDepth as i32,\n                    font.mMathDepth as i32,\n                    font_metrics.script_percent_scale_down,\n                    font_metrics.script_script_percent_scale_down,\n                )\n            };\n\n            let parent_size = parent_font.mSize.0;\n            let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;\n            let new_size = parent_size.scale_by(scale);\n            let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);\n\n            if scale <= 1. {\n                // The parent size can be smaller than scriptminsize, e.g. if it\n                // was specified explicitly. Don't scale in this case, but we\n                // don't want to set it to scriptminsize either since that will\n                // make it larger.\n                if parent_size <= min {\n                    (parent_size, new_unconstrained_size)\n                } else {\n                    (min.max(new_size), new_unconstrained_size)\n                }\n            } else {\n                // If the new unconstrained size is larger than the min size,\n                // this means we have escaped the grasp of scriptminsize and can\n                // revert to using the unconstrained size.\n                // However, if the new size is even larger (perhaps due to usage\n                // of em units), use that instead.\n                (\n                    new_size.min(new_unconstrained_size.max(min)),\n                    new_unconstrained_size,\n                )\n            }\n        };\n        let font = context.builder.mutate_font();\n        font.mFont.size = NonNegative(new_size);\n        font.mSize = NonNegative(new_size);\n        font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);\n    }\n}\n"
  },
  {
    "path": "style/properties/computed_value_flags.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Misc information about a given computed style.\n\n/// Misc information about a given computed style.\n///\n/// All flags are currently inherited for text, pseudo elements, and\n/// anonymous boxes, see StyleBuilder::for_inheritance and its callsites.\n/// If we ever want to add some flags that shouldn't inherit for them,\n/// we might want to add a function to handle this.\n#[repr(C)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(crate::derives::MallocSizeOf))]\npub struct ComputedValueFlags(u32);\n\nbitflags! {\n    impl ComputedValueFlags: u32 {\n        /// Whether the style or any of the ancestors has a text-decoration-line\n        /// property that should get propagated to descendants.\n        ///\n        /// text-decoration-line is a reset property, but gets propagated in the\n        /// frame/box tree.\n        const HAS_TEXT_DECORATION_LINES = 1 << 0;\n\n        /// Whether line break inside should be suppressed.\n        ///\n        /// If this flag is set, the line should not be broken inside,\n        /// which means inlines act as if nowrap is set, <br> element is\n        /// suppressed, and blocks are inlinized.\n        ///\n        /// This bit is propagated to all children of line participants.\n        /// It is currently used by ruby to make its content unbreakable.\n        const SHOULD_SUPPRESS_LINEBREAK = 1 << 1;\n\n        /// A flag used to mark text that that has text-combine-upright.\n        ///\n        /// This is used from Gecko's layout engine.\n        const IS_TEXT_COMBINED = 1 << 2;\n\n        /// A flag used to mark styles under a relevant link that is also\n        /// visited.\n        const IS_RELEVANT_LINK_VISITED = 1 << 3;\n\n        /// A flag used to mark styles which are a ::first-line or under one.\n        const IS_IN_FIRST_LINE_SUBTREE = 1 << 4;\n\n        /// A flag used to mark styles which have contain:style or under one.\n        const SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE = 1 << 5;\n\n        /// Whether this style's `display` or `content`property depends on our parent style.\n        ///\n        /// This is important because it may affect our optimizations to avoid\n        /// computing the style of pseudo-elements, given whether the\n        /// pseudo-element is generated depends on the `display` (or `content`) value.\n        const DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE = 1 << 6;\n\n        /// Whether the child explicitly inherits any reset property.\n        const INHERITS_RESET_STYLE = 1 << 7;\n\n        /// Whether any value on our style is font-metric-dependent on our\n        /// primary font.\n        const DEPENDS_ON_SELF_FONT_METRICS = 1 << 8;\n\n        /// Whether any value on our style is font-metric-dependent on the\n        /// primary font of our parent.\n        const DEPENDS_ON_INHERITED_FONT_METRICS = 1 << 9;\n\n        /// Whether this style is the style of the document element.\n        const IS_ROOT_ELEMENT_STYLE = 1 << 10;\n\n        /// Whether this element is inside an `opacity: 0` subtree.\n        const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 11;\n\n        /// Whether there are author-specified rules for border-* properties\n        /// (except border-image-*), background-color, or background-image.\n        ///\n        /// TODO(emilio): Maybe do include border-image, see:\n        ///\n        /// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845\n        const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 12;\n\n        /// Whether the style depends on viewport units.\n        const USES_VIEWPORT_UNITS = 1 << 13;\n\n        /// Whether the style depends on viewport units on container queries.\n        ///\n        /// This needs to be a separate flag from `USES_VIEWPORT_UNITS` because\n        /// it causes us to re-match the style (rather than re-cascascading it,\n        /// which is enough for other uses of viewport units).\n        const USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES = 1 << 14;\n\n        /// A flag used to mark styles which have `container-type` of `size` or\n        /// `inline-size`, or under one.\n        const SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE = 1 << 15;\n\n        /// Whether the style uses container query units, in which case the style depends on the\n        /// container's size and we can't reuse it across cousins (without double-checking the\n        /// container at least).\n        const USES_CONTAINER_UNITS = 1 << 16;\n\n        /// Whether there are author-specific rules for text `color`.\n        const HAS_AUTHOR_SPECIFIED_TEXT_COLOR = 1 << 17;\n\n        /// Whether this style considered a scope style rule.\n        const CONSIDERED_NONTRIVIAL_SCOPED_STYLE = 1 << 18;\n\n        /// Whether this style is that of a `display: contents` element that is either a direct\n        /// child of an item container or another `display: contents` element, the style of which\n        /// has this flag set, marked in order to cascade beyond them to the descendants of the\n        /// the item container that do generate a box.\n        const DISPLAY_CONTENTS_IN_ITEM_CONTAINER = 1 << 19;\n\n        /// Whether there are author-specific rules for `text-shadow`.\n        const HAS_AUTHOR_SPECIFIED_TEXT_SHADOW = 1 << 20;\n\n        /// Whether this style depends on container style query.\n        const DEPENDS_ON_CONTAINER_STYLE_QUERY = 1 << 21;\n\n        /// Whether this style is in an appearance: base subtree\n        const IS_IN_APPEARANCE_BASE_SUBTREE = 1 << 22;\n\n        /// Whether grid-auto-flow is author-specified.\n        const HAS_AUTHOR_SPECIFIED_GRID_AUTO_FLOW = 1 << 23;\n\n        /// Whether this style depends on root font metrics in container queries.\n        const DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY = 1 << 24;\n    }\n}\n\nimpl Default for ComputedValueFlags {\n    #[inline]\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nimpl ComputedValueFlags {\n    /// Flags that are unconditionally propagated to descendants.\n    #[inline]\n    fn inherited_flags() -> Self {\n        Self::IS_RELEVANT_LINK_VISITED\n            | Self::IS_IN_FIRST_LINE_SUBTREE\n            | Self::HAS_TEXT_DECORATION_LINES\n            | Self::IS_IN_OPACITY_ZERO_SUBTREE\n            | Self::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE\n            | Self::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE\n            | Self::IS_IN_APPEARANCE_BASE_SUBTREE\n    }\n\n    /// Flags that may be propagated to descendants.\n    #[inline]\n    fn maybe_inherited_flags() -> Self {\n        Self::inherited_flags()\n            | Self::SHOULD_SUPPRESS_LINEBREAK\n            | Self::DISPLAY_CONTENTS_IN_ITEM_CONTAINER\n    }\n\n    /// Flags that are an input to the cascade.\n    #[inline]\n    fn cascade_input_flags() -> Self {\n        Self::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES\n            | Self::CONSIDERED_NONTRIVIAL_SCOPED_STYLE\n            | Self::DEPENDS_ON_CONTAINER_STYLE_QUERY\n            | Self::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY\n    }\n\n    /// Returns the flags that are always propagated to descendants.\n    ///\n    /// See StyleAdjuster::set_bits and StyleBuilder.\n    #[inline]\n    pub fn inherited(self) -> Self {\n        self & Self::inherited_flags()\n    }\n\n    /// Flags that are conditionally propagated to descendants, just to handle\n    /// properly style invalidation.\n    #[inline]\n    pub fn maybe_inherited(self) -> Self {\n        self & Self::maybe_inherited_flags()\n    }\n\n    /// Flags that are an input to the cascade.\n    #[inline]\n    pub fn for_cascade_inputs(self) -> Self {\n        self & Self::cascade_input_flags()\n    }\n}\n"
  },
  {
    "path": "style/properties/counted_unknown_properties.py",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\nCOUNTED_UNKNOWN_PROPERTIES = [\n    \"-webkit-tap-highlight-color\",\n    \"speak\",\n    \"text-size-adjust\",\n    \"-webkit-user-drag\",\n    \"orphans\",\n    \"widows\",\n    \"-webkit-user-modify\",\n    \"-webkit-margin-before\",\n    \"-webkit-margin-after\",\n    \"-webkit-margin-start\",\n    \"-webkit-column-break-inside\",\n    \"-webkit-padding-start\",\n    \"-webkit-margin-end\",\n    \"-webkit-box-reflect\",\n    \"-webkit-print-color-adjust\",\n    \"-webkit-mask-box-image\",\n    \"-webkit-line-break\",\n    \"-webkit-writing-mode\",\n    \"-webkit-hyphenate-character\",\n    \"-webkit-highlight\",\n    \"background-repeat-x\",\n    \"-webkit-padding-end\",\n    \"background-repeat-y\",\n    \"-webkit-text-emphasis-color\",\n    \"-webkit-margin-top-collapse\",\n    \"-webkit-rtl-ordering\",\n    \"-webkit-padding-before\",\n    \"-webkit-text-decorations-in-effect\",\n    \"-webkit-border-vertical-spacing\",\n    \"-webkit-locale\",\n    \"-webkit-padding-after\",\n    \"-webkit-border-horizontal-spacing\",\n    \"color-rendering\",\n    \"-webkit-column-break-before\",\n    \"-webkit-transform-origin-x\",\n    \"-webkit-transform-origin-y\",\n    \"-webkit-text-emphasis-position\",\n    \"buffered-rendering\",\n    \"-webkit-text-orientation\",\n    \"-webkit-text-combine\",\n    \"-webkit-text-emphasis-style\",\n    \"-webkit-text-emphasis\",\n    \"-webkit-mask-box-image-width\",\n    \"-webkit-mask-box-image-source\",\n    \"-webkit-mask-box-image-outset\",\n    \"-webkit-mask-box-image-slice\",\n    \"-webkit-mask-box-image-repeat\",\n    \"-webkit-margin-after-collapse\",\n    \"-webkit-border-before-color\",\n    \"-webkit-border-before-width\",\n    \"-webkit-perspective-origin-x\",\n    \"-webkit-perspective-origin-y\",\n    \"-webkit-margin-before-collapse\",\n    \"-webkit-border-before-style\",\n    \"-webkit-margin-bottom-collapse\",\n    \"-webkit-ruby-position\",\n    \"-webkit-column-break-after\",\n    \"-webkit-margin-collapse\",\n    \"-webkit-border-before\",\n    \"-webkit-border-end\",\n    \"-webkit-border-after\",\n    \"-webkit-border-start\",\n    \"-webkit-min-logical-width\",\n    \"-webkit-logical-height\",\n    \"-webkit-transform-origin-z\",\n    \"-webkit-font-size-delta\",\n    \"-webkit-logical-width\",\n    \"-webkit-max-logical-width\",\n    \"-webkit-min-logical-height\",\n    \"-webkit-max-logical-height\",\n    \"-webkit-border-end-color\",\n    \"-webkit-border-end-width\",\n    \"-webkit-border-start-color\",\n    \"-webkit-border-start-width\",\n    \"-webkit-border-after-color\",\n    \"-webkit-border-after-width\",\n    \"-webkit-border-end-style\",\n    \"-webkit-border-after-style\",\n    \"-webkit-border-start-style\",\n    \"-webkit-mask-repeat-x\",\n    \"-webkit-mask-repeat-y\",\n    \"user-zoom\",\n    \"min-zoom\",\n    \"-webkit-box-decoration-break\",\n    \"orientation\",\n    \"max-zoom\",\n    \"-webkit-app-region\",\n    \"-webkit-column-rule\",\n    \"-webkit-column-span\",\n    \"-webkit-column-gap\",\n    \"-webkit-shape-outside\",\n    \"-webkit-column-rule-width\",\n    \"-webkit-column-count\",\n    \"-webkit-opacity\",\n    \"-webkit-column-width\",\n    \"-webkit-shape-image-threshold\",\n    \"-webkit-column-rule-style\",\n    \"-webkit-columns\",\n    \"-webkit-column-rule-color\",\n    \"-webkit-shape-margin\",\n]\n"
  },
  {
    "path": "style/properties/counter_style_descriptors.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# The descriptors of the @counter-style rule.\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-system\n[system]\ntype = \"System\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-negative\n[negative]\ntype = \"Negative\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-negative\n[prefix]\ntype = \"Symbol\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-suffix\n[suffix]\ntype = \"Symbol\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-range\n[range]\ntype = \"CounterRanges\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-pad\n[pad]\ntype = \"Pad\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-fallback\n[fallback]\ntype = \"Fallback\"\n\n# https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols\n[symbols]\ntype = \"Symbols\"\n\n# https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols\n[additive-symbols]\ntype = \"AdditiveSymbols\"\n\n# https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as\n[speak-as]\ntype = \"SpeakAs\"\n"
  },
  {
    "path": "style/properties/data.py",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at https://mozilla.org/MPL/2.0/.\n\nimport re\nimport toml\nimport os\nfrom itertools import groupby\nfrom counted_unknown_properties import COUNTED_UNKNOWN_PROPERTIES\n\n# It is important that the order of these physical / logical variants matches\n# the order of the enum variants in logical_geometry.rs\nPHYSICAL_SIDES = [\"top\", \"right\", \"bottom\", \"left\"]\nPHYSICAL_CORNERS = [\"top-left\", \"top-right\", \"bottom-right\", \"bottom-left\"]\nPHYSICAL_AXES = [\"y\", \"x\"]\nPHYSICAL_SIZES = [\"height\", \"width\"]\nLOGICAL_SIDES = [\"block-start\", \"block-end\", \"inline-start\", \"inline-end\"]\nLOGICAL_CORNERS = [\"start-start\", \"start-end\", \"end-start\", \"end-end\"]\nLOGICAL_SIZES = [\"block-size\", \"inline-size\"]\nLOGICAL_AXES = [\"block\", \"inline\"]\n\nSYSTEM_FONT_LONGHANDS = \"\"\"font_family font_size font_style\n                           font_stretch font_weight\"\"\".split()\n\nPRIORITARY_PROPERTIES = set(\n    [\n        # The writing-mode group has the most priority of all property groups, as\n        # sizes like font-size can depend on it.\n        \"writing-mode\",\n        \"direction\",\n        \"text-orientation\",\n        # The fonts and colors group has the second priority, as all other lengths\n        # and colors depend on them.\n        #\n        # There are some interdependencies between these, but we fix them up in\n        # Cascade::fixup_font_stuff.\n        # Needed to properly compute the zoomed font-size.\n        \"-x-text-scale\",\n        # Needed to do font-size computation in a language-dependent way.\n        \"-x-lang\",\n        # Needed for ruby to respect language-dependent min-font-size\n        # preferences properly, see bug 1165538.\n        \"-moz-min-font-size-ratio\",\n        # font-size depends on math-depth's computed value.\n        \"math-depth\",\n        # Needed to compute the first available font and its used size,\n        # in order to compute font-relative units correctly.\n        \"font-size\",\n        \"font-size-adjust\",\n        \"font-weight\",\n        \"font-stretch\",\n        \"font-style\",\n        \"font-family\",\n        # color-scheme affects how system colors and light-dark() resolve.\n        \"color-scheme\",\n        # forced-color-adjust affects whether colors are adjusted.\n        \"forced-color-adjust\",\n        # Zoom affects all absolute lengths.\n        \"zoom\",\n        # Line height lengths depend on this.\n        \"line-height\",\n        # appearance and -moz-default-appearance control whether\n        # @appearance-base rules apply.\n        \"-moz-default-appearance\",\n        \"appearance\",\n    ]\n)\n\nVISITED_DEPENDENT_PROPERTIES = set(\n    [\n        \"column-rule-color\",\n        \"text-emphasis-color\",\n        \"-webkit-text-fill-color\",\n        \"-webkit-text-stroke-color\",\n        \"text-decoration-color\",\n        \"fill\",\n        \"stroke\",\n        \"caret-color\",\n        \"background-color\",\n        \"border-top-color\",\n        \"border-right-color\",\n        \"border-bottom-color\",\n        \"border-left-color\",\n        \"border-block-start-color\",\n        \"border-inline-end-color\",\n        \"border-block-end-color\",\n        \"border-inline-start-color\",\n        \"outline-color\",\n        \"color\",\n    ]\n)\n\n# Bitfield values for all rule types which can have property declarations.\nSTYLE_RULE = 1 << 0\nPAGE_RULE = 1 << 1\nKEYFRAME_RULE = 1 << 2\nPOSITION_TRY_RULE = 1 << 3\nSCOPE_RULE = 1 << 4\n\nALL_RULES = STYLE_RULE | PAGE_RULE | KEYFRAME_RULE | SCOPE_RULE\nDEFAULT_RULES = STYLE_RULE | KEYFRAME_RULE | SCOPE_RULE\nDEFAULT_RULES_AND_PAGE = DEFAULT_RULES | PAGE_RULE\nDEFAULT_RULES_EXCEPT_KEYFRAME = STYLE_RULE | SCOPE_RULE\nDEFAULT_RULES_AND_POSITION_TRY = DEFAULT_RULES | POSITION_TRY_RULE\n\n# Rule name to value dict\nRULE_VALUES = {\n    \"style\": STYLE_RULE,\n    \"page\": PAGE_RULE,\n    \"keyframe\": KEYFRAME_RULE,\n    \"position-try\": POSITION_TRY_RULE,\n    \"scope\": SCOPE_RULE,\n}\n\n\ndef rule_values_from_arg(rule_types):\n    if not rule_types:\n        return DEFAULT_RULES\n    mask = 0\n    for rule in rule_types:\n        mask |= RULE_VALUES[rule]\n    return mask\n\n\ndef to_rust_ident(name):\n    name = name.replace(\"-\", \"_\")\n    if name in [\"static\", \"super\", \"box\", \"move\"]:  # Rust keywords\n        name += \"_\"\n    return name\n\n\ndef idl_method(name, camel_case):\n    if name == \"float\":\n        return \"CssFloat\"\n    if name.startswith(\"-x-\"):\n        return camel_case[1:]\n    return camel_case\n\n\ndef to_snake_case(ident):\n    return re.sub(\"([A-Z]+)\", lambda m: \"_\" + m.group(1).lower(), ident).strip(\"_\")\n\n\ndef to_camel_case(ident):\n    return re.sub(\n        \"(^|_|-)([a-z0-9])\", lambda m: m.group(2).upper(), ident.strip(\"_\").strip(\"-\")\n    )\n\n\ndef to_camel_case_lower(ident):\n    camel = to_camel_case(ident)\n    return camel[0].lower() + camel[1:]\n\n\n# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute\ndef to_idl_name(name):\n    return re.sub(\"-([a-z])\", lambda m: m.group(1).upper(), name)\n\n\ndef parse_aliases(value):\n    aliases = {}\n    for pair in value:\n        [a, v] = pair.split(\"=\")\n        aliases[a] = v\n    return aliases\n\n\nclass Vector(object):\n    def __init__(\n        self,\n        need_index=False,\n        none_value=None,\n        separator='Comma',\n        animation_type=None,\n        simple_bindings=False,\n    ):\n        self.need_index = need_index\n        self.none_value = none_value\n        self.separator = separator\n        self.animation_type = animation_type\n        self.simple_bindings = simple_bindings\n\n\nclass Keyword(object):\n    def __init__(\n        self,\n        name,\n        values,\n        gecko_constant_prefix=None,\n        gecko_enum_prefix=None,\n        extra_gecko_values=None,\n        extra_servo_values=None,\n        gecko_aliases=None,\n        servo_aliases=None,\n        gecko_inexhaustive=None,\n    ):\n        self.name = name\n        self.values = values;\n        assert isinstance(values, list), name\n        if gecko_constant_prefix and gecko_enum_prefix:\n            raise TypeError(\n                \"Only one of gecko_constant_prefix and gecko_enum_prefix \"\n                \"can be specified\"\n            )\n        self.gecko_constant_prefix = gecko_constant_prefix\n        self.gecko_enum_prefix = gecko_enum_prefix\n        if not gecko_constant_prefix and not gecko_enum_prefix:\n            self.gecko_enum_prefix = \"Style\" + to_camel_case(name.replace(\"-moz-\", \"\").replace(\"-webkit-\", \"\"))\n        self.extra_gecko_values = extra_gecko_values or []\n        self.extra_servo_values = extra_servo_values or []\n        self.gecko_aliases = parse_aliases(gecko_aliases or [])\n        self.servo_aliases = parse_aliases(servo_aliases or [])\n        self.gecko_inexhaustive = gecko_inexhaustive or self.gecko_constant_prefix is not None\n\n    def values_for(self, engine):\n        if engine == \"gecko\":\n            return self.values + self.extra_gecko_values\n        elif engine == \"servo\":\n            return self.values + self.extra_servo_values\n        else:\n            raise Exception(\"Bad engine: \" + engine)\n\n    def aliases_for(self, engine):\n        if engine == \"gecko\":\n            return self.gecko_aliases\n        elif engine == \"servo\":\n            return self.servo_aliases\n        else:\n            raise Exception(\"Bad engine: \" + engine)\n\n    def gecko_constant(self, value):\n        moz_stripped = value.replace(\"-moz-\", \"\")\n        if self.gecko_enum_prefix:\n            parts = moz_stripped.replace(\"-\", \"_\").split(\"_\")\n            parts = [p.title() for p in parts]\n            return self.gecko_enum_prefix + \"::\" + \"\".join(parts)\n        else:\n            suffix = moz_stripped.replace(\"-\", \"_\")\n            return self.gecko_constant_prefix + \"_\" + suffix.upper()\n\n    def needs_cast(self):\n        return self.gecko_enum_prefix is None\n\n    def maybe_cast(self, type_str):\n        return \"as \" + type_str if self.needs_cast() else \"\"\n\n    def casted_constant_name(self, value, cast_type):\n        if cast_type is None:\n            raise TypeError(\"We should specify the cast_type.\")\n\n        if self.gecko_enum_prefix is None:\n            return cast_type.upper() + \"_\" + self.gecko_constant(value)\n        else:\n            return (\n                cast_type.upper()\n                + \"_\"\n                + self.gecko_constant(value).upper().replace(\"::\", \"_\")\n            )\n\n\ndef parse_property_aliases(alias_list):\n    result = []\n    if alias_list:\n        for alias in alias_list:\n            (name, _, pref) = alias.partition(\":\")\n            result.append((name, pref))\n    return result\n\n\ndef to_phys(name, logical, physical):\n    return name.replace(logical, physical).replace(\"inset-\", \"\")\n\n\nclass Property(object):\n    def __init__(\n        self,\n        name,\n        spec,\n        servo_pref,\n        gecko_pref,\n        enabled_in,\n        rule_types_allowed,\n        aliases,\n        extra_prefixes,\n        flags,\n    ):\n        self.name = name\n        if not spec:\n            raise TypeError(\"Spec should be specified for \" + name)\n        self.spec = spec\n        self.ident = to_rust_ident(name)\n        self.camel_case = to_camel_case(self.ident)\n        self.servo_pref = servo_pref\n        self.gecko_pref = gecko_pref\n        self.idl_method = idl_method(name, self.camel_case)\n        self.rule_types_allowed = rule_values_from_arg(rule_types_allowed)\n        # For enabled_in, the setup is as follows:\n        # It needs to be one of the four values: [\"\", \"ua\", \"chrome\", \"content\"]\n        #  * \"chrome\" implies \"ua\", and implies that they're explicitly\n        #    enabled.\n        #  * \"\" implies the property will never be parsed.\n        #  * \"content\" implies the property is accessible unconditionally,\n        #    modulo a pref, set via servo_pref / gecko_pref.\n        assert enabled_in in (\"\", \"ua\", \"chrome\", \"content\")\n        self.enabled_in = enabled_in\n        self.aliases = parse_property_aliases(aliases)\n        self.extra_prefixes = parse_property_aliases(extra_prefixes)\n        self.flags = flags.split() if flags else []\n\n    def rule_types_allowed_names(self):\n        for name in RULE_VALUES:\n            if self.rule_types_allowed & RULE_VALUES[name] != 0:\n                yield name\n\n    def experimental(self, engine):\n        if engine == \"gecko\":\n            return bool(self.gecko_pref)\n        elif engine == \"servo\":\n            return bool(self.servo_pref)\n        else:\n            raise Exception(\"Bad engine: \" + engine)\n\n    def explicitly_enabled_in_ua_sheets(self):\n        return self.enabled_in in (\"ua\", \"chrome\")\n\n    def explicitly_enabled_in_chrome(self):\n        return self.enabled_in == \"chrome\"\n\n    def enabled_in_content(self):\n        return self.enabled_in == \"content\"\n\n    def is_visited_dependent(self):\n        return self.name in VISITED_DEPENDENT_PROPERTIES\n\n    def is_prioritary(self):\n        return self.name in PRIORITARY_PROPERTIES\n\n    def noncustomcsspropertyid(self):\n        return \"NonCustomCSSPropertyId::eCSSProperty_\" + self.ident\n\n\nclass Longhand(Property):\n    def __init__(\n        self,\n        style_struct,\n        name,\n        initial_value=None,\n        initial_specified_value=None,\n        parse_method='parse',\n        spec=None,\n        animation_type=\"normal\",\n        keyword=None,\n        predefined_type=None,\n        servo_pref=None,\n        gecko_pref=None,\n        enabled_in=\"content\",\n        gecko_ffi_name=None,\n        has_effect_on_gecko_scrollbars=None,\n        rule_types_allowed=None,\n        cast_type=\"u8\",\n        logical=False,\n        logical_group=None,\n        aliases=None,\n        extra_prefixes=None,\n        boxed=False,\n        flags=None,\n        allow_quirks=False,\n        ignored_when_colors_disabled=False,\n        vector=None,\n        servo_restyle_damage=\"rebuild_box\",\n        affects=None,\n    ):\n        Property.__init__(\n            self,\n            name=name,\n            spec=spec,\n            servo_pref=servo_pref,\n            gecko_pref=gecko_pref,\n            enabled_in=enabled_in,\n            rule_types_allowed=rule_types_allowed,\n            aliases=aliases,\n            extra_prefixes=extra_prefixes,\n            flags=flags,\n        )\n\n        self.affects = affects\n        self.flags += self.affects_flags()\n\n        self.parse_method = parse_method\n        self.initial_value = initial_value\n        self.initial_specified_value = initial_specified_value\n        self.keyword = keyword\n        self.predefined_type = predefined_type\n        self.style_struct = style_struct\n        self.has_effect_on_gecko_scrollbars = has_effect_on_gecko_scrollbars\n        assert (\n            has_effect_on_gecko_scrollbars in [None, False, True]\n            and not style_struct.inherited\n            or (gecko_pref is None and enabled_in != \"\")\n            == (has_effect_on_gecko_scrollbars is None)\n        ), (\n            \"Property \"\n            + name\n            + \": has_effect_on_gecko_scrollbars must be \"\n            + \"specified, and must have a value of True or False, iff a \"\n            + \"property is inherited and is behind a Gecko pref or internal\"\n        )\n        self.gecko_ffi_name = gecko_ffi_name or \"m\" + self.camel_case\n        self.cast_type = cast_type\n        self.logical = logical\n        self.logical_group = logical_group\n        if self.logical:\n            assert logical_group, f\"Property {name} must have a logical group\"\n\n        self.boxed = boxed\n        self.allow_quirks = allow_quirks\n        self.ignored_when_colors_disabled = ignored_when_colors_disabled\n        self.vector = Vector(**vector) if vector is not None else None\n\n        assert animation_type in [\"none\", \"normal\", \"discrete\"]\n        self.animation_type = animation_type\n        self.animatable = animation_type != \"none\"\n\n        # See compute_damage for the various values this can take\n        self.servo_restyle_damage = servo_restyle_damage\n\n    def affects_flags(self):\n        # Layout is the stronger hint. This property animation affects layout\n        # or frame construction. `display` or `width` are examples that should\n        # use this.\n        if self.affects == \"layout\":\n            return [\"AFFECTS_LAYOUT\"]\n        # This property doesn't affect layout, but affects overflow.\n        # `transform` and co. are examples of this.\n        if self.affects == \"overflow\":\n            return [\"AFFECTS_OVERFLOW\"]\n        # This property affects the rendered output but doesn't affect layout.\n        # `opacity`, `color`, or `z-index` are examples of this.\n        if self.affects == \"paint\":\n            return [\"AFFECTS_PAINT\"]\n        # This property doesn't affect rendering in any way.\n        # `user-select` is an example of this.\n        assert self.affects == \"\", (\n            \"Property \"\n            + self.name\n            + ': affects must be specified and be one of [\"layout\", \"overflow\", \"paint\", \"\"], see Longhand.affects_flags for documentation'\n        )\n        return []\n\n    @staticmethod\n    def type():\n        return \"longhand\"\n\n    # For a given logical property, return the kind of mapping we need to\n    # perform, and which logical value we represent, in a tuple.\n    def logical_mapping_data(self, data):\n        if not self.logical:\n            return []\n        # Sizes and axes are basically the same for mapping, we just need\n        # slightly different replacements (block-size -> height, etc rather\n        # than -x/-y) below.\n        for [ty, logical_items, physical_items] in [\n            [\"Side\", LOGICAL_SIDES, PHYSICAL_SIDES],\n            [\"Corner\", LOGICAL_CORNERS, PHYSICAL_CORNERS],\n            [\"Axis\", LOGICAL_SIZES, PHYSICAL_SIZES],\n            [\"Axis\", LOGICAL_AXES, PHYSICAL_AXES],\n        ]:\n            candidate = [s for s in logical_items if s in self.name]\n            if candidate:\n                assert len(candidate) == 1\n                return [ty, candidate[0], logical_items, physical_items]\n        assert False, \"Don't know how to deal with \" + self.name\n\n    def logical_mapping_kind(self, data):\n        assert self.logical\n        [kind, item, _, _] = self.logical_mapping_data(data)\n        return \"LogicalMappingKind::{}(Logical{}::{})\".format(\n            kind, kind, to_camel_case(item.replace(\"-size\", \"\"))\n        )\n\n    # For a given logical property return all the physical property names\n    # corresponding to it.\n    def all_physical_mapped_properties(self, data):\n        if not self.logical:\n            return []\n        [_, logical_side, _, physical_items] = self.logical_mapping_data(data)\n        return [\n            data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]\n            for physical_side in physical_items\n        ]\n\n    def may_be_disabled_in(self, shorthand, engine):\n        if engine == \"gecko\":\n            return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref\n        elif engine == \"servo\":\n            return self.servo_pref and self.servo_pref != shorthand.servo_pref\n        else:\n            raise Exception(\"Bad engine: \" + engine)\n\n    def base_type(self):\n        if self.predefined_type and not self.vector:\n            return \"crate::values::specified::{}\".format(self.predefined_type)\n        return \"longhands::{}::SpecifiedValue\".format(self.ident)\n\n    def specified_type(self):\n        if self.predefined_type and not self.vector:\n            ty = \"crate::values::specified::{}\".format(self.predefined_type)\n        else:\n            ty = \"longhands::{}::SpecifiedValue\".format(self.ident)\n        if self.boxed:\n            ty = \"Box<{}>\".format(ty)\n        return ty\n\n    def is_zoom_dependent(self):\n        if not self.predefined_type:\n            return False\n        # TODO: Get this from SpecifiedValueInfo or so instead; see bug 1887627.\n        return self.predefined_type in {\n            \"BorderSideWidth\",\n            \"BorderSpacing\",\n            \"BoxShadow\",\n            \"Filter\",\n            \"FontSize\",\n            \"Inset\",\n            \"Length\",\n            \"LengthPercentage\",\n            \"LengthPercentageOrAuto\",\n            \"LetterSpacing\",\n            \"LineHeight\",\n            \"LineWidth\",\n            \"MaxSize\",\n            \"Margin\",\n            \"NonNegativeLength\",\n            \"NonNegativeLengthOrAuto\",\n            \"NonNegativeLengthOrNumber\",\n            \"NonNegativeLengthOrNumberRect\",\n            \"NonNegativeLengthPercentage\",\n            \"NonNegativeLengthPercentageOrAuto\",\n            \"NonNegativeLengthPercentageOrNormal\",\n            \"Position\",\n            \"PositionOrAuto\",\n            \"SimpleShadow\",\n            \"Size\",\n            \"SVGLength\",\n            \"SVGStrokeDashArray\",\n            \"SVGWidth\",\n            \"TextDecorationLength\",\n            \"TextDecorationInset\",\n            \"TextIndent\",\n            \"WordSpacing\",\n        }\n\n    def is_inherited_zoom_dependent_property(self):\n        if self.logical:\n            return False\n        if not self.style_struct.inherited:\n            return False\n        return self.is_zoom_dependent()\n\n    def specified_is_copy(self):\n        if self.vector or self.boxed:\n            return False\n        if self.predefined_type:\n            return self.predefined_type in {\n                \"AlignmentBaseline\",\n                \"Appearance\",\n                \"AnimationComposition\",\n                \"AnimationDirection\",\n                \"AnimationFillMode\",\n                \"AnimationPlayState\",\n                \"AspectRatio\",\n                \"BaselineSource\",\n                \"BreakBetween\",\n                \"BreakWithin\",\n                \"BackgroundRepeat\",\n                \"BorderImageRepeat\",\n                \"BorderStyle\",\n                \"table::CaptionSide\",\n                \"Clear\",\n                \"ColumnCount\",\n                \"Contain\",\n                \"ContentVisibility\",\n                \"ContainerType\",\n                \"Display\",\n                \"DominantBaseline\",\n                \"FillRule\",\n                \"Float\",\n                \"FontLanguageOverride\",\n                \"FontSizeAdjust\",\n                \"FontStretch\",\n                \"FontStyle\",\n                \"FontSynthesis\",\n                \"FontSynthesisStyle\",\n                \"FontVariantEastAsian\",\n                \"FontVariantLigatures\",\n                \"FontVariantNumeric\",\n                \"FontWeight\",\n                \"GreaterThanOrEqualToOneNumber\",\n                \"GridAutoFlow\",\n                \"ImageRendering\",\n                \"Inert\",\n                \"InitialLetter\",\n                \"Integer\",\n                \"PositionArea\",\n                \"PositionAreaKeyword\",\n                \"PositionProperty\",\n                \"ContentDistribution\",\n                \"ItemPlacement\",\n                \"SelfAlignment\",\n                \"JustifyItems\",\n                \"LineBreak\",\n                \"LineClamp\",\n                \"MasonryAutoFlow\",\n                \"MozTheme\",\n                \"BoolInteger\",\n                \"text::MozControlCharacterVisibility\",\n                \"MathDepth\",\n                \"MozScriptMinSize\",\n                \"MozScriptSizeMultiplier\",\n                \"TransformBox\",\n                \"TextDecorationSkipInk\",\n                \"NonNegativeNumber\",\n                \"OffsetRotate\",\n                \"Opacity\",\n                \"OutlineStyle\",\n                \"Overflow\",\n                \"OverflowAnchor\",\n                \"OverflowWrap\",\n                \"OverscrollBehavior\",\n                \"PageOrientation\",\n                \"Percentage\",\n                \"PointerEvents\",\n                \"PositionTryOrder\",\n                \"PositionVisibility\",\n                \"PrintColorAdjust\",\n                \"ForcedColorAdjust\",\n                \"Resize\",\n                \"RubyPosition\",\n                \"SVGOpacity\",\n                \"SVGPaintOrder\",\n                \"ScrollbarGutter\",\n                \"ScrollSnapAlign\",\n                \"ScrollSnapAxis\",\n                \"ScrollSnapStop\",\n                \"ScrollSnapStrictness\",\n                \"ScrollSnapType\",\n                \"TextAlign\",\n                \"TextAlignLast\",\n                \"TextAutospace\",\n                \"TextBoxEdge\",\n                \"TextBoxTrim\",\n                \"TextDecorationLine\",\n                \"TextEmphasisPosition\",\n                \"TextJustify\",\n                \"TextTransform\",\n                \"TextUnderlinePosition\",\n                \"TouchAction\",\n                \"TransformStyle\",\n                \"UserFocus\",\n                \"UserSelect\",\n                \"VectorEffect\",\n                \"WordBreak\",\n                \"WritingModeProperty\",\n                \"XSpan\",\n                \"XTextScale\",\n                \"ZIndex\",\n                \"Zoom\",\n            }\n        if self.name == \"overflow-y\":\n            return True\n        return bool(self.keyword)\n\n    def animated_type(self):\n        assert self.animatable\n        computed = \"<{} as ToComputedValue>::ComputedValue\".format(self.base_type())\n        if self.animation_type == \"discrete\":\n            return computed\n        return \"<{} as ToAnimatedValue>::AnimatedValue\".format(computed)\n\n\nclass Shorthand(Property):\n    def __init__(\n        self,\n        name,\n        sub_properties,\n        spec=None,\n        servo_pref=None,\n        gecko_pref=None,\n        kind=None,\n        allow_quirks=False,\n        derive_serialize=False,\n        derive_value_info=True,\n        enabled_in=\"content\",\n        rule_types_allowed=None,\n        aliases=None,\n        extra_prefixes=None,\n        flags=None,\n    ):\n        Property.__init__(\n            self,\n            name=name,\n            spec=spec,\n            servo_pref=servo_pref,\n            gecko_pref=gecko_pref,\n            enabled_in=enabled_in,\n            rule_types_allowed=rule_types_allowed,\n            aliases=aliases,\n            extra_prefixes=extra_prefixes,\n            flags=flags,\n        )\n        self.sub_properties = sub_properties\n        self.derive_serialize = derive_serialize\n        self.derive_value_info = derive_value_info\n        self.allow_quirks = allow_quirks\n        self.kind = kind\n\n    def get_animatable(self):\n        for sub in self.sub_properties:\n            if sub.animatable:\n                return True\n        return False\n\n    animatable = property(get_animatable)\n\n    @staticmethod\n    def type():\n        return \"shorthand\"\n\n\nclass Alias(object):\n    def __init__(self, name, original, gecko_pref):\n        self.name = name\n        self.ident = to_rust_ident(name)\n        self.camel_case = to_camel_case(self.ident)\n        self.idl_method = idl_method(name, self.camel_case)\n        self.original = original\n        self.enabled_in = original.enabled_in\n        self.animatable = original.animatable\n        self.servo_pref = original.servo_pref\n        self.gecko_pref = gecko_pref\n        self.rule_types_allowed = original.rule_types_allowed\n        self.flags = original.flags\n\n    @staticmethod\n    def type():\n        return \"alias\"\n\n    def rule_types_allowed_names(self):\n        for name in RULE_VALUES:\n            if self.rule_types_allowed & RULE_VALUES[name] != 0:\n                yield name\n\n    def experimental(self, engine):\n        if engine == \"gecko\":\n            return bool(self.gecko_pref)\n        elif engine == \"servo\":\n            return bool(self.servo_pref)\n        else:\n            raise Exception(\"Bad engine: \" + engine)\n\n    def explicitly_enabled_in_ua_sheets(self):\n        return self.enabled_in in [\"ua\", \"chrome\"]\n\n    def explicitly_enabled_in_chrome(self):\n        return self.enabled_in == \"chrome\"\n\n    def enabled_in_content(self):\n        return self.enabled_in == \"content\"\n\n    def noncustomcsspropertyid(self):\n        return \"NonCustomCSSPropertyId::eCSSPropertyAlias_%s\" % self.ident\n\n\nclass Method(object):\n    def __init__(self, name, return_type=None, arg_types=None, is_mut=False):\n        self.name = name\n        self.return_type = return_type\n        self.arg_types = arg_types or []\n        self.is_mut = is_mut\n\n    def arg_list(self):\n        args = [\"_: \" + x for x in self.arg_types]\n        args = [\"&mut self\" if self.is_mut else \"&self\"] + args\n        return \", \".join(args)\n\n    def signature(self):\n        sig = \"fn %s(%s)\" % (self.name, self.arg_list())\n        if self.return_type:\n            sig = sig + \" -> \" + self.return_type\n        return sig\n\n    def declare(self):\n        return self.signature() + \";\"\n\n    def stub(self):\n        return self.signature() + \"{ unimplemented!() }\"\n\n\nclass StyleStruct(object):\n    def __init__(self, name, inherited, gecko_name=None):\n        self.gecko_struct_name = \"Gecko\" + name\n        self.name = name\n        self.name_lower = to_snake_case(name)\n        self.ident = to_rust_ident(self.name_lower)\n        self.longhands = []\n        self.inherited = inherited\n        self.gecko_name = gecko_name or name\n        self.gecko_ffi_name = \"nsStyle\" + self.gecko_name\n        self.document_dependent = self.gecko_name in [\"Font\", \"Visibility\", \"Text\"]\n\n\nclass Descriptor(object):\n    def __init__(self, name, type, gecko_pref=None, ignore_malloc_size_of=None):\n        self.name = name\n        self.type = type\n        self.gecko_pref = gecko_pref\n        self.ignore_malloc_size_of = ignore_malloc_size_of\n        self.ident = to_rust_ident(name)\n        self.camel_case = to_camel_case(name)\n\n\nclass PropertiesData(object):\n    def __init__(self, engine):\n        self.engine = engine\n        self.longhands = []\n        self.longhands_by_name = {}\n        self.logical_groups = {}\n        self.longhand_aliases = []\n        self.shorthands = []\n        self.shorthands_by_name = {}\n        self.shorthand_aliases = []\n        self.counted_unknown_properties = [\n            CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES\n        ]\n\n        self.style_structs = [\n            StyleStruct(\"Background\", inherited=False),\n            StyleStruct(\"Border\", inherited=False),\n            StyleStruct(\"Box\", inherited=False, gecko_name=\"Display\"),\n            StyleStruct(\"Column\", inherited=False),\n            StyleStruct(\"Counters\", inherited=False, gecko_name=\"Content\"),\n            StyleStruct(\"Effects\", inherited=False),\n            StyleStruct(\"Font\", inherited=True),\n            StyleStruct(\"InheritedBox\", inherited=True, gecko_name=\"Visibility\"),\n            StyleStruct(\"InheritedSVG\", inherited=True, gecko_name=\"SVG\"),\n            StyleStruct(\"InheritedTable\", inherited=True, gecko_name=\"TableBorder\"),\n            StyleStruct(\"InheritedText\", inherited=True, gecko_name=\"Text\"),\n            StyleStruct(\"InheritedUI\", inherited=True, gecko_name=\"UI\"),\n            StyleStruct(\"List\", inherited=True),\n            StyleStruct(\"Margin\", inherited=False),\n            StyleStruct(\"Outline\", inherited=False),\n            StyleStruct(\"Padding\", inherited=False),\n            StyleStruct(\"Page\", inherited=False),\n            StyleStruct(\"Position\", inherited=False),\n            StyleStruct(\"SVG\", inherited=False, gecko_name=\"SVGReset\"),\n            StyleStruct(\"Table\", inherited=False),\n            StyleStruct(\"Text\", inherited=False, gecko_name=\"TextReset\"),\n            StyleStruct(\"UI\", inherited=False, gecko_name=\"UIReset\"),\n            StyleStruct(\"XUL\", inherited=False),\n        ]\n\n        longhands_toml = toml.loads(open(os.path.join(os.path.dirname(__file__), \"longhands.toml\")).read())\n        for name, args in longhands_toml.items():\n            style_struct = self.style_struct_by_name_lower(args[\"struct\"])\n            del args['struct']\n\n            # Handle keyword properties\n            if 'keyword' in args:\n                keyword_dict = args.pop('keyword')\n                if 'values' not in keyword_dict:\n                    raise TypeError(f\"{name}: keyword should have 'values'\")\n                values = keyword_dict.pop('values')\n                keyword = Keyword(name, values, **keyword_dict)\n                self.declare_longhand(style_struct, name, keyword=keyword, **args)\n            else:\n                # Handle predefined_type properties\n                if 'type' not in args:\n                    raise TypeError(f\"{name} should have a type\")\n                args['predefined_type'] = args.pop('type')\n                if 'initial' not in args and not args.get('vector'):\n                    raise TypeError(f\"{name} should have an initial value (only vector properties should lack one)\")\n                args['initial_value'] = args.pop('initial', None)\n                self.declare_longhand(style_struct, name, **args)\n\n        for group, props in self.logical_groups.items():\n            logical_count = sum(1 for p in props if p.logical)\n            if logical_count * 2 != len(props):\n                raise RuntimeError(f\"Logical group {group} has unbalanced logical / physical properties\")\n\n        # After this code, `data.longhands` is sorted in the following order:\n        # - first all keyword variants and all variants known to be Copy,\n        # - second all the other variants, such as all variants with the same field\n        #   have consecutive discriminants.\n        # The variable `variants` contain the same entries as `data.longhands` in\n        # the same order, but must exist separately to the data source, because\n        # we then need to add three additional variants `WideKeywordDeclaration`,\n        # `VariableDeclaration` and `CustomDeclaration`.\n        self.declaration_variants = []\n        for property in self.longhands:\n            self.declaration_variants.append({\n                \"name\": property.camel_case,\n                \"type\": property.specified_type(),\n                \"doc\": \"`\" + property.name + \"`\",\n                \"copy\": property.specified_is_copy(),\n            })\n        groups = {}\n        keyfunc = lambda x: x[\"type\"]\n        sortkeys = {}\n        # WARNING: It is *really* important for the variants of `LonghandId`\n        # and `PropertyDeclaration` to be defined in the exact same order,\n        # with the exception of `CSSWideKeyword`, `WithVariables` and `Custom`,\n        # which don't exist in `LonghandId`.\n        for ty, group in groupby(sorted(self.declaration_variants, key=keyfunc), keyfunc):\n            group = list(group)\n            groups[ty] = group\n            for v in group:\n                if len(group) == 1:\n                    sortkeys[v[\"name\"]] = (not v[\"copy\"], 1, v[\"name\"], \"\")\n                else:\n                    sortkeys[v[\"name\"]] = (not v[\"copy\"], len(group), ty, v[\"name\"])\n        # It is extremely important to sort the `data.longhands` array here so\n        # that it is in the same order as `variants`, for `LonghandId` and\n        # `PropertyDeclarationId` to coincide.\n        self.longhands.sort(key=lambda x: sortkeys[x.camel_case])\n        self.declaration_variants.sort(key=lambda x: sortkeys[x[\"name\"]])\n        self.declaration_extra_variants = [\n            {\n                \"name\": \"CSSWideKeyword\",\n                \"type\": \"WideKeywordDeclaration\",\n                \"doc\": \"A CSS-wide keyword.\",\n                \"copy\": False,\n            },\n            {\n                \"name\": \"WithVariables\",\n                \"type\": \"VariableDeclaration\",\n                \"doc\": \"An unparsed declaration.\",\n                \"copy\": False,\n            },\n            {\n                \"name\": \"Custom\",\n                \"type\": \"CustomDeclaration\",\n                \"doc\": \"A custom property declaration.\",\n                \"copy\": False,\n            },\n        ]\n        for v in self.declaration_extra_variants:\n            self.declaration_variants.append(v)\n            groups[v[\"type\"]] = [v]\n\n        shorthands_toml = toml.loads(open(os.path.join(os.path.dirname(__file__), \"shorthands.toml\")).read())\n        for name, args in shorthands_toml.items():\n            self.declare_shorthand(name, **args)\n        self.declare_all_shorthand()\n\n        self.font_face_descriptors = self._load_descriptors(\"font_face_descriptors.toml\")\n        self.counter_style_descriptors = self._load_descriptors(\"counter_style_descriptors.toml\")\n        self.property_descriptors = self._load_descriptors(\"property_descriptors.toml\")\n        self.view_transition_descriptors = self._load_descriptors(\"view_transition_descriptors.toml\")\n\n\n    def declare_all_shorthand(self):\n        # We don't define the 'all' shorthand using the regular helpers:shorthand\n        # mechanism, since it causes some very large types to be generated.\n        #\n        # Make sure logical properties appear before its physical\n        # counter-parts, in order to prevent bugs like:\n        #\n        #   https://bugzilla.mozilla.org/show_bug.cgi?id=1410028\n        #\n        # FIXME(emilio): Adopt the resolution from:\n        #\n        #   https://github.com/w3c/csswg-drafts/issues/1898\n        #\n        # when there is one, whatever that is.\n        logical_longhands = []\n        other_longhands = []\n        for p in self.longhands:\n            if p.name in ['direction', 'unicode-bidi']:\n                continue;\n            if not p.enabled_in_content() and not p.experimental(self.engine):\n                continue;\n            if \"style\" not in p.rule_types_allowed_names():\n                continue;\n            if p.logical:\n                logical_longhands.append(p)\n            else:\n                other_longhands.append(p)\n\n        # Cache locality when iterating over the `all` shorthand is important\n        # for transition handling, so we sort by style struct.\n        # We technically don't care about the logical prop order (because those\n        # are not animated themselves), but we sort them the same way for\n        # consistency.\n        logical_longhands.sort(key=lambda p: p.style_struct.name)\n        other_longhands.sort(key=lambda p: p.style_struct.name)\n\n        all_names = list(map(lambda p: p.name, logical_longhands + other_longhands))\n\n        self.all_shorthand_length = len(all_names)\n        self.declare_shorthand(\n            \"all\",\n            all_names,\n            spec=\"https://drafts.csswg.org/css-cascade-3/#all-shorthand\"\n        )\n\n\n    def _load_descriptors(self, filename):\n        path = os.path.join(os.path.dirname(__file__), filename)\n        data = toml.loads(open(path).read())\n        return [Descriptor(name, **args) for name, args in data.items()]\n\n    def style_struct_by_name_lower(self, name):\n        for s in self.style_structs:\n            if s.name_lower == name:\n                return s\n        raise TypeError(f\"Unexpected struct name {name}\")\n\n    def active_style_structs(self):\n        return [s for s in self.style_structs if s.longhands]\n\n    def add_prefixed_aliases(self, property):\n        for prefix, pref in property.extra_prefixes:\n            property.aliases.append((\"-%s-%s\" % (prefix, property.name), pref))\n\n    def declare_longhand(self, style_struct, name, extra_gecko_aliases=None, engine=None, **kwargs):\n        if engine and self.engine != engine:\n            return\n        if extra_gecko_aliases and self.engine == \"gecko\":\n            kwargs.setdefault('aliases', []).extend(extra_gecko_aliases)\n        longhand = Longhand(style_struct, name, **kwargs)\n        self.add_prefixed_aliases(longhand)\n        longhand.aliases = [Alias(xp[0], longhand, xp[1]) for xp in longhand.aliases]\n        self.longhand_aliases += longhand.aliases\n        style_struct.longhands.append(longhand)\n        self.longhands.append(longhand)\n        self.longhands_by_name[name] = longhand\n        if longhand.logical_group:\n            self.logical_groups.setdefault(\n                longhand.logical_group, []\n            ).append(longhand)\n\n        return longhand\n\n    def declare_shorthand(self, name, sub_properties, extra_gecko_sub_properties=None, extra_gecko_aliases=None, engine=None, *args, **kwargs):\n        if engine and self.engine != engine:\n            return\n        if self.engine == \"gecko\":\n            if extra_gecko_sub_properties:\n                sub_properties.extend(extra_gecko_sub_properties)\n            if extra_gecko_aliases:\n                kwargs.setdefault('aliases', []).extend(extra_gecko_aliases)\n        sub_properties = [self.longhands_by_name[s] for s in sub_properties]\n        shorthand = Shorthand(name, sub_properties, *args, **kwargs)\n        self.add_prefixed_aliases(shorthand)\n        shorthand.aliases = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.aliases]\n        self.shorthand_aliases += shorthand.aliases\n        self.shorthands.append(shorthand)\n        self.shorthands_by_name[name] = shorthand\n        return shorthand\n\n    def shorthands_except_all(self):\n        return [s for s in self.shorthands if s.name != \"all\"]\n\n    def all_aliases(self):\n        return self.longhand_aliases + self.shorthand_aliases\n\n    def all_properties_and_aliases(self):\n        return self.longhands + self.shorthands + self.longhand_aliases + self.shorthand_aliases\n\n\ndef _add_logical_props(data, props):\n    groups = set()\n    for prop in props:\n        if prop not in data.longhands_by_name:\n            assert data.engine == \"servo\"\n            continue\n        prop = data.longhands_by_name[prop]\n        if prop.logical_group:\n            groups.add(prop.logical_group)\n    for group in groups:\n        for prop in data.logical_groups[group]:\n            props.add(prop.name)\n\n\n# These are probably Gecko bugs and should be supported per spec.\ndef _remove_common_first_line_and_first_letter_properties(props, engine):\n    if engine == \"gecko\":\n        props.remove(\"tab-size\")\n        props.remove(\"hyphens\")\n        props.remove(\"line-break\")\n        props.remove(\"text-align-last\")\n        props.remove(\"text-emphasis-position\")\n        props.remove(\"text-emphasis-style\")\n        props.remove(\"text-emphasis-color\")\n        props.remove(\"text-wrap-style\")\n\n    props.remove(\"overflow-wrap\")\n    props.remove(\"text-align\")\n    props.remove(\"text-justify\")\n    props.remove(\"white-space-collapse\")\n    props.remove(\"text-wrap-mode\")\n    props.remove(\"word-break\")\n    props.remove(\"text-indent\")\n\n\nclass PropertyRestrictions:\n    @staticmethod\n    def logical_group(data, group):\n        return [p.name for p in data.logical_groups[group]]\n\n    @staticmethod\n    def shorthand(data, shorthand):\n        if shorthand not in data.shorthands_by_name:\n            return []\n        return [p.name for p in data.shorthands_by_name[shorthand].sub_properties]\n\n    @staticmethod\n    def spec(data, spec_path):\n        return [p.name for p in data.longhands if spec_path in p.spec]\n\n    # https://svgwg.org/svg2-draft/propidx.html\n    @staticmethod\n    def svg_text_properties():\n        props = set(\n            [\n                \"fill\",\n                \"fill-opacity\",\n                \"fill-rule\",\n                \"paint-order\",\n                \"stroke\",\n                \"stroke-dasharray\",\n                \"stroke-dashoffset\",\n                \"stroke-linecap\",\n                \"stroke-linejoin\",\n                \"stroke-miterlimit\",\n                \"stroke-opacity\",\n                \"stroke-width\",\n                \"text-rendering\",\n                \"vector-effect\",\n            ]\n        )\n        return props\n\n    @staticmethod\n    def webkit_text_properties():\n        props = set(\n            [\n                # Kinda like css-text?\n                \"-webkit-text-stroke-width\",\n                \"-webkit-text-fill-color\",\n                \"-webkit-text-stroke-color\",\n            ]\n        )\n        return props\n\n    # https://drafts.csswg.org/css-pseudo/#first-letter-styling\n    @staticmethod\n    def first_letter(data):\n        props = set(\n            [\n                \"color\",\n                \"opacity\",\n                \"float\",\n                \"initial-letter\",\n                # Kinda like css-fonts?\n                \"-moz-osx-font-smoothing\",\n                \"alignment-baseline\",\n                \"baseline-shift\",\n                \"baseline-source\",\n                \"line-height\",\n                # Kinda like css-backgrounds?\n                \"background-blend-mode\",\n            ]\n            + PropertyRestrictions.shorthand(data, \"padding\")\n            + PropertyRestrictions.shorthand(data, \"margin\")\n            + PropertyRestrictions.spec(data, \"css-fonts\")\n            + PropertyRestrictions.spec(data, \"css-backgrounds\")\n            + PropertyRestrictions.spec(data, \"css-text\")\n            + PropertyRestrictions.spec(data, \"css-shapes\")\n            + PropertyRestrictions.spec(data, \"css-text-decor\")\n        )\n        props = props.union(PropertyRestrictions.svg_text_properties())\n        props = props.union(PropertyRestrictions.webkit_text_properties())\n\n        _add_logical_props(data, props)\n\n        _remove_common_first_line_and_first_letter_properties(props, data.engine)\n        return props\n\n    # https://drafts.csswg.org/css-pseudo/#first-line-styling\n    @staticmethod\n    def first_line(data):\n        props = set(\n            [\n                # Per spec.\n                \"color\",\n                \"opacity\",\n                # Kinda like css-fonts?\n                \"-moz-osx-font-smoothing\",\n                \"alignment-baseline\",\n                \"baseline-shift\",\n                \"baseline-source\",\n                \"line-height\",\n                # Kinda like css-backgrounds?\n                \"background-blend-mode\",\n            ]\n            + PropertyRestrictions.spec(data, \"css-fonts\")\n            + PropertyRestrictions.spec(data, \"css-backgrounds\")\n            + PropertyRestrictions.spec(data, \"css-text\")\n            + PropertyRestrictions.spec(data, \"css-text-decor\")\n        )\n        props = props.union(PropertyRestrictions.svg_text_properties())\n        props = props.union(PropertyRestrictions.webkit_text_properties())\n\n        # These are probably Gecko bugs and should be supported per spec.\n        for prop in PropertyRestrictions.shorthand(data, \"border\"):\n            props.remove(prop)\n        for prop in PropertyRestrictions.shorthand(data, \"border-radius\"):\n            props.remove(prop)\n        props.remove(\"box-shadow\")\n\n        _remove_common_first_line_and_first_letter_properties(props, data.engine)\n        return props\n\n    # https://drafts.csswg.org/css-pseudo/#placeholder\n    #\n    # The spec says that placeholder and first-line have the same restrictions,\n    # but that's not true in Gecko and we also allow a handful other properties\n    # for ::placeholder.\n    @staticmethod\n    def placeholder(data):\n        props = PropertyRestrictions.first_line(data)\n        props.add(\"opacity\")\n        props.add(\"text-overflow\")\n        props.add(\"text-align\")\n        props.add(\"text-justify\")\n        for p in PropertyRestrictions.shorthand(data, \"text-wrap\"):\n            props.add(p)\n        for p in PropertyRestrictions.shorthand(data, \"white-space\"):\n            props.add(p)\n        # ::placeholder can't be SVG text\n        props -= PropertyRestrictions.svg_text_properties()\n        # Historically ::placeholder's line-height was !important in the UA sheet.\n        props.remove(\"line-height\")\n\n        return props\n\n    # https://drafts.csswg.org/css-lists-3/#marker-properties\n    @staticmethod\n    def marker(data):\n        return set(\n            [\n                \"color\",\n                \"content\",\n                \"counter-increment\",\n                \"counter-reset\",\n                \"counter-set\",\n                \"cursor\",\n                \"direction\",\n                \"hyphens\",\n                \"line-height\",\n                \"quotes\",\n                \"text-combine-upright\",\n                \"text-emphasis-color\",\n                \"text-emphasis-position\",\n                \"text-emphasis-style\",\n                \"text-orientation\",\n                \"text-shadow\",\n                \"text-transform\",\n                \"unicode-bidi\",\n                \"-moz-osx-font-smoothing\",\n            ]\n            + PropertyRestrictions.shorthand(data, \"animation-range\")\n            + PropertyRestrictions.shorthand(data, \"text-wrap\")\n            + PropertyRestrictions.shorthand(data, \"white-space\")\n            + PropertyRestrictions.spec(data, \"css-fonts\")\n            + PropertyRestrictions.spec(data, \"css-animations\")\n            + PropertyRestrictions.spec(data, \"css-transitions\")\n        )\n\n    # https://www.w3.org/TR/webvtt1/#the-cue-pseudo-element\n    @staticmethod\n    def cue(data):\n        return set(\n            [\n                \"color\",\n                \"opacity\",\n                \"visibility\",\n                \"text-shadow\",\n                \"text-combine-upright\",\n                \"ruby-position\",\n                # XXX Should these really apply to cue?\n                \"-moz-osx-font-smoothing\",\n                # FIXME(emilio): background-blend-mode should be part of the\n                # background shorthand, and get reset, per\n                # https://drafts.fxtf.org/compositing/#background-blend-mode\n                \"background-blend-mode\",\n            ]\n            + PropertyRestrictions.shorthand(data, \"text-decoration\")\n            + PropertyRestrictions.shorthand(data, \"text-wrap\")\n            + PropertyRestrictions.shorthand(data, \"white-space\")\n            + PropertyRestrictions.shorthand(data, \"background\")\n            + PropertyRestrictions.shorthand(data, \"outline\")\n            + PropertyRestrictions.shorthand(data, \"font\")\n            + PropertyRestrictions.shorthand(data, \"font-synthesis\")\n        )\n\n\nclass CountedUnknownProperty:\n    def __init__(self, name):\n        self.name = name\n        self.ident = to_rust_ident(name)\n        self.camel_case = to_camel_case(self.ident)\n"
  },
  {
    "path": "style/properties/declaration_block.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A property declaration block.\n\n#![deny(missing_docs)]\n\nuse super::{\n    property_counts, AllShorthand, ComputedValues, LogicalGroupSet, LonghandIdSet,\n    LonghandIdSetIterator, NonCustomPropertyIdSet, PropertyDeclaration, PropertyDeclarationId,\n    PropertyId, ShorthandId, SourcePropertyDeclaration, SourcePropertyDeclarationDrain,\n    SubpropertiesVec,\n};\nuse crate::context::QuirksMode;\nuse crate::custom_properties;\nuse crate::derives::*;\nuse crate::dom::AttributeTracker;\nuse crate::error_reporting::{ContextualParseError, ParseErrorReporter};\nuse crate::parser::ParserContext;\nuse crate::properties::{\n    animated_properties::{AnimationValue, AnimationValueMap},\n    StyleBuilder,\n};\nuse crate::rule_cache::RuleCacheConditions;\nuse crate::rule_tree::RuleCascadeFlags;\nuse crate::selector_map::PrecomputedHashSet;\nuse crate::selector_parser::SelectorImpl;\nuse crate::shared_lock::Locked;\nuse crate::stylesheets::container_rule::ContainerSizeQuery;\nuse crate::stylesheets::{CssRuleType, Origin, UrlExtraData};\nuse crate::stylist::Stylist;\nuse crate::values::computed::Context;\nuse cssparser::{\n    parse_important, AtRuleParser, CowRcStr, DeclarationParser, Delimiter, ParseErrorKind, Parser,\n    ParserInput, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser,\n    SourceLocation,\n};\nuse itertools::Itertools;\nuse selectors::SelectorList;\nuse servo_arc::Arc;\nuse smallbitvec::SmallBitVec;\nuse smallvec::SmallVec;\nuse std::fmt::{self, Write};\nuse std::iter::Zip;\nuse std::slice::Iter;\nuse style_traits::{\n    CssString, CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,\n    TypedValueList,\n};\nuse thin_vec::ThinVec;\n\n/// A set of property declarations including animations and transitions.\n#[derive(Default)]\npub struct AnimationDeclarations {\n    /// Declarations for animations.\n    pub animations: Option<Arc<Locked<PropertyDeclarationBlock>>>,\n    /// Declarations for transitions.\n    pub transitions: Option<Arc<Locked<PropertyDeclarationBlock>>>,\n}\n\nimpl AnimationDeclarations {\n    /// Whether or not this `AnimationDeclarations` is empty.\n    pub fn is_empty(&self) -> bool {\n        self.animations.is_none() && self.transitions.is_none()\n    }\n}\n\n/// An enum describes how a declaration should update\n/// the declaration block.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\nenum DeclarationUpdate {\n    /// The given declaration doesn't update anything.\n    None,\n    /// The given declaration is new, and should be append directly.\n    Append,\n    /// The given declaration can be updated in-place at the given position.\n    UpdateInPlace { pos: usize },\n    /// The given declaration cannot be updated in-place, and an existing\n    /// one needs to be removed at the given position.\n    AppendAndRemove { pos: usize },\n}\n\n/// A struct describes how a declaration block should be updated by\n/// a `SourcePropertyDeclaration`.\n#[derive(Default)]\npub struct SourcePropertyDeclarationUpdate {\n    updates: SubpropertiesVec<DeclarationUpdate>,\n    new_count: usize,\n    any_removal: bool,\n}\n\n/// A declaration [importance][importance].\n///\n/// [importance]: https://drafts.csswg.org/css-cascade/#importance\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\npub enum Importance {\n    /// Indicates a declaration without `!important`.\n    Normal,\n\n    /// Indicates a declaration with `!important`.\n    Important,\n}\n\nimpl Default for Importance {\n    fn default() -> Self {\n        Self::Normal\n    }\n}\n\nimpl Importance {\n    /// Return whether this is an important declaration.\n    pub fn important(self) -> bool {\n        match self {\n            Self::Normal => false,\n            Self::Important => true,\n        }\n    }\n}\n\n/// A set of properties.\n#[derive(Clone, Debug, ToShmem, Default, MallocSizeOf)]\npub struct PropertyDeclarationIdSet {\n    longhands: LonghandIdSet,\n    custom: PrecomputedHashSet<custom_properties::Name>,\n}\n\nimpl PropertyDeclarationIdSet {\n    /// Add the given property to the set.\n    pub fn insert(&mut self, id: PropertyDeclarationId) -> bool {\n        match id {\n            PropertyDeclarationId::Longhand(id) => {\n                if self.longhands.contains(id) {\n                    return false;\n                }\n                self.longhands.insert(id);\n                return true;\n            },\n            PropertyDeclarationId::Custom(name) => self.custom.insert((*name).clone()),\n        }\n    }\n\n    /// Return whether the given property is in the set.\n    pub fn contains(&self, id: PropertyDeclarationId) -> bool {\n        match id {\n            PropertyDeclarationId::Longhand(id) => self.longhands.contains(id),\n            PropertyDeclarationId::Custom(name) => self.custom.contains(name),\n        }\n    }\n\n    /// Remove the given property from the set.\n    pub fn remove(&mut self, id: PropertyDeclarationId) {\n        match id {\n            PropertyDeclarationId::Longhand(id) => self.longhands.remove(id),\n            PropertyDeclarationId::Custom(name) => {\n                self.custom.remove(name);\n            },\n        }\n    }\n\n    /// Remove all properties from the set.\n    pub fn clear(&mut self) {\n        self.longhands.clear();\n        self.custom.clear();\n    }\n\n    /// Returns whether the set is empty.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.longhands.is_empty() && self.custom.is_empty()\n    }\n    /// Returns whether this set contains any reset longhand.\n    #[inline]\n    pub fn contains_any_reset(&self) -> bool {\n        self.longhands.contains_any_reset()\n    }\n\n    /// Returns whether this set contains all longhands in the specified set.\n    #[inline]\n    pub fn contains_all_longhands(&self, longhands: &LonghandIdSet) -> bool {\n        self.longhands.contains_all(longhands)\n    }\n\n    /// Returns whether this set contains all properties in the specified set.\n    #[inline]\n    pub fn contains_all(&self, properties: &PropertyDeclarationIdSet) -> bool {\n        if !self.longhands.contains_all(&properties.longhands) {\n            return false;\n        }\n        if properties.custom.len() > self.custom.len() {\n            return false;\n        }\n        properties\n            .custom\n            .iter()\n            .all(|item| self.custom.contains(item))\n    }\n\n    /// Iterate over the current property declaration id set.\n    pub fn iter(&self) -> PropertyDeclarationIdSetIterator<'_> {\n        PropertyDeclarationIdSetIterator {\n            longhands: self.longhands.iter(),\n            custom: self.custom.iter(),\n        }\n    }\n}\n\n/// An iterator over a set of longhand ids.\npub struct PropertyDeclarationIdSetIterator<'a> {\n    longhands: LonghandIdSetIterator<'a>,\n    custom: std::collections::hash_set::Iter<'a, custom_properties::Name>,\n}\n\nimpl<'a> Iterator for PropertyDeclarationIdSetIterator<'a> {\n    type Item = PropertyDeclarationId<'a>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        // LonghandIdSetIterator's implementation always returns None\n        // after it did it once, so the code below will then continue\n        // to iterate over the custom properties.\n        match self.longhands.next() {\n            Some(id) => Some(PropertyDeclarationId::Longhand(id)),\n            None => match self.custom.next() {\n                Some(a) => Some(PropertyDeclarationId::Custom(a)),\n                None => None,\n            },\n        }\n    }\n}\n\n/// Overridden declarations are skipped.\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(Clone, ToShmem, Default)]\npub struct PropertyDeclarationBlock {\n    /// The group of declarations, along with their importance.\n    ///\n    /// Only deduplicated declarations appear here.\n    declarations: ThinVec<PropertyDeclaration>,\n\n    /// The \"important\" flag for each declaration in `declarations`.\n    declarations_importance: SmallBitVec,\n\n    /// The set of properties that are present in the block.\n    property_ids: PropertyDeclarationIdSet,\n}\n\nimpl PartialEq for PropertyDeclarationBlock {\n    fn eq(&self, other: &Self) -> bool {\n        // property_ids must be equal if declarations are equal, so we don't\n        // need to compare them explicitly.\n        self.declarations == other.declarations\n            && self.declarations_importance == other.declarations_importance\n    }\n}\n\n/// Iterator over `(PropertyDeclaration, Importance)` pairs.\npub struct DeclarationImportanceIterator<'a> {\n    iter: Zip<Iter<'a, PropertyDeclaration>, smallbitvec::Iter<'a>>,\n}\n\nimpl<'a> Default for DeclarationImportanceIterator<'a> {\n    fn default() -> Self {\n        Self {\n            iter: [].iter().zip(smallbitvec::Iter::default()),\n        }\n    }\n}\n\nimpl<'a> DeclarationImportanceIterator<'a> {\n    /// Constructor.\n    fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self {\n        DeclarationImportanceIterator {\n            iter: declarations.iter().zip(important.iter()),\n        }\n    }\n}\n\nimpl<'a> Iterator for DeclarationImportanceIterator<'a> {\n    type Item = (&'a PropertyDeclaration, Importance);\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        self.iter.next().map(|(decl, important)| {\n            (\n                decl,\n                if important {\n                    Importance::Important\n                } else {\n                    Importance::Normal\n                },\n            )\n        })\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        self.iter.size_hint()\n    }\n}\n\nimpl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> {\n    #[inline(always)]\n    fn next_back(&mut self) -> Option<Self::Item> {\n        self.iter.next_back().map(|(decl, important)| {\n            (\n                decl,\n                if important {\n                    Importance::Important\n                } else {\n                    Importance::Normal\n                },\n            )\n        })\n    }\n}\n\n/// Iterator for AnimationValue to be generated from PropertyDeclarationBlock.\npub struct AnimationValueIterator<'a, 'cx, 'cx_a: 'cx> {\n    iter: DeclarationImportanceIterator<'a>,\n    context: &'cx mut Context<'cx_a>,\n    style: &'a ComputedValues,\n    default_values: &'a ComputedValues,\n}\n\nimpl<'a, 'cx, 'cx_a: 'cx> AnimationValueIterator<'a, 'cx, 'cx_a> {\n    fn new(\n        declarations: &'a PropertyDeclarationBlock,\n        context: &'cx mut Context<'cx_a>,\n        style: &'a ComputedValues,\n        default_values: &'a ComputedValues,\n    ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {\n        AnimationValueIterator {\n            iter: declarations.declaration_importance_iter(),\n            context,\n            style,\n            default_values,\n        }\n    }\n}\n\nimpl<'a, 'cx, 'cx_a: 'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> {\n    type Item = AnimationValue;\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            let (decl, importance) = self.iter.next()?;\n\n            if importance.important() {\n                continue;\n            }\n\n            let animation = AnimationValue::from_declaration(\n                decl,\n                &mut self.context,\n                self.style,\n                self.default_values,\n                // TODO (descalante): should be able to get an attr from an animated element\n                &mut AttributeTracker::new_dummy(),\n            );\n\n            if let Some(anim) = animation {\n                return Some(anim);\n            }\n        }\n    }\n}\n\nimpl fmt::Debug for PropertyDeclarationBlock {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.declarations.fmt(f)\n    }\n}\n\nimpl PropertyDeclarationBlock {\n    /// Returns the number of declarations in the block.\n    #[inline]\n    pub fn len(&self) -> usize {\n        self.declarations.len()\n    }\n\n    /// Returns whether the block is empty.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.declarations.is_empty()\n    }\n\n    /// Create an empty block\n    #[inline]\n    pub fn new() -> Self {\n        PropertyDeclarationBlock {\n            declarations: ThinVec::new(),\n            declarations_importance: SmallBitVec::new(),\n            property_ids: PropertyDeclarationIdSet::default(),\n        }\n    }\n\n    /// Create a block with a single declaration\n    pub fn with_one(declaration: PropertyDeclaration, importance: Importance) -> Self {\n        let mut property_ids = PropertyDeclarationIdSet::default();\n        property_ids.insert(declaration.id());\n        let mut declarations = ThinVec::with_capacity(1);\n        declarations.push(declaration);\n        PropertyDeclarationBlock {\n            declarations,\n            declarations_importance: SmallBitVec::from_elem(1, importance.important()),\n            property_ids,\n        }\n    }\n\n    /// The declarations in this block\n    #[inline]\n    pub fn declarations(&self) -> &[PropertyDeclaration] {\n        &self.declarations\n    }\n\n    /// The `important` flags for declarations in this block\n    #[inline]\n    pub fn declarations_importance(&self) -> &SmallBitVec {\n        &self.declarations_importance\n    }\n\n    /// Iterate over `(PropertyDeclaration, Importance)` pairs\n    #[inline]\n    pub fn declaration_importance_iter(&self) -> DeclarationImportanceIterator<'_> {\n        DeclarationImportanceIterator::new(&self.declarations, &self.declarations_importance)\n    }\n\n    /// Iterate over `PropertyDeclaration` for Importance::Normal\n    #[inline]\n    pub fn normal_declaration_iter<'a>(\n        &'a self,\n    ) -> impl DoubleEndedIterator<Item = &'a PropertyDeclaration> {\n        self.declaration_importance_iter()\n            .filter(|(_, importance)| !importance.important())\n            .map(|(declaration, _)| declaration)\n    }\n\n    /// Return an iterator of (AnimatableLonghand, AnimationValue).\n    #[inline]\n    pub fn to_animation_value_iter<'a, 'cx, 'cx_a: 'cx>(\n        &'a self,\n        context: &'cx mut Context<'cx_a>,\n        style: &'a ComputedValues,\n        default_values: &'a ComputedValues,\n    ) -> AnimationValueIterator<'a, 'cx, 'cx_a> {\n        AnimationValueIterator::new(self, context, style, default_values)\n    }\n\n    /// Returns whether this block contains any declaration with `!important`.\n    ///\n    /// This is based on the `declarations_importance` bit-vector,\n    /// which should be maintained whenever `declarations` is changed.\n    #[inline]\n    pub fn any_important(&self) -> bool {\n        !self.declarations_importance.all_false()\n    }\n\n    /// Returns whether this block contains any declaration without `!important`.\n    ///\n    /// This is based on the `declarations_importance` bit-vector,\n    /// which should be maintained whenever `declarations` is changed.\n    #[inline]\n    pub fn any_normal(&self) -> bool {\n        !self.declarations_importance.all_true()\n    }\n\n    /// Returns a `PropertyDeclarationIdSet` representing the properties that are changed in\n    /// this block.\n    #[inline]\n    pub fn property_ids(&self) -> &PropertyDeclarationIdSet {\n        &self.property_ids\n    }\n\n    /// Returns whether this block contains a declaration of a given property id.\n    #[inline]\n    pub fn contains(&self, id: PropertyDeclarationId) -> bool {\n        self.property_ids.contains(id)\n    }\n\n    /// Returns whether this block contains any reset longhand.\n    #[inline]\n    pub fn contains_any_reset(&self) -> bool {\n        self.property_ids.contains_any_reset()\n    }\n\n    /// Get a declaration for a given property.\n    ///\n    /// NOTE: This is linear time in the case of custom properties or in the\n    /// case the longhand is actually in the declaration block.\n    #[inline]\n    pub fn get(\n        &self,\n        property: PropertyDeclarationId,\n    ) -> Option<(&PropertyDeclaration, Importance)> {\n        if !self.contains(property) {\n            return None;\n        }\n        self.declaration_importance_iter()\n            .find(|(declaration, _)| declaration.id() == property)\n    }\n\n    /// Tries to serialize a given shorthand from the declarations in this\n    /// block.\n    pub fn shorthand_to_css(\n        &self,\n        shorthand: ShorthandId,\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        // Step 1.2.1 of\n        // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue\n        let mut list = SmallVec::<[&_; 10]>::new();\n        let mut important_count = 0;\n\n        // Step 1.2.2\n        for longhand in shorthand.longhands() {\n            // Step 1.2.2.1\n            let declaration = self.get(PropertyDeclarationId::Longhand(longhand));\n\n            // Step 1.2.2.2 & 1.2.2.3\n            match declaration {\n                Some((declaration, importance)) => {\n                    list.push(declaration);\n                    if importance.important() {\n                        important_count += 1;\n                    }\n                },\n                None => return Ok(()),\n            }\n        }\n\n        // If there is one or more longhand with important, and one or more\n        // without important, we don't serialize it as a shorthand.\n        if important_count > 0 && important_count != list.len() {\n            return Ok(());\n        }\n\n        // Step 1.2.3\n        // We don't print !important when serializing individual properties,\n        // so we treat this as a normal-importance property\n        match shorthand.get_shorthand_appendable_value(&list) {\n            Some(appendable_value) => append_declaration_value(dest, appendable_value),\n            None => return Ok(()),\n        }\n    }\n\n    /// Find the value of the given property in this block and serialize it\n    ///\n    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue>\n    pub fn property_value_to_css(\n        &self,\n        property: &PropertyId,\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        // Step 1.1: done when parsing a string to PropertyId\n\n        // Step 1.2\n        let longhand_or_custom = match property.as_shorthand() {\n            Ok(shorthand) => return self.shorthand_to_css(shorthand, dest),\n            Err(longhand_or_custom) => longhand_or_custom,\n        };\n\n        if let Some((value, _importance)) = self.get(longhand_or_custom) {\n            // Step 2\n            value.to_css(dest)\n        } else {\n            // Step 3\n            Ok(())\n        }\n    }\n\n    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority>\n    pub fn property_priority(&self, property: &PropertyId) -> Importance {\n        // Step 1: done when parsing a string to PropertyId\n\n        // Step 2\n        match property.as_shorthand() {\n            Ok(shorthand) => {\n                // Step 2.1 & 2.2 & 2.3\n                if shorthand.longhands().all(|l| {\n                    self.get(PropertyDeclarationId::Longhand(l))\n                        .map_or(false, |(_, importance)| importance.important())\n                }) {\n                    Importance::Important\n                } else {\n                    Importance::Normal\n                }\n            },\n            Err(longhand_or_custom) => {\n                // Step 3\n                self.get(longhand_or_custom)\n                    .map_or(Importance::Normal, |(_, importance)| importance)\n            },\n        }\n    }\n\n    /// Find the value of the given property in this block and reify it.\n    /// Returns `Err(())` if the property is not present in this declaration\n    /// block.\n    pub fn property_value_to_typed_value_list(\n        &self,\n        property: &PropertyId,\n    ) -> Result<Option<TypedValueList>, ()> {\n        match property.as_shorthand() {\n            Ok(shorthand) => {\n                if shorthand\n                    .longhands()\n                    .all(|longhand| self.contains(PropertyDeclarationId::Longhand(longhand)))\n                {\n                    Ok(None)\n                } else {\n                    Err(())\n                }\n            },\n            Err(longhand_or_custom) => match self.get(longhand_or_custom) {\n                Some((value, _importance)) => Ok(value.to_typed_value_list()),\n                None => Err(()),\n            },\n        }\n    }\n\n    /// Adds or overrides the declaration for a given property in this block.\n    ///\n    /// See the documentation of `push` to see what impact `source` has when the\n    /// property is already there.\n    pub fn extend(\n        &mut self,\n        mut drain: SourcePropertyDeclarationDrain,\n        importance: Importance,\n    ) -> bool {\n        let all_shorthand_len = match drain.all_shorthand {\n            AllShorthand::NotSet => 0,\n            AllShorthand::CSSWideKeyword(_) | AllShorthand::WithVariables(_) => {\n                property_counts::ALL_SHORTHAND_EXPANDED\n            },\n        };\n        let push_calls_count = drain.declarations.len() + all_shorthand_len;\n\n        // With deduplication the actual length increase may be less than this.\n        self.declarations.reserve(push_calls_count);\n\n        let mut changed = false;\n        for decl in &mut drain.declarations {\n            changed |= self.push(decl, importance);\n        }\n        drain\n            .all_shorthand\n            .declarations()\n            .fold(changed, |changed, decl| {\n                changed | self.push(decl, importance)\n            })\n    }\n\n    /// Adds or overrides the declaration for a given property in this block.\n    ///\n    /// Returns whether the declaration has changed.\n    ///\n    /// This is only used for parsing and internal use.\n    pub fn push(&mut self, declaration: PropertyDeclaration, importance: Importance) -> bool {\n        let id = declaration.id();\n        if !self.property_ids.insert(id) {\n            let mut index_to_remove = None;\n            for (i, slot) in self.declarations.iter_mut().enumerate() {\n                if slot.id() != id {\n                    continue;\n                }\n\n                let important = self.declarations_importance[i];\n\n                // For declarations from parsing, non-important declarations\n                // shouldn't override existing important one.\n                if important && !importance.important() {\n                    return false;\n                }\n\n                index_to_remove = Some(i);\n                break;\n            }\n\n            if let Some(index) = index_to_remove {\n                self.declarations.remove(index);\n                self.declarations_importance.remove(index);\n                self.declarations.push(declaration);\n                self.declarations_importance.push(importance.important());\n                return true;\n            }\n        }\n\n        self.declarations.push(declaration);\n        self.declarations_importance.push(importance.important());\n        true\n    }\n\n    /// Prepares updating this declaration block with the given\n    /// `SourcePropertyDeclaration` and importance, and returns whether\n    /// there is something to update.\n    pub fn prepare_for_update(\n        &self,\n        source_declarations: &SourcePropertyDeclaration,\n        importance: Importance,\n        updates: &mut SourcePropertyDeclarationUpdate,\n    ) -> bool {\n        debug_assert!(updates.updates.is_empty());\n        // Check whether we are updating for an all shorthand change.\n        if !matches!(source_declarations.all_shorthand, AllShorthand::NotSet) {\n            debug_assert!(source_declarations.declarations.is_empty());\n            return source_declarations\n                .all_shorthand\n                .declarations()\n                .any(|decl| {\n                    !self.contains(decl.id())\n                        || self\n                            .declarations\n                            .iter()\n                            .enumerate()\n                            .find(|&(_, ref d)| d.id() == decl.id())\n                            .map_or(true, |(i, d)| {\n                                let important = self.declarations_importance[i];\n                                *d != decl || important != importance.important()\n                            })\n                });\n        }\n        // Fill `updates` with update information.\n        let mut any_update = false;\n        let new_count = &mut updates.new_count;\n        let any_removal = &mut updates.any_removal;\n        let updates = &mut updates.updates;\n        updates.extend(\n            source_declarations\n                .declarations\n                .iter()\n                .map(|declaration| {\n                    if !self.contains(declaration.id()) {\n                        return DeclarationUpdate::Append;\n                    }\n                    let longhand_id = declaration.id().as_longhand();\n                    if let Some(longhand_id) = longhand_id {\n                        if let Some(logical_group) = longhand_id.logical_group() {\n                            let mut needs_append = false;\n                            for (pos, decl) in self.declarations.iter().enumerate().rev() {\n                                let id = match decl.id().as_longhand() {\n                                    Some(id) => id,\n                                    None => continue,\n                                };\n                                if id == longhand_id {\n                                    if needs_append {\n                                        return DeclarationUpdate::AppendAndRemove { pos };\n                                    }\n                                    let important = self.declarations_importance[pos];\n                                    if decl == declaration && important == importance.important() {\n                                        return DeclarationUpdate::None;\n                                    }\n                                    return DeclarationUpdate::UpdateInPlace { pos };\n                                }\n                                if !needs_append\n                                    && id.logical_group() == Some(logical_group)\n                                    && id.is_logical() != longhand_id.is_logical()\n                                {\n                                    needs_append = true;\n                                }\n                            }\n                            unreachable!(\"Longhand should be found in loop above\");\n                        }\n                    }\n                    self.declarations\n                        .iter()\n                        .enumerate()\n                        .find(|&(_, ref decl)| decl.id() == declaration.id())\n                        .map_or(DeclarationUpdate::Append, |(pos, decl)| {\n                            let important = self.declarations_importance[pos];\n                            if decl == declaration && important == importance.important() {\n                                DeclarationUpdate::None\n                            } else {\n                                DeclarationUpdate::UpdateInPlace { pos }\n                            }\n                        })\n                })\n                .inspect(|update| {\n                    if matches!(update, DeclarationUpdate::None) {\n                        return;\n                    }\n                    any_update = true;\n                    match update {\n                        DeclarationUpdate::Append => {\n                            *new_count += 1;\n                        },\n                        DeclarationUpdate::AppendAndRemove { .. } => {\n                            *any_removal = true;\n                        },\n                        _ => {},\n                    }\n                }),\n        );\n        any_update\n    }\n\n    /// Update this declaration block with the given data.\n    pub fn update(\n        &mut self,\n        drain: SourcePropertyDeclarationDrain,\n        importance: Importance,\n        updates: &mut SourcePropertyDeclarationUpdate,\n    ) {\n        let important = importance.important();\n        if !matches!(drain.all_shorthand, AllShorthand::NotSet) {\n            debug_assert!(updates.updates.is_empty());\n            for decl in drain.all_shorthand.declarations() {\n                let id = decl.id();\n                if self.property_ids.insert(id) {\n                    self.declarations.push(decl);\n                    self.declarations_importance.push(important);\n                } else {\n                    let (idx, slot) = self\n                        .declarations\n                        .iter_mut()\n                        .enumerate()\n                        .find(|&(_, ref d)| d.id() == decl.id())\n                        .unwrap();\n                    *slot = decl;\n                    self.declarations_importance.set(idx, important);\n                }\n            }\n            return;\n        }\n\n        self.declarations.reserve(updates.new_count);\n        if updates.any_removal {\n            // Prepare for removal and fixing update positions.\n            struct UpdateOrRemoval<'a> {\n                item: &'a mut DeclarationUpdate,\n                pos: usize,\n                remove: bool,\n            }\n            let mut updates_and_removals: SubpropertiesVec<UpdateOrRemoval> = updates\n                .updates\n                .iter_mut()\n                .filter_map(|item| {\n                    let (pos, remove) = match *item {\n                        DeclarationUpdate::UpdateInPlace { pos } => (pos, false),\n                        DeclarationUpdate::AppendAndRemove { pos } => (pos, true),\n                        _ => return None,\n                    };\n                    Some(UpdateOrRemoval { item, pos, remove })\n                })\n                .collect();\n            // Execute removals. It's important to do it in reverse index order,\n            // so that removing doesn't invalidate following positions.\n            updates_and_removals.sort_unstable_by_key(|update| update.pos);\n            updates_and_removals\n                .iter()\n                .rev()\n                .filter(|update| update.remove)\n                .for_each(|update| {\n                    self.declarations.remove(update.pos);\n                    self.declarations_importance.remove(update.pos);\n                });\n            // Fixup pos field for updates.\n            let mut removed_count = 0;\n            for update in updates_and_removals.iter_mut() {\n                if update.remove {\n                    removed_count += 1;\n                    continue;\n                }\n                debug_assert_eq!(\n                    *update.item,\n                    DeclarationUpdate::UpdateInPlace { pos: update.pos }\n                );\n                *update.item = DeclarationUpdate::UpdateInPlace {\n                    pos: update.pos - removed_count,\n                };\n            }\n        }\n        // Execute updates and appends.\n        for (decl, update) in drain.declarations.zip_eq(updates.updates.iter()) {\n            match *update {\n                DeclarationUpdate::None => {},\n                DeclarationUpdate::Append | DeclarationUpdate::AppendAndRemove { .. } => {\n                    self.property_ids.insert(decl.id());\n                    self.declarations.push(decl);\n                    self.declarations_importance.push(important);\n                },\n                DeclarationUpdate::UpdateInPlace { pos } => {\n                    self.declarations[pos] = decl;\n                    self.declarations_importance.set(pos, important);\n                },\n            }\n        }\n        updates.updates.clear();\n    }\n\n    /// Returns the first declaration that would be removed by removing\n    /// `property`.\n    #[inline]\n    pub fn first_declaration_to_remove(&self, property: &PropertyId) -> Option<usize> {\n        if let Err(longhand_or_custom) = property.as_shorthand() {\n            if !self.contains(longhand_or_custom) {\n                return None;\n            }\n        }\n\n        self.declarations\n            .iter()\n            .position(|declaration| declaration.id().is_or_is_longhand_of(property))\n    }\n\n    /// Removes a given declaration at a given index.\n    #[inline]\n    fn remove_declaration_at(&mut self, i: usize) {\n        self.property_ids.remove(self.declarations[i].id());\n        self.declarations_importance.remove(i);\n        self.declarations.remove(i);\n    }\n\n    /// Clears all the declarations from this block.\n    #[inline]\n    pub fn clear(&mut self) {\n        self.declarations_importance.clear();\n        self.declarations.clear();\n        self.property_ids.clear();\n    }\n\n    /// <https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty>\n    ///\n    /// `first_declaration` needs to be the result of\n    /// `first_declaration_to_remove`.\n    #[inline]\n    pub fn remove_property(&mut self, property: &PropertyId, first_declaration: usize) {\n        debug_assert_eq!(\n            Some(first_declaration),\n            self.first_declaration_to_remove(property)\n        );\n        debug_assert!(self.declarations[first_declaration]\n            .id()\n            .is_or_is_longhand_of(property));\n\n        self.remove_declaration_at(first_declaration);\n\n        let shorthand = match property.as_shorthand() {\n            Ok(s) => s,\n            Err(_longhand_or_custom) => return,\n        };\n\n        let mut i = first_declaration;\n        let mut len = self.len();\n        while i < len {\n            if !self.declarations[i].id().is_longhand_of(shorthand) {\n                i += 1;\n                continue;\n            }\n\n            self.remove_declaration_at(i);\n            len -= 1;\n        }\n    }\n\n    /// Take a declaration block known to contain a single property and serialize it.\n    pub fn single_value_to_css(\n        &self,\n        property: &PropertyId,\n        dest: &mut CssStringWriter,\n        computed_values: Option<&ComputedValues>,\n        stylist: &Stylist,\n    ) -> fmt::Result {\n        if let Ok(shorthand) = property.as_shorthand() {\n            return self.shorthand_to_css(shorthand, dest);\n        }\n\n        // FIXME(emilio): Should this assert, or assert that the declaration is\n        // the property we expect?\n        let declaration = match self.declarations.get(0) {\n            Some(d) => d,\n            None => return Err(fmt::Error),\n        };\n\n        let mut rule_cache_conditions = RuleCacheConditions::default();\n        let mut context = Context::new(\n            StyleBuilder::new(\n                stylist.device(),\n                Some(stylist),\n                computed_values,\n                None,\n                None,\n                false,\n            ),\n            stylist.quirks_mode(),\n            &mut rule_cache_conditions,\n            ContainerSizeQuery::none(),\n            RuleCascadeFlags::empty(),\n        );\n\n        if let Some(cv) = computed_values {\n            context.builder.substitution_functions.custom_properties =\n                cv.custom_properties().clone();\n        };\n\n        match (declaration, computed_values) {\n            // If we have a longhand declaration with variables, those variables\n            // will be stored as unparsed values.\n            //\n            // As a temporary measure to produce sensible results in Gecko's\n            // getKeyframes() implementation for CSS animations, if\n            // |computed_values| is supplied, we use it to expand such variable\n            // declarations. This will be fixed properly in Gecko bug 1391537.\n            (&PropertyDeclaration::WithVariables(ref declaration), Some(_)) => declaration\n                .value\n                .substitute_variables(\n                    declaration.id,\n                    &context.builder.substitution_functions,\n                    stylist,\n                    &context,\n                    &mut Default::default(),\n                    &mut AttributeTracker::new_dummy(),\n                )\n                .to_css(dest),\n            (ref d, _) => d.to_css(dest),\n        }\n    }\n\n    /// Convert AnimationValueMap to PropertyDeclarationBlock.\n    pub fn from_animation_value_map(animation_value_map: &AnimationValueMap) -> Self {\n        let len = animation_value_map.len();\n        let mut declarations = ThinVec::with_capacity(len);\n        let mut property_ids = PropertyDeclarationIdSet::default();\n\n        for (property, animation_value) in animation_value_map.iter() {\n            property_ids.insert(property.as_borrowed());\n            declarations.push(animation_value.uncompute());\n        }\n\n        PropertyDeclarationBlock {\n            declarations,\n            property_ids,\n            declarations_importance: SmallBitVec::from_elem(len, false),\n        }\n    }\n\n    /// Returns true if the declaration block has a CSSWideKeyword for the given\n    /// property.\n    pub fn has_css_wide_keyword(&self, property: &PropertyId) -> bool {\n        if let Err(longhand_or_custom) = property.as_shorthand() {\n            if !self.property_ids.contains(longhand_or_custom) {\n                return false;\n            }\n        }\n        self.declarations.iter().any(|decl| {\n            decl.id().is_or_is_longhand_of(property) && decl.get_css_wide_keyword().is_some()\n        })\n    }\n\n    /// Like the method on ToCss, but without the type parameter to avoid\n    /// accidentally monomorphizing this large function multiple times for\n    /// different writers.\n    ///\n    /// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block\n    pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {\n        let mut is_first_serialization = true; // trailing serializations should have a prepended space\n\n        // Step 1 -> dest = result list\n\n        // Step 2\n        //\n        // NOTE(emilio): We reuse this set for both longhands and shorthands\n        // with subtly different meaning. For longhands, only longhands that\n        // have actually been serialized (either by themselves, or as part of a\n        // shorthand) appear here. For shorthands, all the shorthands that we've\n        // attempted to serialize appear here.\n        let mut already_serialized = NonCustomPropertyIdSet::new();\n\n        // Step 3\n        'declaration_loop: for (declaration, importance) in self.declaration_importance_iter() {\n            // Step 3.1\n            let property = declaration.id();\n            let longhand_id = match property {\n                PropertyDeclarationId::Longhand(id) => id,\n                PropertyDeclarationId::Custom(..) => {\n                    // Given the invariants that there are no duplicate\n                    // properties in a declaration block, and that custom\n                    // properties can't be part of a shorthand, we can just care\n                    // about them here.\n                    append_serialization(\n                        dest,\n                        &property,\n                        AppendableValue::Declaration(declaration),\n                        importance,\n                        &mut is_first_serialization,\n                    )?;\n                    continue;\n                },\n            };\n\n            // Step 3.2\n            if already_serialized.contains(longhand_id.into()) {\n                continue;\n            }\n\n            // Steps 3.3 & 3.4\n            for shorthand in longhand_id.shorthands() {\n                // We already attempted to serialize this shorthand before.\n                if already_serialized.contains(shorthand.into()) {\n                    continue;\n                }\n                already_serialized.insert(shorthand.into());\n\n                if shorthand.is_legacy_shorthand() {\n                    continue;\n                }\n\n                // Step 3.3.1:\n                //     Let longhands be an array consisting of all CSS\n                //     declarations in declaration block’s declarations that\n                //     that are not in already serialized and have a property\n                //     name that maps to one of the shorthand properties in\n                //     shorthands.\n                let longhands = {\n                    // TODO(emilio): This could just index in an array if we\n                    // remove pref-controlled longhands.\n                    let mut ids = LonghandIdSet::new();\n                    for longhand in shorthand.longhands() {\n                        ids.insert(longhand);\n                    }\n                    ids\n                };\n\n                // Step 3.4.2\n                //     If all properties that map to shorthand are not present\n                //     in longhands, continue with the steps labeled shorthand\n                //     loop.\n                if !self.property_ids.contains_all_longhands(&longhands) {\n                    continue;\n                }\n\n                // Step 3.4.3:\n                //     Let current longhands be an empty array.\n                let mut current_longhands = SmallVec::<[&_; 10]>::new();\n                let mut logical_groups = LogicalGroupSet::new();\n                let mut saw_one = false;\n                let mut logical_mismatch = false;\n                let mut seen = LonghandIdSet::new();\n                let mut important_count = 0;\n\n                // Step 3.4.4:\n                //    Append all CSS declarations in longhands that have a\n                //    property name that maps to shorthand to current longhands.\n                for (declaration, importance) in self.declaration_importance_iter() {\n                    let longhand = match declaration.id() {\n                        PropertyDeclarationId::Longhand(id) => id,\n                        PropertyDeclarationId::Custom(..) => continue,\n                    };\n\n                    if longhands.contains(longhand) {\n                        saw_one = true;\n                        if importance.important() {\n                            important_count += 1;\n                        }\n                        current_longhands.push(declaration);\n                        if shorthand != ShorthandId::All {\n                            // All is special because it contains both physical\n                            // and logical longhands.\n                            if let Some(g) = longhand.logical_group() {\n                                logical_groups.insert(g);\n                            }\n                            seen.insert(longhand);\n                            if seen == longhands {\n                                break;\n                            }\n                        }\n                    } else if saw_one {\n                        if let Some(g) = longhand.logical_group() {\n                            if logical_groups.contains(g) {\n                                logical_mismatch = true;\n                                break;\n                            }\n                        }\n                    }\n                }\n\n                // 3.4.5:\n                //     If there is one or more CSS declarations in current\n                //     longhands have their important flag set and one or more\n                //     with it unset, continue with the steps labeled shorthand\n                //     loop.\n                let is_important = important_count > 0;\n                if is_important && important_count != current_longhands.len() {\n                    continue;\n                }\n\n                // 3.4.6:\n                //    If there’s any declaration in declaration block in between\n                //    the first and the last longhand in current longhands which\n                //    belongs to the same logical property group, but has a\n                //    different mapping logic as any of the longhands in current\n                //    longhands, and is not in current longhands, continue with\n                //    the steps labeled shorthand loop.\n                if logical_mismatch {\n                    continue;\n                }\n\n                let importance = if is_important {\n                    Importance::Important\n                } else {\n                    Importance::Normal\n                };\n\n                // 3.4.7:\n                //    Let value be the result of invoking serialize a CSS value\n                //    of current longhands.\n                let appendable_value =\n                    match shorthand.get_shorthand_appendable_value(&current_longhands) {\n                        None => continue,\n                        Some(appendable_value) => appendable_value,\n                    };\n\n                // We avoid re-serializing if we're already an\n                // AppendableValue::Css.\n                let mut v = CssString::new();\n                let value = match appendable_value {\n                    AppendableValue::Css(css) => {\n                        debug_assert!(!css.is_empty());\n                        appendable_value\n                    },\n                    other => {\n                        append_declaration_value(&mut v, other)?;\n\n                        // 3.4.8:\n                        //     If value is the empty string, continue with the\n                        //     steps labeled shorthand loop.\n                        if v.is_empty() {\n                            continue;\n                        }\n\n                        AppendableValue::Css({\n                            // Safety: serialization only generates valid utf-8.\n                            #[cfg(feature = \"gecko\")]\n                            unsafe {\n                                v.as_str_unchecked()\n                            }\n                            #[cfg(feature = \"servo\")]\n                            &v\n                        })\n                    },\n                };\n\n                // 3.4.9:\n                //     Let serialized declaration be the result of invoking\n                //     serialize a CSS declaration with property name shorthand,\n                //     value value, and the important flag set if the CSS\n                //     declarations in current longhands have their important\n                //     flag set.\n                //\n                // 3.4.10:\n                //     Append serialized declaration to list.\n                append_serialization(\n                    dest,\n                    &shorthand,\n                    value,\n                    importance,\n                    &mut is_first_serialization,\n                )?;\n\n                // 3.4.11:\n                //     Append the property names of all items of current\n                //     longhands to already serialized.\n                for current_longhand in &current_longhands {\n                    let longhand_id = match current_longhand.id() {\n                        PropertyDeclarationId::Longhand(id) => id,\n                        PropertyDeclarationId::Custom(..) => unreachable!(),\n                    };\n\n                    // Substep 9\n                    already_serialized.insert(longhand_id.into());\n                }\n\n                // 3.4.12:\n                //     Continue with the steps labeled declaration loop.\n                continue 'declaration_loop;\n            }\n\n            // Steps 3.5, 3.6 & 3.7:\n            //     Let value be the result of invoking serialize a CSS value of\n            //     declaration.\n            //\n            //     Let serialized declaration be the result of invoking\n            //     serialize a CSS declaration with property name property,\n            //     value value, and the important flag set if declaration has\n            //     its important flag set.\n            //\n            //     Append serialized declaration to list.\n            append_serialization(\n                dest,\n                &property,\n                AppendableValue::Declaration(declaration),\n                importance,\n                &mut is_first_serialization,\n            )?;\n\n            // Step 3.8:\n            //     Append property to already serialized.\n            already_serialized.insert(longhand_id.into());\n        }\n\n        // Step 4\n        Ok(())\n    }\n}\n\n/// A convenient enum to represent different kinds of stuff that can represent a\n/// _value_ in the serialization of a property declaration.\npub enum AppendableValue<'a, 'b: 'a> {\n    /// A given declaration, of which we'll serialize just the value.\n    Declaration(&'a PropertyDeclaration),\n    /// A set of declarations for a given shorthand.\n    ///\n    /// FIXME: This needs more docs, where are the shorthands expanded? We print\n    /// the property name before-hand, don't we?\n    DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]),\n    /// A raw CSS string, coming for example from a property with CSS variables,\n    /// or when storing a serialized shorthand value before appending directly.\n    Css(&'a str),\n}\n\n/// Potentially appends whitespace after the first (property: value;) pair.\nfn handle_first_serialization<W>(dest: &mut W, is_first_serialization: &mut bool) -> fmt::Result\nwhere\n    W: Write,\n{\n    if !*is_first_serialization {\n        dest.write_char(' ')\n    } else {\n        *is_first_serialization = false;\n        Ok(())\n    }\n}\n\n/// Append a given kind of appendable value to a serialization.\npub fn append_declaration_value<'a, 'b: 'a>(\n    dest: &mut CssStringWriter,\n    appendable_value: AppendableValue<'a, 'b>,\n) -> fmt::Result {\n    match appendable_value {\n        AppendableValue::Css(css) => dest.write_str(css),\n        AppendableValue::Declaration(decl) => decl.to_css(dest),\n        AppendableValue::DeclarationsForShorthand(shorthand, decls) => {\n            shorthand.longhands_to_css(decls, dest)\n        },\n    }\n}\n\n/// Append a given property and value pair to a serialization.\npub fn append_serialization<'a, 'b: 'a, N>(\n    dest: &mut CssStringWriter,\n    property_name: &N,\n    appendable_value: AppendableValue<'a, 'b>,\n    importance: Importance,\n    is_first_serialization: &mut bool,\n) -> fmt::Result\nwhere\n    N: ToCss,\n{\n    handle_first_serialization(dest, is_first_serialization)?;\n\n    property_name.to_css(&mut CssWriter::new(dest))?;\n    dest.write_str(\": \")?;\n\n    append_declaration_value(dest, appendable_value)?;\n\n    if importance.important() {\n        dest.write_str(\" !important\")?;\n    }\n\n    dest.write_char(';')\n}\n\n/// A helper to parse the style attribute of an element, in order for this to be\n/// shared between Servo and Gecko.\n///\n/// Inline because we call this cross-crate.\n#[inline]\npub fn parse_style_attribute(\n    input: &str,\n    url_data: &UrlExtraData,\n    error_reporter: Option<&dyn ParseErrorReporter>,\n    quirks_mode: QuirksMode,\n    rule_type: CssRuleType,\n) -> PropertyDeclarationBlock {\n    let context = ParserContext::new(\n        Origin::Author,\n        url_data,\n        Some(rule_type),\n        ParsingMode::DEFAULT,\n        quirks_mode,\n        /* namespaces = */ Default::default(),\n        error_reporter,\n        None,\n        /* attr_taint */ Default::default(),\n    );\n\n    let mut input = ParserInput::new(input);\n    parse_property_declaration_list(&context, &mut Parser::new(&mut input), &[])\n}\n\n/// Parse a given property declaration. Can result in multiple\n/// `PropertyDeclaration`s when expanding a shorthand, for example.\n///\n/// This does not attempt to parse !important at all.\n#[inline]\npub fn parse_one_declaration_into(\n    declarations: &mut SourcePropertyDeclaration,\n    id: PropertyId,\n    input: &str,\n    origin: Origin,\n    url_data: &UrlExtraData,\n    error_reporter: Option<&dyn ParseErrorReporter>,\n    parsing_mode: ParsingMode,\n    quirks_mode: QuirksMode,\n    rule_type: CssRuleType,\n) -> Result<(), ()> {\n    let context = ParserContext::new(\n        origin,\n        url_data,\n        Some(rule_type),\n        parsing_mode,\n        quirks_mode,\n        /* namespaces = */ Default::default(),\n        error_reporter,\n        None,\n        /* attr_taint */ Default::default(),\n    );\n\n    let property_id_for_error_reporting = if context.error_reporting_enabled() {\n        Some(id.clone())\n    } else {\n        None\n    };\n\n    let mut input = ParserInput::new(input);\n    let mut parser = Parser::new(&mut input);\n    let start_position = parser.position();\n    parser\n        .parse_entirely(|parser| {\n            PropertyDeclaration::parse_into(declarations, id, &context, parser)\n        })\n        .map_err(|err| {\n            if context.error_reporting_enabled() {\n                report_one_css_error(\n                    &context,\n                    None,\n                    &[],\n                    err,\n                    parser.slice_from(start_position),\n                    property_id_for_error_reporting,\n                )\n            }\n        })\n}\n\n/// A struct to parse property declarations.\nstruct PropertyDeclarationParser<'a, 'b: 'a, 'i> {\n    context: &'a ParserContext<'b>,\n    state: &'a mut DeclarationParserState<'i>,\n}\n\n/// The state needed to parse a declaration block.\n///\n/// It stores declarations in output_block.\n#[derive(Default)]\npub struct DeclarationParserState<'i> {\n    /// The output block where results are stored.\n    output_block: PropertyDeclarationBlock,\n    /// Declarations from the last declaration parsed. (note that a shorthand might expand to\n    /// multiple declarations).\n    declarations: SourcePropertyDeclaration,\n    /// The importance from the last declaration parsed.\n    importance: Importance,\n    /// A list of errors that have happened so far. Not all of them might be reported.\n    errors: SmallParseErrorVec<'i>,\n    /// The start of the first declaration\n    first_declaration_start: SourceLocation,\n    /// The last parsed property id, if any.\n    last_parsed_property_id: Option<PropertyId>,\n}\n\nimpl<'i> DeclarationParserState<'i> {\n    /// Getter for first_declaration_start.\n    pub fn first_declaration_start(&self) -> SourceLocation {\n        self.first_declaration_start\n    }\n\n    /// Returns whether any parsed declarations have been parsed so far.\n    pub fn has_parsed_declarations(&self) -> bool {\n        !self.output_block.is_empty()\n    }\n\n    /// Takes the parsed declarations.\n    pub fn take_declarations(&mut self) -> PropertyDeclarationBlock {\n        std::mem::take(&mut self.output_block)\n    }\n\n    /// Parse a single declaration value.\n    pub fn parse_value<'t>(\n        &mut self,\n        context: &ParserContext,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        declaration_start: &ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        let id = match PropertyId::parse(&name, context) {\n            Ok(id) => id,\n            Err(..) => {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name)));\n            },\n        };\n        if context.error_reporting_enabled() {\n            self.last_parsed_property_id = Some(id.clone());\n        }\n        input.parse_until_before(Delimiter::Bang, |input| {\n            PropertyDeclaration::parse_into(&mut self.declarations, id, context, input)\n        })?;\n        self.importance = match input.try_parse(parse_important) {\n            Ok(()) => {\n                if !context.allows_important_declarations() {\n                    return Err(\n                        input.new_custom_error(StyleParseErrorKind::UnexpectedImportantDeclaration)\n                    );\n                }\n                Importance::Important\n            },\n            Err(_) => Importance::Normal,\n        };\n        // In case there is still unparsed text in the declaration, we should roll back.\n        input.expect_exhausted()?;\n        let has_parsed_declarations = self.has_parsed_declarations();\n        self.output_block\n            .extend(self.declarations.drain(), self.importance);\n        // We've successfully parsed a declaration, so forget about\n        // `last_parsed_property_id`. It'd be wrong to associate any\n        // following error with this property.\n        self.last_parsed_property_id = None;\n\n        if !has_parsed_declarations {\n            self.first_declaration_start = declaration_start.source_location();\n        }\n\n        Ok(())\n    }\n\n    /// Reports any CSS errors that have ocurred if needed.\n    #[inline]\n    pub fn report_errors_if_needed(\n        &mut self,\n        context: &ParserContext,\n        selectors: &[SelectorList<SelectorImpl>],\n    ) {\n        if self.errors.is_empty() {\n            return;\n        }\n        self.do_report_css_errors(context, selectors);\n    }\n\n    #[cold]\n    fn do_report_css_errors(\n        &mut self,\n        context: &ParserContext,\n        selectors: &[SelectorList<SelectorImpl>],\n    ) {\n        for (error, slice, property) in self.errors.drain(..) {\n            report_one_css_error(\n                context,\n                Some(&self.output_block),\n                selectors,\n                error,\n                slice,\n                property,\n            )\n        }\n    }\n\n    /// Resets the declaration parser state, and reports the error if needed.\n    #[inline]\n    pub fn did_error(&mut self, context: &ParserContext, error: ParseError<'i>, slice: &'i str) {\n        self.declarations.clear();\n        if !context.error_reporting_enabled() {\n            return;\n        }\n        let property = self.last_parsed_property_id.take();\n        self.errors.push((error, slice, property));\n    }\n}\n\n/// Default methods reject all at rules.\nimpl<'a, 'b, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {\n    type Prelude = ();\n    type AtRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\n/// Default methods reject all rules.\nimpl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {\n    type Prelude = ();\n    type QualifiedRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\n/// Based on NonMozillaVendorIdentifier from Gecko's CSS parser.\nfn is_non_mozilla_vendor_identifier(name: &str) -> bool {\n    (name.starts_with(\"-\") && !name.starts_with(\"-moz-\")) || name.starts_with(\"_\")\n}\n\nimpl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b, 'i> {\n    type Declaration = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_value<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        declaration_start: &ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        self.state\n            .parse_value(self.context, name, input, declaration_start)\n    }\n}\n\nimpl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>\n    for PropertyDeclarationParser<'a, 'b, 'i>\n{\n    fn parse_declarations(&self) -> bool {\n        true\n    }\n    // TODO(emilio): Nesting.\n    fn parse_qualified(&self) -> bool {\n        false\n    }\n}\n\ntype SmallParseErrorVec<'i> = SmallVec<[(ParseError<'i>, &'i str, Option<PropertyId>); 2]>;\n\nfn alias_of_known_property(name: &str) -> Option<PropertyId> {\n    let mut prefixed = String::with_capacity(name.len() + 5);\n    prefixed.push_str(\"-moz-\");\n    prefixed.push_str(name);\n    PropertyId::parse_enabled_for_all_content(&prefixed).ok()\n}\n\n#[cold]\nfn report_one_css_error<'i>(\n    context: &ParserContext,\n    block: Option<&PropertyDeclarationBlock>,\n    selectors: &[SelectorList<SelectorImpl>],\n    mut error: ParseError<'i>,\n    slice: &str,\n    property: Option<PropertyId>,\n) {\n    debug_assert!(context.error_reporting_enabled());\n\n    fn all_properties_in_block(block: &PropertyDeclarationBlock, property: &PropertyId) -> bool {\n        match property.as_shorthand() {\n            Ok(id) => id\n                .longhands()\n                .all(|longhand| block.contains(PropertyDeclarationId::Longhand(longhand))),\n            Err(longhand_or_custom) => block.contains(longhand_or_custom),\n        }\n    }\n\n    if let ParseErrorKind::Custom(StyleParseErrorKind::UnknownProperty(ref name)) = error.kind {\n        if is_non_mozilla_vendor_identifier(name) {\n            // If the unrecognized property looks like a vendor-specific property,\n            // silently ignore it instead of polluting the error output.\n            return;\n        }\n        if let Some(alias) = alias_of_known_property(name) {\n            // This is an unknown property, but its -moz-* version is known.\n            // We don't want to report error if the -moz-* version is already\n            // specified.\n            if let Some(block) = block {\n                if all_properties_in_block(block, &alias) {\n                    return;\n                }\n            }\n        }\n    }\n\n    if let Some(ref property) = property {\n        if let Some(block) = block {\n            if all_properties_in_block(block, property) {\n                return;\n            }\n        }\n        // Was able to parse property ID - Either an invalid value, or is constrained\n        // by the rule block it's in to be invalid. In the former case, we need to unwrap\n        // the error to be more specific.\n        if !matches!(\n            error.kind,\n            ParseErrorKind::Custom(StyleParseErrorKind::UnexpectedImportantDeclaration)\n        ) {\n            error = match *property {\n                PropertyId::Custom(ref c) => {\n                    StyleParseErrorKind::new_invalid(format!(\"--{}\", c), error)\n                },\n                _ => StyleParseErrorKind::new_invalid(\n                    property.non_custom_id().unwrap().name(),\n                    error,\n                ),\n            };\n        }\n    }\n\n    let location = error.location;\n    let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, selectors);\n    context.log_css_error(location, error);\n}\n\n/// Parse a list of property declarations and return a property declaration\n/// block.\npub fn parse_property_declaration_list(\n    context: &ParserContext,\n    input: &mut Parser,\n    selectors: &[SelectorList<SelectorImpl>],\n) -> PropertyDeclarationBlock {\n    let mut state = DeclarationParserState::default();\n    let mut parser = PropertyDeclarationParser {\n        context,\n        state: &mut state,\n    };\n    let mut iter = RuleBodyParser::new(input, &mut parser);\n    while let Some(declaration) = iter.next() {\n        match declaration {\n            Ok(()) => {},\n            Err((error, slice)) => iter.parser.state.did_error(context, error, slice),\n        }\n    }\n    parser.state.report_errors_if_needed(context, selectors);\n    state.output_block\n}\n"
  },
  {
    "path": "style/properties/font_face_descriptors.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# The descriptors of the @font-family rule.\n\n[font-family]\ntype = \"FamilyName\"\n\n[src]\ntype = \"SourceList\"\n\n[font-style]\ntype = \"FontStyle\"\n\n[font-weight]\ntype = \"FontWeightRange\"\n\n[font-stretch]\ntype = \"FontStretchRange\"\n\n[font-display]\ntype = \"FontDisplay\"\n\n[unicode-range]\ntype = \"Vec<cssparser::UnicodeRange>\"\n\n[font-feature-settings]\ntype = \"FontFeatureSettings\"\n\n[font-variation-settings]\ntype = \"FontVariationSettings\"\ngecko_pref = \"layout.css.font-variations.enabled\"\n\n[font-language-override]\ntype = \"FontLanguageOverride\"\n\n[ascent-override]\ntype = \"MetricsOverride\"\n\n[descent-override]\ntype = \"MetricsOverride\"\n\n[line-gap-override]\ntype = \"MetricsOverride\"\n\n[size-adjust]\ntype = \"specified::NonNegativePercentage\"\n"
  },
  {
    "path": "style/properties/gecko.mako.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n// `data` comes from components/style/properties.mako.rs; see build.rs for more details.\n\n<%! from data import to_camel_case, to_camel_case_lower, Keyword, SYSTEM_FONT_LONGHANDS %>\n<%namespace name=\"helpers\" file=\"/helpers.mako.rs\" />\n\nuse crate::Atom;\nuse crate::logical_geometry::PhysicalSide;\nuse crate::computed_value_flags::*;\nuse crate::custom_properties::ComputedCustomProperties;\nuse crate::device::Device;\nuse crate::gecko_bindings::bindings;\n% for style_struct in data.style_structs:\nuse crate::gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name};\nuse crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};\nuse crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};\n% endfor\nuse crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength;\nuse crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang;\nuse crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom;\nuse crate::gecko_bindings::structs::{self, PseudoStyleType};\nuse crate::gecko::data::PerDocumentStyleData;\nuse crate::logical_geometry::WritingMode;\nuse crate::properties::longhands;\nuse crate::rule_tree::StrongRuleNode;\nuse crate::selector_parser::PseudoElement;\nuse servo_arc::{Arc, UniqueArc};\nuse std::mem::{forget, MaybeUninit, ManuallyDrop};\nuse std::{ops, ptr};\nuse crate::values;\nuse crate::values::computed::{Time, Zoom};\nuse crate::values::computed::font::FontSize;\nuse crate::dom::AttributeReferences;\n\n\npub mod style_structs {\n    % for style_struct in data.style_structs:\n    pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};\n\n    unsafe impl Send for ${style_struct.name} {}\n    unsafe impl Sync for ${style_struct.name} {}\n    % endfor\n}\n\n/// FIXME(emilio): This is completely duplicated with the other properties code.\npub type ComputedValuesInner = structs::ServoComputedData;\n\n#[repr(C)]\npub struct ComputedValues(structs::mozilla::ComputedStyle);\n\nimpl ComputedValues {\n    #[inline]\n    pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle {\n        &self.0\n    }\n\n    pub fn new(\n        pseudo: Option<&PseudoElement>,\n        custom_properties: ComputedCustomProperties,\n        attributes_referenced: AttributeReferences,\n        writing_mode: WritingMode,\n        effective_zoom: Zoom,\n        flags: ComputedValueFlags,\n        rules: Option<StrongRuleNode>,\n        visited_style: Option<Arc<ComputedValues>>,\n        % for style_struct in data.style_structs:\n        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,\n        % endfor\n    ) -> Arc<Self> {\n        ComputedValuesInner::new(\n            pseudo,\n            custom_properties,\n            attributes_referenced,\n            writing_mode,\n            effective_zoom,\n            flags,\n            rules,\n            visited_style,\n            % for style_struct in data.style_structs:\n            ${style_struct.ident},\n            % endfor\n        ).to_outer()\n    }\n\n    pub fn default_values(doc: &structs::Document) -> Arc<Self> {\n        ComputedValuesInner::new(\n            /* pseudo = */ None,\n            ComputedCustomProperties::default(),\n            AttributeReferences::default(),\n            WritingMode::empty(), // FIXME(bz): This seems dubious\n            Zoom::ONE,\n            ComputedValueFlags::empty(),\n            /* rules = */ None,\n            /* visited_style = */ None,\n            % for style_struct in data.style_structs:\n            style_structs::${style_struct.name}::default(doc),\n            % endfor\n        ).to_outer()\n    }\n\n    /// Converts the computed values to an Arc<> from a reference.\n    pub fn to_arc(&self) -> Arc<Self> {\n        // SAFETY: We're guaranteed to be allocated as an Arc<> since the\n        // functions above are the only ones that create ComputedValues\n        // instances in Gecko (and that must be the case since ComputedValues'\n        // member is private).\n        unsafe { Arc::from_raw_addrefed(self) }\n    }\n\n    #[inline]\n    pub fn is_pseudo_style(&self) -> bool {\n        self.pseudo_type != PseudoStyleType::NotPseudo\n    }\n\n    #[inline]\n    pub fn pseudo(&self) -> Option<PseudoElement> {\n        if !self.is_pseudo_style() {\n            return None;\n        }\n        PseudoElement::from_pseudo_type(self.pseudo_type, None)\n    }\n\n    #[inline]\n    pub fn is_first_line_style(&self) -> bool {\n        self.pseudo() == Some(PseudoElement::FirstLine)\n    }\n\n    /// Returns true if the display property is changed from 'none' to others.\n    pub fn is_display_property_changed_from_none(\n        &self,\n        old_values: Option<&ComputedValues>\n    ) -> bool {\n        use crate::properties::longhands::display::computed_value::T as Display;\n\n        old_values.map_or(false, |old| {\n            let old_display_style = old.get_box().clone_display();\n            let new_display_style = self.get_box().clone_display();\n            old_display_style == Display::None &&\n            new_display_style != Display::None\n        })\n    }\n\n    /// Calls the given function for each cached lazy pseudo-element style.\n    pub fn each_cached_lazy_pseudo<F>(&self, mut f: F)\n    where\n        F: FnMut(&Self),\n    {\n        thin_vec::auto_thin_vec!(let array: [*const structs::ComputedStyle; 4]);\n        unsafe {\n            bindings::Gecko_GetCachedLazyPseudoStyles(\n                self.as_gecko_computed_style(),\n                array.as_mut().as_mut_ptr(),\n            );\n        }\n        for style in array.iter() {\n            // ComputedValues is a newtype around ComputedStyle, so same layout\n            let values: &ComputedValues = unsafe { &*(*style as *const ComputedValues) };\n            f(values);\n        }\n    }\n\n}\n\nimpl Drop for ComputedValues {\n    fn drop(&mut self) {\n        // XXX this still relies on the destructor of ComputedValuesInner to run on the rust side,\n        // that's pretty wild.\n        unsafe {\n            bindings::Gecko_ComputedStyle_Destroy(&mut self.0);\n        }\n    }\n}\n\nunsafe impl Sync for ComputedValues {}\nunsafe impl Send for ComputedValues {}\n\nimpl Drop for ComputedValuesInner {\n    fn drop(&mut self) {\n        % for style_struct in data.style_structs:\n        let _ = unsafe { Arc::from_raw(self.${style_struct.name_lower}_ptr()) };\n        % endfor\n        if !self.visited_style.is_null() {\n            let _ = unsafe { Arc::from_raw(self.visited_style_ptr()) };\n        }\n    }\n}\n\nimpl ComputedValuesInner {\n    pub fn new(\n        pseudo: Option<&PseudoElement>,\n        custom_properties: ComputedCustomProperties,\n        attribute_references: AttributeReferences,\n        writing_mode: WritingMode,\n        effective_zoom: Zoom,\n        flags: ComputedValueFlags,\n        rules: Option<StrongRuleNode>,\n        visited_style: Option<Arc<ComputedValues>>,\n        % for style_struct in data.style_structs:\n        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,\n        % endfor\n    ) -> Self {\n        let pseudo_type = match pseudo {\n            Some(p) => p.pseudo_type(),\n            None => PseudoStyleType::NotPseudo,\n        };\n        Self {\n            custom_properties,\n            attribute_references,\n            writing_mode,\n            rules,\n            visited_style: visited_style.map_or(ptr::null(), Arc::into_raw) as *const _,\n            flags,\n            pseudo_type,\n            effective_zoom,\n            % for style_struct in data.style_structs:\n            ${style_struct.gecko_name}: Arc::into_raw(${style_struct.ident}) as *const _,\n            % endfor\n        }\n    }\n\n    // Share ComputedValues but with different flags.\n    pub fn clone_with_flags(&self, flags: ComputedValueFlags, pseudo: Option<&PseudoElement>) -> Arc<ComputedValues> {\n        Self::new(\n            pseudo,\n            self.custom_properties.clone(),\n            self.attribute_references.clone(),\n            self.writing_mode.clone(),\n            self.effective_zoom.clone(),\n            flags,\n            self.rules.clone(),\n            if self.visited_style.is_null() {\n                None\n            } else {\n                Some(unsafe { Arc::from_raw_addrefed(self.visited_style as *const _) })\n            },\n            % for style_struct in data.style_structs:\n            unsafe { Arc::from_raw_addrefed(self.${style_struct.gecko_name} as *const _) },\n            % endfor\n        ).to_outer()\n    }\n\n    fn to_outer(self) -> Arc<ComputedValues> {\n        unsafe {\n            let mut arc = UniqueArc::<ComputedValues>::new_uninit();\n            bindings::Gecko_ComputedStyle_Init(\n                arc.as_mut_ptr() as *mut _,\n                &self\n            );\n            // We're simulating move semantics by having C++ do a memcpy and\n            // then forgetting it on this end.\n            forget(self);\n            UniqueArc::assume_init(arc).shareable()\n        }\n    }\n}\n\nimpl ops::Deref for ComputedValues {\n    type Target = ComputedValuesInner;\n    #[inline]\n    fn deref(&self) -> &ComputedValuesInner {\n        &self.0.mSource\n    }\n}\n\nimpl ops::DerefMut for ComputedValues {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut ComputedValuesInner {\n        &mut self.0.mSource\n    }\n}\n\nimpl ComputedValuesInner {\n    /// Returns true if the value of the `content` property would make a\n    /// pseudo-element not rendered.\n    #[inline]\n    pub fn ineffective_content_property(&self) -> bool {\n        self.get_counters().ineffective_content_property()\n    }\n\n    #[inline]\n    fn visited_style_ptr(&self) -> *const ComputedValues {\n        self.visited_style as *const _\n    }\n\n    /// Returns the visited style, if any.\n    pub fn visited_style(&self) -> Option<&ComputedValues> {\n        unsafe { self.visited_style_ptr().as_ref() }\n    }\n\n    % for style_struct in data.style_structs:\n    #[inline]\n    fn ${style_struct.name_lower}_ptr(&self) -> *const style_structs::${style_struct.name} {\n        // This is sound because the wrapper we create is repr(transparent).\n        self.${style_struct.gecko_name} as *const _\n    }\n\n    #[inline]\n    pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {\n        unsafe { &*self.${style_struct.name_lower}_ptr() }\n    }\n    % endfor\n}\n\n<%def name=\"impl_simple_setter(ident, gecko_ffi_name)\">\n    #[allow(non_snake_case)]\n    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {\n        ${set_gecko_property(gecko_ffi_name, \"From::from(v)\")}\n    }\n</%def>\n\n<%def name=\"impl_simple_eq(ident, gecko_ffi_name)\">\n    #[allow(non_snake_case)]\n    pub fn ${ident}_equals(&self, other: &Self) -> bool {\n        self.${gecko_ffi_name} == other.${gecko_ffi_name}\n    }\n</%def>\n\n<%def name=\"impl_fallback_eq(ident)\">\n    #[allow(non_snake_case)]\n    pub fn ${ident}_equals(&self, other: &Self) -> bool {\n        // TODO: Could be more efficient\n        self.clone_${ident}() == other.clone_${ident}()\n    }\n</%def>\n\n<%def name=\"impl_simple_clone(ident, gecko_ffi_name)\">\n    #[allow(non_snake_case)]\n    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {\n        From::from(self.${gecko_ffi_name}.clone())\n    }\n</%def>\n\n<%def name=\"impl_physical_sides(ident, props)\">\n    pub fn get_${ident}(&self, s: PhysicalSide) -> &longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T {\n        match s {\n            PhysicalSide::Top => &self.${data.longhands_by_name[props[0]].gecko_ffi_name},\n            PhysicalSide::Right => &self.${data.longhands_by_name[props[1]].gecko_ffi_name},\n            PhysicalSide::Bottom => &self.${data.longhands_by_name[props[2]].gecko_ffi_name},\n            PhysicalSide::Left => &self.${data.longhands_by_name[props[3]].gecko_ffi_name},\n        }\n    }\n    pub fn set_${ident}(&mut self, s: PhysicalSide, v: longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T) {\n        match s {\n            PhysicalSide::Top => self.set_${data.longhands_by_name[props[0]].ident}(v),\n            PhysicalSide::Right => self.set_${data.longhands_by_name[props[1]].ident}(v),\n            PhysicalSide::Bottom => self.set_${data.longhands_by_name[props[2]].ident}(v),\n            PhysicalSide::Left => self.set_${data.longhands_by_name[props[3]].ident}(v),\n        }\n    }\n</%def>\n\n<%def name=\"impl_simple_copy(ident, gecko_ffi_name, *kwargs)\">\n    #[allow(non_snake_case)]\n    pub fn copy_${ident}_from(&mut self, other: &Self) {\n        self.${gecko_ffi_name} = other.${gecko_ffi_name}.clone();\n    }\n\n    #[allow(non_snake_case)]\n    pub fn reset_${ident}(&mut self, other: &Self) {\n        self.copy_${ident}_from(other)\n    }\n</%def>\n\n<%!\ndef get_gecko_property(ffi_name, self_param = \"self\"):\n    return \"%s.%s\" % (self_param, ffi_name)\n\ndef set_gecko_property(ffi_name, expr):\n    return \"self.%s = %s;\" % (ffi_name, expr)\n%>\n\n<%def name=\"impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8')\">\n    #[allow(non_snake_case)]\n    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {\n        use crate::properties::longhands::${ident}::computed_value::T as Keyword;\n        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts\n        let result = match v {\n            % for value in keyword.values_for('gecko'):\n                Keyword::${to_camel_case(value)} =>\n                    structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)},\n            % endfor\n        };\n        ${set_gecko_property(gecko_ffi_name, \"result\")}\n    }\n</%def>\n\n<%def name=\"impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')\">\n    #[allow(non_snake_case)]\n    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {\n        use crate::properties::longhands::${ident}::computed_value::T as Keyword;\n        // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts\n\n        // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch).\n        // And they are convert to signed integer in Rust bindings. We need to cast then\n        // as signed type when we have both signed/unsigned integer in order to use them\n        // as match's arms.\n        // Also, to use same implementation here we use casted constant if we have only singed values.\n        % if keyword.gecko_enum_prefix is None:\n        % for value in keyword.values_for('gecko'):\n        const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} =\n            structs::${keyword.gecko_constant(value)} as ${cast_type};\n        % endfor\n\n        match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} {\n            % for value in keyword.values_for('gecko'):\n            ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_camel_case(value)},\n            % endfor\n            % if keyword.gecko_inexhaustive:\n            _ => panic!(\"Found unexpected value in style struct for ${ident} property\"),\n            % endif\n        }\n        % else:\n        match ${get_gecko_property(gecko_ffi_name)} {\n            % for value in keyword.values_for('gecko'):\n            structs::${keyword.gecko_constant(value)} => Keyword::${to_camel_case(value)},\n            % endfor\n            % if keyword.gecko_inexhaustive:\n            _ => panic!(\"Found unexpected value in style struct for ${ident} property\"),\n            % endif\n        }\n        % endif\n    }\n</%def>\n\n<%def name=\"impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)\">\n<%call expr=\"impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)\"></%call>\n<%call expr=\"impl_simple_copy(ident, gecko_ffi_name, **kwargs)\"></%call>\n<%call expr=\"impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)\"></%call>\n<%call expr=\"impl_simple_eq(ident, gecko_ffi_name)\"></%call>\n</%def>\n\n<%def name=\"impl_simple(ident, gecko_ffi_name)\">\n<%call expr=\"impl_simple_setter(ident, gecko_ffi_name)\"></%call>\n<%call expr=\"impl_simple_copy(ident, gecko_ffi_name)\"></%call>\n<%call expr=\"impl_simple_clone(ident, gecko_ffi_name)\"></%call>\n<%call expr=\"impl_simple_eq(ident, gecko_ffi_name)\"></%call>\n</%def>\n\n<%def name=\"impl_border_width(ident, gecko_ffi_name, inherit_from)\">\n    #[allow(non_snake_case)]\n    pub fn set_${ident}(&mut self, v: Au) {\n        let value = v.0;\n        self.${inherit_from} = value;\n        self.${gecko_ffi_name} = value;\n    }\n\n    #[allow(non_snake_case)]\n    pub fn copy_${ident}_from(&mut self, other: &Self) {\n        self.${inherit_from} = other.${inherit_from};\n        // NOTE: This is needed to easily handle the `unset` and `initial`\n        // keywords, which are implemented calling this function.\n        //\n        // In practice, this means that we may have an incorrect value here, but\n        // we'll adjust that properly in the style fixup phase.\n        //\n        // FIXME(emilio): We could clean this up a bit special-casing the reset_\n        // function below.\n        self.${gecko_ffi_name} = other.${inherit_from};\n    }\n\n    #[allow(non_snake_case)]\n    pub fn reset_${ident}(&mut self, other: &Self) {\n        self.copy_${ident}_from(other)\n    }\n\n    #[allow(non_snake_case)]\n    pub fn clone_${ident}(&self) -> Au {\n        Au(self.${gecko_ffi_name})\n    }\n\n    ${impl_simple_eq(ident, gecko_ffi_name)}\n</%def>\n\n<%def name=\"impl_style_struct(style_struct)\">\n/// A wrapper for ${style_struct.gecko_ffi_name}, to be able to manually construct / destruct /\n/// clone it.\n#[repr(transparent)]\npub struct ${style_struct.gecko_struct_name}(ManuallyDrop<structs::${style_struct.gecko_ffi_name}>);\n\nimpl ops::Deref for ${style_struct.gecko_struct_name} {\n    type Target = structs::${style_struct.gecko_ffi_name};\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl ops::DerefMut for ${style_struct.gecko_struct_name} {\n    #[inline]\n    fn deref_mut(&mut self) -> &mut <Self as ops::Deref>::Target {\n        &mut self.0\n    }\n}\n\nimpl ${style_struct.gecko_struct_name} {\n    #[allow(dead_code, unused_variables)]\n    pub fn default(document: &structs::Document) -> Arc<Self> {\n% if style_struct.document_dependent:\n        unsafe {\n            let mut result = UniqueArc::<Self>::new_uninit();\n            Gecko_Construct_Default_${style_struct.gecko_ffi_name}(\n                result.as_mut_ptr() as *mut _,\n                document,\n            );\n            UniqueArc::assume_init(result).shareable()\n        }\n% else:\n        static DEFAULT: std::sync::LazyLock<Arc<${style_struct.gecko_struct_name}>> = std::sync::LazyLock::new(|| unsafe {\n            let mut result = UniqueArc::<${style_struct.gecko_struct_name}>::new_uninit();\n            Gecko_Construct_Default_${style_struct.gecko_ffi_name}(\n                result.as_mut_ptr() as *mut _,\n                std::ptr::null(),\n            );\n            let arc = UniqueArc::assume_init(result).shareable();\n            arc.mark_as_intentionally_leaked();\n            arc\n        });\n        DEFAULT.clone()\n% endif\n    }\n}\n\nimpl Drop for ${style_struct.gecko_struct_name} {\n    fn drop(&mut self) {\n        unsafe {\n            Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut **self);\n        }\n    }\n}\nimpl Clone for ${style_struct.gecko_struct_name} {\n    fn clone(&self) -> Self {\n        unsafe {\n            let mut result = MaybeUninit::<Self>::uninit();\n            // FIXME(bug 1595895): Zero the memory to keep valgrind happy, but\n            // these looks like Valgrind false-positives at a quick glance.\n            ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);\n            Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &**self);\n            result.assume_init()\n        }\n    }\n}\n</%def>\n\n<%def name=\"impl_font_settings(ident, gecko_type, tag_type, value_type, gecko_value_type)\">\n    <% gecko_ffi_name = to_camel_case_lower(ident) %>\n\n    pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {\n        let iter = v.0.iter().map(|other| structs::${gecko_type} {\n            mTag: other.tag.0,\n            mValue: other.value as ${gecko_value_type},\n        });\n        self.mFont.${gecko_ffi_name}.clear();\n        self.mFont.${gecko_ffi_name}.extend(iter);\n    }\n\n    ${impl_simple_copy(ident, \"mFont.\" + gecko_ffi_name)}\n    ${impl_fallback_eq(ident)}\n\n    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {\n        use crate::values::generics::font::{FontSettings, FontTag, ${tag_type}};\n\n        FontSettings(\n            self.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| {\n                ${tag_type} {\n                    tag: FontTag(gecko_font_setting.mTag),\n                    value: gecko_font_setting.mValue as ${value_type},\n                }\n            }).collect()\n        )\n    }\n</%def>\n\n<%def name=\"impl_trait(style_struct_name, skip_longhands='')\">\n<%\n    style_struct = next(x for x in data.style_structs if x.name == style_struct_name)\n    longhands = [x for x in style_struct.longhands\n                if not (skip_longhands == \"*\" or x.name in skip_longhands.split())]\n\n    def longhand_method(longhand):\n        args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)\n\n        if longhand.logical:\n            return\n        # get the method and pass additional keyword or type-specific arguments\n        if longhand.keyword:\n            method = impl_keyword\n            args.update(keyword=longhand.keyword)\n            if \"font\" in longhand.ident:\n                args.update(cast_type=longhand.cast_type)\n        else:\n            method = impl_simple\n\n        method(**args)\n%>\nimpl ${style_struct.gecko_struct_name} {\n    /*\n     * Manually-Implemented Methods.\n     */\n    ${caller.body().strip()}\n\n    /*\n     * Auto-Generated Methods.\n     */\n    <%\n    for longhand in longhands:\n        longhand_method(longhand)\n    %>\n}\n</%def>\n\n<%!\nclass Side(object):\n    def __init__(self, name, index):\n        self.name = name\n        self.ident = name.lower()\n        self.index = index\n\nSIDES = [Side(\"Top\", 0), Side(\"Right\", 1), Side(\"Bottom\", 2), Side(\"Left\", 3)]\n%>\n\n#[allow(dead_code)]\nfn static_assert() {\n    // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.\n    % for side in SIDES:\n    { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }\n    % endfor\n}\n\n\n<%self:impl_trait style_struct_name=\"Border\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Margin\">\n    ${impl_physical_sides(\"margin\", [\"margin-top\", \"margin-right\", \"margin-bottom\", \"margin-left\"])}\n</%self:impl_trait>\n<%self:impl_trait style_struct_name=\"Padding\"></%self:impl_trait>\n<%self:impl_trait style_struct_name=\"Page\"></%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Position\">\n    ${impl_physical_sides(\"inset\", [\"top\", \"right\", \"bottom\", \"left\"])}\n    pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {\n        debug_assert_ne!(v, values::specified::JustifyItems::legacy());\n        self.mJustifyItems.computed = v;\n    }\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Outline\">\n</%self:impl_trait>\n\n<% skip_font_longhands = \"\"\"font-size -x-lang font-feature-settings font-variation-settings\"\"\" %>\n<%self:impl_trait style_struct_name=\"Font\"\n    skip_longhands=\"${skip_font_longhands}\">\n\n    // Negative numbers are invalid at parse time, but <integer> is still an\n    // i32.\n    <% impl_font_settings(\"font_feature_settings\", \"gfxFontFeature\", \"FeatureTagValue\", \"i32\", \"u32\") %>\n    <% impl_font_settings(\"font_variation_settings\", \"gfxFontVariation\", \"VariationValue\", \"f32\", \"f32\") %>\n\n    pub fn unzoom_fonts(&mut self, device: &Device) {\n        use crate::values::generics::NonNegative;\n        self.mSize = NonNegative(device.unzoom_text(self.mSize.0));\n        self.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.mScriptUnconstrainedSize.0));\n        self.mFont.size = NonNegative(device.unzoom_text(self.mFont.size.0));\n    }\n\n    pub fn copy_font_size_from(&mut self, other: &Self) {\n        self.mScriptUnconstrainedSize = other.mScriptUnconstrainedSize;\n\n        self.mSize = other.mScriptUnconstrainedSize;\n        // NOTE: Intentionally not copying from mFont.size. The cascade process\n        // recomputes the used size as needed.\n        self.mFont.size = other.mSize;\n        self.mFontSizeKeyword = other.mFontSizeKeyword;\n\n        // TODO(emilio): Should we really copy over these two?\n        self.mFontSizeFactor = other.mFontSizeFactor;\n        self.mFontSizeOffset = other.mFontSizeOffset;\n    }\n\n    pub fn reset_font_size(&mut self, other: &Self) {\n        self.copy_font_size_from(other)\n    }\n\n    pub fn font_size_equals(&self, other: &Self) -> bool {\n        self.mSize == other.mSize\n    }\n\n    pub fn set_font_size(&mut self, v: FontSize) {\n        let computed_size = v.computed_size;\n        self.mScriptUnconstrainedSize = computed_size;\n\n        // These two may be changed from Cascade::fixup_font_stuff.\n        self.mSize = computed_size;\n        // NOTE: Intentionally not copying from used_size. The cascade process\n        // recomputes the used size as needed.\n        self.mFont.size = computed_size;\n\n        self.mFontSizeKeyword = v.keyword_info.kw;\n        self.mFontSizeFactor = v.keyword_info.factor;\n        self.mFontSizeOffset = v.keyword_info.offset;\n    }\n\n    pub fn clone_font_size(&self) -> FontSize {\n        use crate::values::specified::font::KeywordInfo;\n\n        FontSize {\n            computed_size: self.mSize,\n            used_size: self.mFont.size,\n            keyword_info: KeywordInfo {\n                kw: self.mFontSizeKeyword,\n                factor: self.mFontSizeFactor,\n                offset: self.mFontSizeOffset,\n            }\n        }\n    }\n\n    #[allow(non_snake_case)]\n    pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) {\n        let ptr = v.0.as_ptr();\n        forget(v);\n        unsafe {\n            Gecko_nsStyleFont_SetLang(&mut **self, ptr);\n        }\n    }\n\n    #[allow(non_snake_case)]\n    pub fn copy__x_lang_from(&mut self, other: &Self) {\n        unsafe {\n            Gecko_nsStyleFont_CopyLangFrom(&mut **self, &**other);\n        }\n    }\n\n    #[allow(non_snake_case)]\n    pub fn reset__x_lang(&mut self, other: &Self) {\n        self.copy__x_lang_from(other)\n    }\n\n    #[allow(non_snake_case)]\n    pub fn clone__x_lang(&self) -> longhands::_x_lang::computed_value::T {\n        longhands::_x_lang::computed_value::T(unsafe {\n            Atom::from_raw(self.mLanguage.mRawPtr)\n        })\n    }\n\n    #[allow(non_snake_case)]\n    pub fn _x_lang_equals(&self, other: &Self) -> bool {\n        self.mLanguage.mRawPtr == other.mLanguage.mRawPtr\n    }\n</%self:impl_trait>\n\n\n<%def name=\"impl_coordinated_property(type, ident, gecko_ffi_name)\">\n    #[allow(non_snake_case)]\n    pub fn set_${type}_${ident}<I>(&mut self, v: I)\n    where\n        I: IntoIterator<Item = longhands::${type}_${ident}::computed_value::single_value::T>,\n        I::IntoIter: ExactSizeIterator + Clone\n    {\n        let v = v.into_iter();\n        debug_assert_ne!(v.len(), 0);\n        let input_len = v.len();\n        self.m${to_camel_case(type)}s.ensure_len(input_len);\n\n        self.m${to_camel_case(type)}${gecko_ffi_name}Count = input_len as u32;\n        for (gecko, servo) in self.m${to_camel_case(type)}s.iter_mut().take(input_len as usize).zip(v) {\n            gecko.m${gecko_ffi_name} = servo;\n        }\n    }\n    #[allow(non_snake_case)]\n    pub fn ${type}_${ident}_at(&self, index: usize)\n        -> longhands::${type}_${ident}::computed_value::SingleComputedValue {\n        self.m${to_camel_case(type)}s[index % self.${type}_${ident}_count()].m${gecko_ffi_name}.clone()\n    }\n    #[allow(non_snake_case)]\n    pub fn copy_${type}_${ident}_from(&mut self, other: &Self) {\n        self.m${to_camel_case(type)}s.ensure_len(other.m${to_camel_case(type)}s.len());\n\n        let count = other.m${to_camel_case(type)}${gecko_ffi_name}Count;\n        self.m${to_camel_case(type)}${gecko_ffi_name}Count = count;\n\n        let iter = self.m${to_camel_case(type)}s.iter_mut().take(count as usize).zip(\n            other.m${to_camel_case(type)}s.iter()\n        );\n\n        for (ours, others) in iter {\n            ours.m${gecko_ffi_name} = others.m${gecko_ffi_name}.clone();\n        }\n    }\n    #[allow(non_snake_case)]\n    pub fn reset_${type}_${ident}(&mut self, other: &Self) {\n        self.copy_${type}_${ident}_from(other)\n    }\n    #[allow(non_snake_case)]\n    pub fn ${type}_${ident}_count(&self) -> usize {\n        self.m${to_camel_case(type)}${gecko_ffi_name}Count as usize\n    }\n    #[allow(non_snake_case)]\n    pub fn ${type}_${ident}_equals(&self, other: &Self) -> bool {\n        let count = self.${type}_${ident}_count();\n        if count != other.${type}_${ident}_count() {\n            return false;\n        }\n        let iter = self.m${to_camel_case(type)}s.iter().take(count as usize).zip(\n            other.m${to_camel_case(type)}s.iter()\n        );\n        for (ours, others) in iter {\n            if ours.m${gecko_ffi_name} != others.m${gecko_ffi_name} {\n                return false;\n            }\n        }\n        true\n    }\n</%def>\n\n<% skip_box_longhands= \"\"\"display contain\"\"\" %>\n<%self:impl_trait style_struct_name=\"Box\" skip_longhands=\"${skip_box_longhands}\">\n    #[inline]\n    pub fn set_display(&mut self, v: longhands::display::computed_value::T) {\n        self.mDisplay = v;\n        self.mOriginalDisplay = v;\n    }\n\n    #[inline]\n    pub fn copy_display_from(&mut self, other: &Self) {\n        self.set_display(other.mDisplay);\n    }\n\n    #[inline]\n    pub fn reset_display(&mut self, other: &Self) {\n        self.copy_display_from(other)\n    }\n\n    #[inline]\n    pub fn set_adjusted_display(\n        &mut self,\n        v: longhands::display::computed_value::T,\n        _is_item_or_root: bool\n    ) {\n        self.mDisplay = v;\n    }\n\n    #[inline]\n    pub fn clone_display(&self) -> longhands::display::computed_value::T {\n        self.mDisplay\n    }\n\n    #[inline]\n    pub fn display_equals(&self, other: &Self) -> bool {\n        self.mDisplay == other.mDisplay\n    }\n\n    #[inline]\n    pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) {\n        self.mContain = v;\n        self.mEffectiveContainment = v;\n    }\n\n    #[inline]\n    pub fn copy_contain_from(&mut self, other: &Self) {\n        self.set_contain(other.mContain);\n    }\n\n    #[inline]\n    pub fn reset_contain(&mut self, other: &Self) {\n        self.copy_contain_from(other)\n    }\n\n    #[inline]\n    pub fn clone_contain(&self) -> longhands::contain::computed_value::T {\n        self.mContain\n    }\n\n    #[inline]\n    pub fn contain_equals(&self, other: &Self) -> bool {\n        self.mContain == other.mContain\n    }\n\n    #[inline]\n    pub fn set_effective_containment(\n        &mut self,\n        v: longhands::contain::computed_value::T\n    ) {\n        self.mEffectiveContainment = v;\n    }\n\n    #[inline]\n    pub fn clone_effective_containment(&self) -> longhands::contain::computed_value::T {\n        self.mEffectiveContainment\n    }\n</%self:impl_trait>\n\n<%def name=\"simple_image_array_property(name, shorthand, field_name)\">\n    <%\n        image_layers_field = \"mImage\" if shorthand == \"background\" else \"mMask\"\n        copy_simple_image_array_property(name, shorthand, image_layers_field, field_name)\n    %>\n\n    pub fn set_${shorthand}_${name}<I>(&mut self, v: I)\n    where I: IntoIterator<Item=longhands::${shorthand}_${name}::computed_value::single_value::T>,\n          I::IntoIter: ExactSizeIterator\n    {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n        let v = v.into_iter();\n\n        unsafe {\n          Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),\n                                        LayerType::${shorthand.title()});\n        }\n\n        self.${image_layers_field}.${field_name}Count = v.len() as u32;\n        for (servo, geckolayer) in v.zip(self.${image_layers_field}.mLayers.iter_mut()) {\n            geckolayer.${field_name} = {\n                ${caller.body()}\n            };\n        }\n    }\n    ${impl_fallback_eq(f\"{shorthand}_{name}\")}\n</%def>\n\n<%def name=\"copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)\">\n    pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n\n        let count = other.${layers_field_name}.${field_name}Count;\n        unsafe {\n            Gecko_EnsureImageLayersLength(&mut self.${layers_field_name},\n                                          count as usize,\n                                          LayerType::${shorthand.title()});\n        }\n        // FIXME(emilio): This may be bogus in the same way as bug 1426246.\n        for (layer, other) in self.${layers_field_name}.mLayers.iter_mut()\n                                  .zip(other.${layers_field_name}.mLayers.iter())\n                                  .take(count as usize) {\n            layer.${field_name} = other.${field_name}.clone();\n        }\n        self.${layers_field_name}.${field_name}Count = count;\n    }\n\n    pub fn reset_${shorthand}_${name}(&mut self, other: &Self) {\n        self.copy_${shorthand}_${name}_from(other)\n    }\n</%def>\n\n<%def name=\"impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)\">\n    <%\n        ident = \"%s_%s\" % (shorthand, name)\n        style_struct = next(x for x in data.style_structs if x.name == struct_name)\n        longhand = next(x for x in style_struct.longhands if x.ident == ident)\n        keyword = longhand.keyword\n    %>\n\n    <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %>\n\n    pub fn set_${ident}<I>(&mut self, v: I)\n    where\n        I: IntoIterator<Item=longhands::${ident}::computed_value::single_value::T>,\n        I::IntoIter: ExactSizeIterator,\n    {\n        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n\n        let v = v.into_iter();\n\n        unsafe {\n          Gecko_EnsureImageLayersLength(&mut self.${layer_field_name}, v.len(),\n                                        LayerType::${shorthand.title()});\n        }\n\n        self.${layer_field_name}.${field_name}Count = v.len() as u32;\n        for (servo, geckolayer) in v.zip(self.${layer_field_name}.mLayers.iter_mut()) {\n            geckolayer.${field_name} = {\n                match servo {\n                    % for value in keyword.values_for(\"gecko\"):\n                    Keyword::${to_camel_case(value)} =>\n                        structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')},\n                    % endfor\n                }\n            };\n        }\n    }\n\n    ${impl_fallback_eq(ident)}\n\n    pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {\n        use crate::properties::longhands::${ident}::single_value::computed_value::T as Keyword;\n        longhands::${ident}::computed_value::List(\n            self.${layer_field_name}.mLayers.iter()\n                .take(self.${layer_field_name}.${field_name}Count as usize)\n                .map(|ref layer| {\n                    match layer.${field_name} {\n                        % for value in longhand.keyword.values_for(\"gecko\"):\n                        structs::${keyword.gecko_constant(value)}\n                            => Keyword::${to_camel_case(value)},\n                        % endfor\n                        % if keyword.gecko_inexhaustive:\n                        _ => panic!(\"Found unexpected value in style struct for ${ident} property\"),\n                        % endif\n                    }\n                }).collect()\n        )\n    }\n</%def>\n\n<%def name=\"impl_common_image_layer_properties(shorthand)\">\n    <%\n        if shorthand == \"background\":\n            image_layers_field = \"mImage\"\n            struct_name = \"Background\"\n        else:\n            image_layers_field = \"mMask\"\n            struct_name = \"SVG\"\n    %>\n\n    <%self:simple_image_array_property name=\"repeat\" shorthand=\"${shorthand}\" field_name=\"mRepeat\">\n        use crate::values::specified::background::BackgroundRepeatKeyword;\n        use crate::gecko_bindings::structs::nsStyleImageLayers_Repeat;\n        use crate::gecko_bindings::structs::StyleImageLayerRepeat;\n\n        fn to_ns(repeat: BackgroundRepeatKeyword) -> StyleImageLayerRepeat {\n            match repeat {\n                BackgroundRepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat,\n                BackgroundRepeatKeyword::Space => StyleImageLayerRepeat::Space,\n                BackgroundRepeatKeyword::Round => StyleImageLayerRepeat::Round,\n                BackgroundRepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat,\n            }\n        }\n\n        let repeat_x = to_ns(servo.0);\n        let repeat_y = to_ns(servo.1);\n        nsStyleImageLayers_Repeat {\n              mXRepeat: repeat_x,\n              mYRepeat: repeat_y,\n        }\n    </%self:simple_image_array_property>\n\n    pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T {\n        use crate::properties::longhands::${shorthand}_repeat::single_value::computed_value::T;\n        use crate::values::specified::background::BackgroundRepeatKeyword;\n        use crate::gecko_bindings::structs::StyleImageLayerRepeat;\n\n        fn to_servo(repeat: StyleImageLayerRepeat) -> BackgroundRepeatKeyword {\n            match repeat {\n                StyleImageLayerRepeat::Repeat => BackgroundRepeatKeyword::Repeat,\n                StyleImageLayerRepeat::Space => BackgroundRepeatKeyword::Space,\n                StyleImageLayerRepeat::Round => BackgroundRepeatKeyword::Round,\n                StyleImageLayerRepeat::NoRepeat => BackgroundRepeatKeyword::NoRepeat,\n                _ => panic!(\"Found unexpected value in style struct for ${shorthand}_repeat property\"),\n            }\n        }\n\n        longhands::${shorthand}_repeat::computed_value::List(\n            self.${image_layers_field}.mLayers.iter()\n                .take(self.${image_layers_field}.mRepeatCount as usize)\n                .map(|ref layer| {\n                    T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat))\n                }).collect()\n        )\n    }\n\n    <% impl_simple_image_array_property(\"clip\", shorthand, image_layers_field, \"mClip\", struct_name) %>\n    <% impl_simple_image_array_property(\"origin\", shorthand, image_layers_field, \"mOrigin\", struct_name) %>\n\n    % for (orientation, keyword) in [(\"x\", \"horizontal\"), (\"y\", \"vertical\")]:\n    pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n\n        let count = other.${image_layers_field}.mPosition${orientation.upper()}Count;\n\n        unsafe {\n            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},\n                                          count as usize,\n                                          LayerType::${shorthand.capitalize()});\n        }\n\n        for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()\n                                  .zip(other.${image_layers_field}.mLayers.iter())\n                                  .take(count as usize) {\n            layer.mPosition.${keyword} = other.mPosition.${keyword}.clone();\n        }\n        self.${image_layers_field}.mPosition${orientation.upper()}Count = count;\n    }\n\n    pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) {\n        self.copy_${shorthand}_position_${orientation}_from(other)\n    }\n\n    pub fn clone_${shorthand}_position_${orientation}(&self)\n        -> longhands::${shorthand}_position_${orientation}::computed_value::T {\n        longhands::${shorthand}_position_${orientation}::computed_value::List(\n            self.${image_layers_field}.mLayers.iter()\n                .take(self.${image_layers_field}.mPosition${orientation.upper()}Count as usize)\n                .map(|position| position.mPosition.${keyword}.clone())\n                .collect()\n        )\n    }\n\n    ${impl_fallback_eq(f\"{shorthand}_position_{orientation}\")}\n\n    pub fn set_${shorthand}_position_${orientation[0]}<I>(&mut self, v: I)\n    where\n        I: IntoIterator<Item = longhands::${shorthand}_position_${orientation[0]}::computed_value::single_value::T>,\n        I::IntoIter: ExactSizeIterator\n    {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n\n        let v = v.into_iter();\n\n        unsafe {\n            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field}, v.len(),\n                                        LayerType::${shorthand.capitalize()});\n        }\n\n        self.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32;\n        for (servo, geckolayer) in v.zip(self.${image_layers_field}\n                                                           .mLayers.iter_mut()) {\n            geckolayer.mPosition.${keyword} = servo;\n        }\n    }\n    % endfor\n\n    <%self:simple_image_array_property name=\"size\" shorthand=\"${shorthand}\" field_name=\"mSize\">\n        servo\n    </%self:simple_image_array_property>\n\n    pub fn clone_${shorthand}_size(&self) -> longhands::${shorthand}_size::computed_value::T {\n        longhands::${shorthand}_size::computed_value::List(\n            self.${image_layers_field}.mLayers.iter().map(|layer| layer.mSize.clone()).collect()\n        )\n    }\n\n    pub fn copy_${shorthand}_image_from(&mut self, other: &Self) {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n        unsafe {\n            let count = other.${image_layers_field}.mImageCount;\n            Gecko_EnsureImageLayersLength(&mut self.${image_layers_field},\n                                          count as usize,\n                                          LayerType::${shorthand.capitalize()});\n\n            for (layer, other) in self.${image_layers_field}.mLayers.iter_mut()\n                                      .zip(other.${image_layers_field}.mLayers.iter())\n                                      .take(count as usize) {\n                layer.mImage = other.mImage.clone();\n            }\n            self.${image_layers_field}.mImageCount = count;\n        }\n    }\n\n    pub fn reset_${shorthand}_image(&mut self, other: &Self) {\n        self.copy_${shorthand}_image_from(other)\n    }\n\n    #[allow(unused_variables)]\n    pub fn set_${shorthand}_image<I>(&mut self, images: I)\n    where\n        I: IntoIterator<Item = longhands::${shorthand}_image::computed_value::single_value::T>,\n        I::IntoIter: ExactSizeIterator\n    {\n        use crate::gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType;\n\n        let images = images.into_iter();\n\n        unsafe {\n            Gecko_EnsureImageLayersLength(\n                &mut self.${image_layers_field},\n                images.len(),\n                LayerType::${shorthand.title()},\n            );\n        }\n\n        self.${image_layers_field}.mImageCount = images.len() as u32;\n        for (image, geckoimage) in images.zip(self.${image_layers_field}.mLayers.iter_mut()) {\n            geckoimage.mImage = image;\n        }\n    }\n\n    ${impl_fallback_eq(f\"{shorthand}_image\")}\n\n    pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T {\n        longhands::${shorthand}_image::computed_value::List(\n            self.${image_layers_field}.mLayers.iter()\n                .take(self.${image_layers_field}.mImageCount as usize)\n                .map(|layer| layer.mImage.clone())\n                .collect()\n        )\n    }\n\n    <%\n        fill_fields = \"mRepeat mClip mOrigin mPositionX mPositionY mImage mSize\"\n        if shorthand == \"background\":\n            fill_fields += \" mAttachment mBlendMode\"\n        else:\n            # mSourceURI uses mImageCount\n            fill_fields += \" mMaskMode mComposite\"\n    %>\n    pub fn fill_arrays(&mut self) {\n        use crate::gecko_bindings::bindings::Gecko_FillAllImageLayers;\n        use std::cmp;\n        let mut max_len = 1;\n        % for member in fill_fields.split():\n            max_len = cmp::max(max_len, self.${image_layers_field}.${member}Count);\n        % endfor\n        unsafe {\n            // While we could do this manually, we'd need to also manually\n            // run all the copy constructors, so we just delegate to gecko\n            Gecko_FillAllImageLayers(&mut self.${image_layers_field}, max_len);\n        }\n    }\n</%def>\n\n// TODO: Gecko accepts lists in most background-related properties. We just use\n// the first element (which is the common case), but at some point we want to\n// add support for parsing these lists in servo and pushing to nsTArray's.\n<% skip_background_longhands = \"\"\"background-repeat\n                                  background-image background-clip\n                                  background-origin background-attachment\n                                  background-size background-position\n                                  background-blend-mode\n                                  background-position-x\n                                  background-position-y\"\"\" %>\n<%self:impl_trait style_struct_name=\"Background\"\n                  skip_longhands=\"${skip_background_longhands}\">\n\n    <% impl_common_image_layer_properties(\"background\") %>\n    <% impl_simple_image_array_property(\"attachment\", \"background\", \"mImage\", \"mAttachment\", \"Background\") %>\n    <% impl_simple_image_array_property(\"blend_mode\", \"background\", \"mImage\", \"mBlendMode\", \"Background\") %>\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"List\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Table\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Effects\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"InheritedBox\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"InheritedTable\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"InheritedText\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Text\">\n</%self:impl_trait>\n\n<% skip_svg_longhands = \"\"\"\nmask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image\n\"\"\"\n%>\n<%self:impl_trait style_struct_name=\"SVG\"\n                  skip_longhands=\"${skip_svg_longhands}\">\n    <% impl_common_image_layer_properties(\"mask\") %>\n    <% impl_simple_image_array_property(\"mode\", \"mask\", \"mMask\", \"mMaskMode\", \"SVG\") %>\n    <% impl_simple_image_array_property(\"composite\", \"mask\", \"mMask\", \"mComposite\", \"SVG\") %>\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"InheritedSVG\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"InheritedUI\">\n    #[inline]\n    pub fn color_scheme_bits(&self) -> values::specified::color::ColorSchemeFlags {\n        self.mColorScheme.bits\n    }\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Column\">\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"Counters\">\n    pub fn ineffective_content_property(&self) -> bool {\n        !self.mContent.is_items()\n    }\n</%self:impl_trait>\n\n<% skip_ui_longhands = \"\"\"animation-name animation-delay animation-duration\n                          animation-direction animation-fill-mode\n                          animation-play-state animation-iteration-count\n                          animation-timing-function animation-composition animation-timeline\n                          animation-range-start animation-range-end\n                          transition-behavior transition-duration transition-delay\n                          transition-timing-function transition-property\n                          scroll-timeline-name scroll-timeline-axis\n                          view-timeline-name view-timeline-axis view-timeline-inset\"\"\" %>\n\n<%self:impl_trait style_struct_name=\"UI\" skip_longhands=\"${skip_ui_longhands}\">\n    ${impl_coordinated_property('transition', 'behavior', 'Behavior')}\n    ${impl_coordinated_property('transition', 'delay', 'Delay')}\n    ${impl_coordinated_property('transition', 'duration', 'Duration')}\n    ${impl_coordinated_property('transition', 'timing_function', 'TimingFunction')}\n    ${impl_coordinated_property('transition', 'property', 'Property')}\n\n    pub fn transition_combined_duration_at(&self, index: usize) -> Time {\n        // https://drafts.csswg.org/css-transitions/#transition-combined-duration\n        Time::from_seconds(\n            self.transition_duration_at(index).seconds().max(0.0) +\n            self.transition_delay_at(index).seconds()\n        )\n    }\n\n    /// Returns whether there are any transitions specified.\n    pub fn specifies_transitions(&self) -> bool {\n        if self.mTransitionPropertyCount == 1 &&\n            self.transition_combined_duration_at(0).seconds() <= 0.0f32 {\n            return false;\n        }\n        self.mTransitionPropertyCount > 0\n    }\n\n    /// Returns whether animation-timeline is initial value. We need this information to resolve\n    /// animation-duration.\n    pub fn has_initial_animation_timeline(&self) -> bool {\n        self.mAnimationTimelineCount == 1 && self.animation_timeline_at(0).is_auto()\n    }\n\n    pub fn animations_equals(&self, other: &Self) -> bool {\n        return self.mAnimationNameCount == other.mAnimationNameCount\n            && self.mAnimationDelayCount == other.mAnimationDelayCount\n            && self.mAnimationDirectionCount == other.mAnimationDirectionCount\n            && self.mAnimationDurationCount == other.mAnimationDurationCount\n            && self.mAnimationFillModeCount == other.mAnimationFillModeCount\n            && self.mAnimationIterationCountCount == other.mAnimationIterationCountCount\n            && self.mAnimationPlayStateCount == other.mAnimationPlayStateCount\n            && self.mAnimationTimingFunctionCount == other.mAnimationTimingFunctionCount\n            && self.mAnimationCompositionCount == other.mAnimationCompositionCount\n            && self.mAnimationTimelineCount == other.mAnimationTimelineCount\n            && self.mAnimationRangeStartCount == other.mAnimationRangeStartCount\n            && self.mAnimationRangeEndCount == other.mAnimationRangeEndCount\n            && unsafe { bindings::Gecko_StyleAnimationsEquals(&self.mAnimations, &other.mAnimations) }\n    }\n\n    ${impl_coordinated_property('animation', 'name', 'Name')}\n    ${impl_coordinated_property('animation', 'delay', 'Delay')}\n    ${impl_coordinated_property('animation', 'duration', 'Duration')}\n    ${impl_coordinated_property('animation', 'direction', 'Direction')}\n    ${impl_coordinated_property('animation', 'fill_mode', 'FillMode')}\n    ${impl_coordinated_property('animation', 'play_state', 'PlayState')}\n    ${impl_coordinated_property('animation', 'composition', 'Composition')}\n    ${impl_coordinated_property('animation', 'iteration_count', 'IterationCount')}\n    ${impl_coordinated_property('animation', 'timeline', 'Timeline')}\n    ${impl_coordinated_property('animation', 'timing_function', 'TimingFunction')}\n    ${impl_coordinated_property('animation', 'range_start', 'RangeStart')}\n    ${impl_coordinated_property('animation', 'range_end', 'RangeEnd')}\n\n    ${impl_coordinated_property('scroll_timeline', 'name', 'Name')}\n    ${impl_coordinated_property('scroll_timeline', 'axis', 'Axis')}\n\n    pub fn scroll_timelines_equals(&self, other: &Self) -> bool {\n        self.mScrollTimelineNameCount == other.mScrollTimelineNameCount\n            && self.mScrollTimelineAxisCount == other.mScrollTimelineAxisCount\n            && unsafe {\n                bindings::Gecko_StyleScrollTimelinesEquals(\n                    &self.mScrollTimelines,\n                    &other.mScrollTimelines,\n                )\n            }\n    }\n\n    ${impl_coordinated_property('view_timeline', 'name', 'Name')}\n    ${impl_coordinated_property('view_timeline', 'axis', 'Axis')}\n    ${impl_coordinated_property('view_timeline', 'inset', 'Inset')}\n\n    pub fn view_timelines_equals(&self, other: &Self) -> bool {\n        self.mViewTimelineNameCount == other.mViewTimelineNameCount\n            && self.mViewTimelineAxisCount == other.mViewTimelineAxisCount\n            && self.mViewTimelineInsetCount == other.mViewTimelineInsetCount\n            && unsafe {\n                bindings::Gecko_StyleViewTimelinesEquals(\n                    &self.mViewTimelines,\n                    &other.mViewTimelines,\n                )\n            }\n    }\n</%self:impl_trait>\n\n<%self:impl_trait style_struct_name=\"XUL\">\n</%self:impl_trait>\n\n% for style_struct in data.style_structs:\n${impl_style_struct(style_struct)}\n% endfor\n\n/// Assert that the initial values set in Gecko style struct constructors\n/// match the values returned by `get_initial_value()` for each longhand.\n#[cfg(feature = \"gecko\")]\n#[inline]\npub fn assert_initial_values_match(data: &PerDocumentStyleData) {\n    if cfg!(debug_assertions) {\n        let data = data.borrow();\n        let cv = data.stylist.device().default_computed_values();\n        <%\n            # Skip properties with initial values that change at computed\n            # value time, or whose initial value depends on the document\n            # / other prefs.\n            SKIPPED = [\n                \"border-top-width\",\n                \"border-bottom-width\",\n                \"border-left-width\",\n                \"border-right-width\",\n                \"column-rule-width\",\n                \"font-family\",\n                \"font-size\",\n                \"outline-width\",\n                \"color\",\n            ]\n            TO_TEST = [p for p in data.longhands if p.enabled_in != \"\" and not p.logical and not p.name in SKIPPED]\n        %>\n        % for property in TO_TEST:\n        assert_eq!(\n            cv.clone_${property.ident}(),\n            longhands::${property.ident}::get_initial_value(),\n            concat!(\n                \"initial value in Gecko style struct for \",\n                stringify!(${property.ident}),\n                \" must match longhands::\",\n                stringify!(${property.ident}),\n                \"::get_initial_value()\"\n            )\n        );\n        % endfor\n    }\n}\n\n% if engine == \"gecko\":\npub mod system_font {\n    //! We deal with system fonts here\n    //!\n    //! System fonts can only be set as a group via the font shorthand.\n    //! They resolve at compute time (not parse time -- this lets the\n    //! browser respond to changes to the OS font settings).\n    //!\n    //! While Gecko handles these as a separate property and keyword\n    //! values on each property indicating that the font should be picked\n    //! from the -x-system-font property, we avoid this. Instead,\n    //! each font longhand has a special SystemFont variant which contains\n    //! the specified system font. When the cascade function (in helpers)\n    //! detects that a value has a system font, it will resolve it, and\n    //! cache it on the ComputedValues. After this, it can be just fetched\n    //! whenever a font longhand on the same element needs the system font.\n    //!\n    //! When a longhand property is holding a SystemFont, it's serialized\n    //! to an empty string as if its value comes from a shorthand with\n    //! variable reference. We may want to improve this behavior at some\n    //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.\n\n    use crate::properties::longhands;\n    use std::hash::{Hash, Hasher};\n    use crate::values::computed::{ToComputedValue, Context};\n    use crate::values::specified::font::SystemFont;\n    // ComputedValues are compared at times\n    // so we need these impls. We don't want to\n    // add Eq to Number (which contains a float)\n    // so instead we have an eq impl which skips the\n    // cached values\n    impl PartialEq for ComputedSystemFont {\n        fn eq(&self, other: &Self) -> bool {\n            self.system_font == other.system_font\n        }\n    }\n    impl Eq for ComputedSystemFont {}\n\n    impl Hash for ComputedSystemFont {\n        fn hash<H: Hasher>(&self, hasher: &mut H) {\n            self.system_font.hash(hasher)\n        }\n    }\n\n    impl ToComputedValue for SystemFont {\n        type ComputedValue = ComputedSystemFont;\n\n        fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue {\n            use crate::gecko_bindings::bindings;\n            use crate::gecko_bindings::structs::nsFont;\n            use crate::values::computed::font::FontSize;\n            use crate::values::specified::font::KeywordInfo;\n            use crate::values::generics::NonNegative;\n            use std::mem;\n\n            let mut system = mem::MaybeUninit::<nsFont>::uninit();\n            let system = unsafe {\n                bindings::Gecko_nsFont_InitSystem(\n                    system.as_mut_ptr(),\n                    *self,\n                    &**cx.style().get_font(),\n                    cx.device().document()\n                );\n                &mut *system.as_mut_ptr()\n            };\n            let size = NonNegative(cx.maybe_zoom_text(system.size.0));\n            let ret = ComputedSystemFont {\n                font_family: system.family.clone(),\n                font_size: FontSize {\n                    computed_size: size,\n                    used_size: size,\n                    keyword_info: KeywordInfo::none()\n                },\n                font_weight: system.weight,\n                font_stretch: system.stretch,\n                font_style: system.style,\n                system_font: *self,\n            };\n            unsafe { bindings::Gecko_nsFont_Destroy(system); }\n            ret\n        }\n\n        fn from_computed_value(_: &ComputedSystemFont) -> Self {\n            unreachable!()\n        }\n    }\n\n    #[inline]\n    /// Compute and cache a system font\n    ///\n    /// Must be called before attempting to compute a system font\n    /// specified value\n    pub fn resolve_system_font(system: SystemFont, context: &mut Context) {\n        // Checking if context.cached_system_font.is_none() isn't enough,\n        // if animating from one system font to another the cached system font\n        // may change\n        if context.cached_system_font.as_ref().is_none_or(|x| x.system_font != system) {\n            let computed = system.to_computed_value(context);\n            context.cached_system_font = Some(computed);\n        }\n    }\n\n    #[derive(Clone, Debug)]\n    pub struct ComputedSystemFont {\n        % for name in SYSTEM_FONT_LONGHANDS:\n            pub ${name}: longhands::${name}::computed_value::T,\n        % endfor\n        pub system_font: SystemFont,\n    }\n}\n% endif\n"
  },
  {
    "path": "style/properties/helpers/animated_properties.mako.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n<%namespace name=\"helpers\" file=\"/helpers.mako.rs\" />\n\n<%\n    from data import SYSTEM_FONT_LONGHANDS, to_camel_case\n    from itertools import groupby\n%>\n\n#[cfg(feature = \"gecko\")] use crate::gecko_bindings::structs::NonCustomCSSPropertyId;\nuse crate::properties::{\n    longhands::{\n        self, visibility::computed_value::T as Visibility,\n    },\n    CSSWideKeyword, LonghandId,\n    PropertyDeclaration, PropertyDeclarationId,\n};\n#[cfg(feature = \"gecko\")] use crate::properties::{\n    gecko,\n    longhands::content_visibility::computed_value::T as ContentVisibility,\n    NonCustomPropertyId,\n};\nuse std::ptr;\nuse std::mem;\nuse rustc_hash::FxHashMap;\nuse super::ComputedValues;\n#[cfg(feature = \"servo\")] use crate::context::SharedStyleContext;\nuse crate::derives::*;\nuse crate::properties::OwnedPropertyDeclarationId;\nuse crate::dom::AttributeTracker;\nuse crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};\nuse crate::values::animated::effects::AnimatedFilter;\n#[cfg(feature = \"gecko\")] use crate::values::computed::TransitionProperty;\nuse crate::values::computed::{ClipRect, Context};\nuse crate::values::computed::ToComputedValue;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::effects::Filter;\nuse void::{self, Void};\nuse crate::properties_and_values::value::CustomAnimatedValue;\nuse debug_unreachable::debug_unreachable;\n\n/// Convert NonCustomCSSPropertyId to TransitionProperty\n#[cfg(feature = \"gecko\")]\n#[allow(non_upper_case_globals)]\nimpl From<NonCustomCSSPropertyId> for TransitionProperty {\n    fn from(property: NonCustomCSSPropertyId) -> TransitionProperty {\n        TransitionProperty::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(property).unwrap())\n    }\n}\n\n/// A collection of AnimationValue that were composed on an element.\n/// This HashMap stores the values that are the last AnimationValue to be\n/// composed for each TransitionProperty.\npub type AnimationValueMap = FxHashMap<OwnedPropertyDeclarationId, AnimationValue>;\n\n/// An enum to represent a single computed value belonging to an animated\n/// property in order to be interpolated with another one. When interpolating,\n/// both values need to belong to the same property.\n#[derive(Debug, MallocSizeOf)]\n#[repr(u16)]\npub enum AnimationValue {\n    % for prop in data.longhands:\n    /// `${prop.name}`\n    % if prop.animatable and not prop.logical:\n    ${prop.camel_case}(${prop.animated_type()}),\n    % else:\n    ${prop.camel_case}(Void),\n    % endif\n    % endfor\n    /// A custom property.\n    Custom(CustomAnimatedValue),\n}\n\n<%\n    animated = []\n    unanimated = []\n    animated_with_logical = []\n    for prop in data.longhands:\n        if prop.animatable:\n            animated_with_logical.append(prop)\n        if prop.animatable and not prop.logical:\n            animated.append(prop)\n        else:\n            unanimated.append(prop)\n%>\n\n#[repr(C)]\nstruct AnimationValueVariantRepr<T> {\n    tag: u16,\n    value: T\n}\n\nimpl Clone for AnimationValue {\n    #[inline]\n    fn clone(&self) -> Self {\n        use self::AnimationValue::*;\n\n        <%\n            [copy, others] = [list(g) for _, g in groupby(animated, key=lambda x: not x.specified_is_copy())]\n        %>\n\n        let self_tag = unsafe { *(self as *const _ as *const u16) };\n        if self_tag <= LonghandId::${copy[-1].camel_case} as u16 {\n            #[derive(Clone, Copy)]\n            #[repr(u16)]\n            enum CopyVariants {\n                % for prop in copy:\n                _${prop.camel_case}(${prop.animated_type()}),\n                % endfor\n            }\n\n            unsafe {\n                let mut out = mem::MaybeUninit::uninit();\n                ptr::write(\n                    out.as_mut_ptr() as *mut CopyVariants,\n                    *(self as *const _ as *const CopyVariants),\n                );\n                return out.assume_init();\n            }\n        }\n\n        match *self {\n            % for ty, props in groupby(others, key=lambda x: x.animated_type()):\n            <% props = list(props) %>\n            ${\" |\\n\".join(\"{}(ref value)\".format(prop.camel_case) for prop in props)} => {\n                % if len(props) == 1:\n                ${props[0].camel_case}(value.clone())\n                % else:\n                unsafe {\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,\n                        AnimationValueVariantRepr {\n                            tag: *(self as *const _ as *const u16),\n                            value: value.clone(),\n                        },\n                    );\n                    out.assume_init()\n                }\n                % endif\n            }\n            % endfor\n            Custom(ref animated_value) => Custom(animated_value.clone()),\n            _ => unsafe { debug_unreachable!() }\n        }\n    }\n}\n\nimpl PartialEq for AnimationValue {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        use self::AnimationValue::*;\n\n        unsafe {\n            let this_tag = *(self as *const _ as *const u16);\n            let other_tag = *(other as *const _ as *const u16);\n            if this_tag != other_tag {\n                return false;\n            }\n\n            match *self {\n                % for ty, props in groupby(animated, key=lambda x: x.animated_type()):\n                ${\" |\\n\".join(\"{}(ref this)\".format(prop.camel_case) for prop in props)} => {\n                    let other_repr =\n                        &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);\n                    *this == other_repr.value\n                }\n                % endfor\n                ${\" |\\n\".join(\"{}(void)\".format(prop.camel_case) for prop in unanimated)} => {\n                    void::unreachable(void)\n                },\n                AnimationValue::Custom(ref this) => {\n                    let other_repr =\n                        &*(other as *const _ as *const AnimationValueVariantRepr<CustomAnimatedValue>);\n                    *this == other_repr.value\n                },\n            }\n        }\n    }\n}\n\nimpl AnimationValue {\n    /// Returns the longhand id this animated value corresponds to.\n    #[inline]\n    pub fn id(&self) -> PropertyDeclarationId<'_> {\n        if let AnimationValue::Custom(animated_value) = self {\n            return PropertyDeclarationId::Custom(&animated_value.name);\n        }\n\n        let id = unsafe { *(self as *const _ as *const LonghandId) };\n        debug_assert_eq!(id, match *self {\n            % for prop in data.longhands:\n            % if prop.animatable and not prop.logical:\n            AnimationValue::${prop.camel_case}(..) => LonghandId::${prop.camel_case},\n            % else:\n            AnimationValue::${prop.camel_case}(void) => void::unreachable(void),\n            % endif\n            % endfor\n            AnimationValue::Custom(..) => unsafe { debug_unreachable!() },\n        });\n        PropertyDeclarationId::Longhand(id)\n    }\n\n    /// Returns whether this value is interpolable with another one.\n    pub fn interpolable_with(&self, other: &Self) -> bool {\n        self.animate(other, Procedure::Interpolate { progress: 0.5 }).is_ok()\n    }\n\n    /// \"Uncompute\" this animation value in order to be used inside the CSS\n    /// cascade.\n    pub fn uncompute(&self) -> PropertyDeclaration {\n        use crate::properties::longhands;\n        use self::AnimationValue::*;\n\n        use super::PropertyDeclarationVariantRepr;\n\n        match *self {\n            <% keyfunc = lambda x: (x.base_type(), x.specified_type(), x.boxed, x.animation_type != \"discrete\") %>\n            % for (ty, specified, boxed, to_animated), props in groupby(animated, key=keyfunc):\n            <% props = list(props) %>\n            ${\" |\\n\".join(\"{}(ref value)\".format(prop.camel_case) for prop in props)} => {\n                % if to_animated:\n                let value = ToAnimatedValue::from_animated_value(value.clone());\n                % endif\n                let value = ${ty}::from_computed_value(&value);\n                % if boxed:\n                let value = Box::new(value);\n                % endif\n                % if len(props) == 1:\n                PropertyDeclaration::${props[0].camel_case}(value)\n                % else:\n                unsafe {\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified}>,\n                        PropertyDeclarationVariantRepr {\n                            tag: *(self as *const _ as *const u16),\n                            value,\n                        },\n                    );\n                    out.assume_init()\n                }\n                % endif\n            }\n            % endfor\n            ${\" |\\n\".join(\"{}(void)\".format(prop.camel_case) for prop in unanimated)} => {\n                void::unreachable(void)\n            },\n            Custom(ref animated_value) => animated_value.to_declaration(),\n        }\n    }\n\n    /// Construct an AnimationValue from a property declaration.\n    pub fn from_declaration(\n        decl: &PropertyDeclaration,\n        context: &mut Context,\n        style: &ComputedValues,\n        initial: &ComputedValues,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> Option<Self> {\n        use super::PropertyDeclarationVariantRepr;\n\n        <%\n            keyfunc = lambda x: (\n                x.specified_type(),\n                x.animated_type(),\n                x.boxed,\n                x.animation_type not in [\"discrete\", \"none\"],\n                x.style_struct.inherited,\n                x.ident in SYSTEM_FONT_LONGHANDS and engine == \"gecko\",\n            )\n        %>\n\n        let animatable = match *decl {\n            % for (specified_ty, ty, boxed, to_animated, inherit, system), props in groupby(animated_with_logical, key=keyfunc):\n            ${\" |\\n\".join(\"PropertyDeclaration::{}(ref value)\".format(prop.camel_case) for prop in props)} => {\n                let decl_repr = unsafe {\n                    &*(decl as *const _ as *const PropertyDeclarationVariantRepr<${specified_ty}>)\n                };\n                let longhand_id = unsafe {\n                    *(&decl_repr.tag as *const u16 as *const LonghandId)\n                };\n                context.for_non_inherited_property = ${\"false\" if inherit else \"true\"};\n                % if system:\n                if let Some(sf) = value.get_system() {\n                    gecko::system_font::resolve_system_font(sf, context)\n                }\n                % endif\n                % if boxed:\n                let value = (**value).to_computed_value(context);\n                % else:\n                let value = value.to_computed_value(context);\n                % endif\n                % if to_animated:\n                let value = value.to_animated_value(&crate::values::animated::Context { style });\n                % endif\n\n                unsafe {\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,\n                        AnimationValueVariantRepr {\n                            tag: longhand_id.to_physical(context.builder.writing_mode) as u16,\n                            value,\n                        },\n                    );\n                    out.assume_init()\n                }\n            }\n            % endfor\n            PropertyDeclaration::CSSWideKeyword(ref declaration) => {\n                match declaration.id.to_physical(context.builder.writing_mode) {\n                    // We put all the animatable properties first in the hopes\n                    // that it might increase match locality.\n                    % for prop in data.longhands:\n                    % if prop.animatable and not prop.logical:\n                    LonghandId::${prop.camel_case} => {\n                        // FIXME(emilio, bug 1533327): I think revert (and\n                        // revert-layer) handling is not fine here, but what to\n                        // do instead?\n                        //\n                        // Seems we'd need the computed value as if it was\n                        // revert, somehow. Treating it as `unset` seems fine\n                        // for now...\n                        let style_struct = match declaration.keyword {\n                            % if not prop.style_struct.inherited:\n                            CSSWideKeyword::Revert |\n                            CSSWideKeyword::RevertRule |\n                            CSSWideKeyword::RevertLayer |\n                            CSSWideKeyword::Unset |\n                            % endif\n                            CSSWideKeyword::Initial => {\n                                initial.get_${prop.style_struct.name_lower}()\n                            },\n                            % if prop.style_struct.inherited:\n                            CSSWideKeyword::Revert |\n                            CSSWideKeyword::RevertRule |\n                            CSSWideKeyword::RevertLayer |\n                            CSSWideKeyword::Unset |\n                            % endif\n                            CSSWideKeyword::Inherit => {\n                                context.builder\n                                       .get_parent_${prop.style_struct.name_lower}()\n                            },\n                        };\n                        let computed = style_struct\n                        % if prop.logical:\n                            .clone_${prop.ident}(context.builder.writing_mode);\n                        % else:\n                            .clone_${prop.ident}();\n                        % endif\n\n                        % if prop.animation_type != \"discrete\":\n                        let computed = computed.to_animated_value(&crate::values::animated::Context {\n                            style\n                        });\n                        % endif\n                        AnimationValue::${prop.camel_case}(computed)\n                    },\n                    % endif\n                    % endfor\n                    % for prop in data.longhands:\n                    % if not prop.animatable or prop.logical:\n                    LonghandId::${prop.camel_case} => return None,\n                    % endif\n                    % endfor\n                }\n            },\n            PropertyDeclaration::WithVariables(ref declaration) => {\n                let mut cache = Default::default();\n                let substituted = {\n                    let substitution_functions = &context.style().substitution_functions();\n\n                    debug_assert!(\n                        context.builder.stylist.is_some(),\n                        \"Need a Stylist to substitute variables!\"\n                    );\n                    declaration.value.substitute_variables(\n                        declaration.id,\n                        substitution_functions,\n                        context.builder.stylist.unwrap(),\n                        context,\n                        &mut cache,\n                        attribute_tracker,\n                    )\n                };\n                return AnimationValue::from_declaration(\n                    &substituted,\n                    context,\n                    style,\n                    initial,\n                    attribute_tracker,\n                )\n            },\n            PropertyDeclaration::Custom(ref declaration) => {\n                AnimationValue::Custom(CustomAnimatedValue::from_declaration(\n                    declaration,\n                    context,\n                )?)\n            },\n            _ => return None // non animatable properties will get included because of shorthands. ignore.\n        };\n        Some(animatable)\n    }\n\n    /// Returns whether the animated value of `property` is different in `before` and `after`, in\n    /// order to determine whether a transition should be started.\n    /// NOTE(emilio): We don't need to convert to animated values here, if the computed value is\n    /// different the animated value should be different too.\n    pub fn is_different_for(\n        property: PropertyDeclarationId,\n        before: &ComputedValues,\n        after: &ComputedValues,\n    ) -> bool {\n        let longhand = match property {\n            PropertyDeclarationId::Longhand(id) => id,\n            PropertyDeclarationId::Custom(ref name) => {\n                // FIXME(bug 1869476): This should use a stylist to determine whether the name\n                // corresponds to an inherited custom property and then choose the\n                // inherited/non_inherited map accordingly.\n                let before = before.custom_properties();\n                let before_value = before.inherited.get(*name).or_else(|| before.non_inherited.get(*name));\n                let after = after.custom_properties();\n                let after_value = after.inherited.get(*name).or_else(|| after.non_inherited.get(*name));\n                return before_value != after_value\n            }\n        };\n\n        match longhand {\n            % for prop in data.longhands:\n            % if prop.animatable and not prop.logical:\n            LonghandId::${prop.camel_case} => !before.${prop.ident}_equals(after),\n            % endif\n            % endfor\n            _ => false,\n        }\n    }\n\n    /// Get an AnimationValue for an declaration id from a given computed values.\n    pub fn from_computed_values(\n        property: PropertyDeclarationId,\n        style: &ComputedValues,\n    ) -> Option<Self> {\n        let property = match property {\n            PropertyDeclarationId::Longhand(id) => id,\n            PropertyDeclarationId::Custom(ref name) => {\n                // FIXME(bug 1869476): This should use a stylist to determine whether the name\n                // corresponds to an inherited custom property and then choose the\n                // inherited/non_inherited map accordingly.\n                let p = &style.custom_properties();\n                let value = p.inherited.get(*name).or_else(|| p.non_inherited.get(*name));\n                return Some(AnimationValue::Custom(CustomAnimatedValue::from_computed(name, value)))\n            }\n        };\n\n        Some(match property {\n            % for prop in data.longhands:\n            % if prop.animatable and not prop.logical:\n            LonghandId::${prop.camel_case} => {\n                let computed = style.clone_${prop.ident}();\n                AnimationValue::${prop.camel_case}(\n                % if prop.animation_type == \"discrete\":\n                    computed\n                % else:\n                    computed.to_animated_value(&crate::values::animated::Context { style })\n                % endif\n                )\n            }\n            % endif\n            % endfor\n            _ => return None,\n        })\n    }\n\n    /// Update `style` with the value of this `AnimationValue`.\n    ///\n    /// SERVO ONLY: This doesn't properly handle things like updating 'em' units\n    /// when animated font-size.\n    #[cfg(feature = \"servo\")]\n    pub fn set_in_style_for_servo(&self, style: &mut ComputedValues, context: &SharedStyleContext) {\n        match self {\n            % for prop in data.longhands:\n            % if prop.animatable and not prop.logical:\n            AnimationValue::${prop.camel_case}(ref value) => {\n                let value: longhands::${prop.ident}::computed_value::T =\n                % if prop.animation_type != \"discrete\":\n                    ToAnimatedValue::from_animated_value(value.clone());\n                % else:\n                    value.clone();\n                % endif\n                style.mutate_${prop.style_struct.name_lower}().set_${prop.ident}(value);\n            }\n            % else:\n            AnimationValue::${prop.camel_case}(..) => unreachable!(),\n            % endif\n            % endfor\n            AnimationValue::Custom(CustomAnimatedValue { name, value }) => {\n                let registration = context.stylist.get_custom_property_registration(&name);\n                match value {\n                    Some(value) => style.custom_properties.insert(registration, name, value.clone()),\n                    None => style.custom_properties.remove(registration, name),\n                }\n            },\n        }\n    }\n}\n\nfn animate_discrete<T: Clone>(this: &T, other: &T, procedure: Procedure) -> Result<T, ()> {\n    if let Procedure::Interpolate { progress } = procedure {\n        Ok(if progress < 0.5 { this.clone() } else { other.clone() })\n    } else {\n        Err(())\n    }\n}\n\nimpl Animate for AnimationValue {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(unsafe {\n            use self::AnimationValue::*;\n\n            let this_tag = *(self as *const _ as *const u16);\n            let other_tag = *(other as *const _ as *const u16);\n            if this_tag != other_tag {\n                panic!(\"Unexpected AnimationValue::animate call\");\n            }\n\n            match *self {\n                <% keyfunc = lambda x: (x.animated_type(), x.animation_type == \"discrete\") %>\n                % for (ty, discrete), props in groupby(animated, key=keyfunc):\n                ${\" |\\n\".join(\"{}(ref this)\".format(prop.camel_case) for prop in props)} => {\n                    let other_repr =\n                        &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);\n                    % if discrete:\n                    let value = animate_discrete(this, &other_repr.value, procedure)?;\n                    % else:\n                    let value = this.animate(&other_repr.value, procedure)?;\n                    % endif\n\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut AnimationValueVariantRepr<${ty}>,\n                        AnimationValueVariantRepr {\n                            tag: this_tag,\n                            value,\n                        },\n                    );\n                    out.assume_init()\n                },\n                % endfor\n                ${\" |\\n\".join(\"{}(void)\".format(prop.camel_case) for prop in unanimated)} => {\n                    void::unreachable(void)\n                },\n                Custom(ref self_value) => {\n                    let Custom(ref other_value) = *other else { unreachable!() };\n                    Custom(self_value.animate(other_value, procedure)?)\n                },\n            }\n        })\n    }\n}\n\n<%\n    nondiscrete = []\n    for prop in animated:\n        if prop.animation_type != \"discrete\":\n            nondiscrete.append(prop)\n%>\n\nimpl ComputeSquaredDistance for AnimationValue {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        unsafe {\n            use self::AnimationValue::*;\n\n            let this_tag = *(self as *const _ as *const u16);\n            let other_tag = *(other as *const _ as *const u16);\n            if this_tag != other_tag {\n                panic!(\"Unexpected AnimationValue::compute_squared_distance call\");\n            }\n\n            match *self {\n                % for ty, props in groupby(nondiscrete, key=lambda x: x.animated_type()):\n                ${\" |\\n\".join(\"{}(ref this)\".format(prop.camel_case) for prop in props)} => {\n                    let other_repr =\n                        &*(other as *const _ as *const AnimationValueVariantRepr<${ty}>);\n\n                    this.compute_squared_distance(&other_repr.value)\n                }\n                % endfor\n                _ => Err(()),\n            }\n        }\n    }\n}\n\nimpl ToAnimatedZero for AnimationValue {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        match *self {\n            % for prop in data.longhands:\n            % if prop.animatable and not prop.logical and prop.animation_type != \"discrete\":\n            AnimationValue::${prop.camel_case}(ref base) => {\n                Ok(AnimationValue::${prop.camel_case}(base.to_animated_zero()?))\n            },\n            % endif\n            % endfor\n            AnimationValue::Custom(..) => {\n                // TODO(bug 1869185): For some non-universal registered custom properties, it may make sense to implement this.\n                Err(())\n            },\n            _ => Err(()),\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/web-animations-1/#animating-visibility>\nimpl Animate for Visibility {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match procedure {\n            Procedure::Interpolate { .. } => {\n                let (this_weight, other_weight) = procedure.weights();\n                match (*self, *other) {\n                    (Visibility::Visible, _) => {\n                        Ok(if this_weight > 0.0 { *self } else { *other })\n                    },\n                    (_, Visibility::Visible) => {\n                        Ok(if other_weight > 0.0 { *other } else { *self })\n                    },\n                    _ => Err(()),\n                }\n            },\n            _ => Err(()),\n        }\n    }\n}\n\nimpl ComputeSquaredDistance for Visibility {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt(if *self == *other { 0. } else { 1. }))\n    }\n}\n\nimpl ToAnimatedZero for Visibility {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\n/// <https://drafts.csswg.org/css-contain-3/#content-visibility-animation>\n#[cfg(feature = \"gecko\")]\nimpl Animate for ContentVisibility {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match procedure {\n            Procedure::Interpolate { .. } => {\n                let (this_weight, other_weight) = procedure.weights();\n                match (*self, *other) {\n                    (ContentVisibility::Hidden, _) => {\n                        Ok(if other_weight > 0.0 { *other } else { *self })\n                    },\n                    (_, ContentVisibility::Hidden) => {\n                        Ok(if this_weight > 0.0 { *self } else { *other })\n                    },\n                    _ => Err(()),\n                }\n            },\n            _ => Err(()),\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ComputeSquaredDistance for ContentVisibility {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt(if *self == *other { 0. } else { 1. }))\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ToAnimatedZero for ContentVisibility {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\n/// <https://drafts.csswg.org/css-transitions/#animtype-rect>\nimpl Animate for ClipRect {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use crate::values::computed::LengthOrAuto;\n        let animate_component = |this: &LengthOrAuto, other: &LengthOrAuto| {\n            let result = this.animate(other, procedure)?;\n            if let Procedure::Interpolate { .. } = procedure {\n                return Ok(result);\n            }\n            if result.is_auto() {\n                // FIXME(emilio): Why? A couple SMIL tests fail without this,\n                // but it seems extremely fishy.\n                return Err(());\n            }\n            Ok(result)\n        };\n\n        Ok(ClipRect {\n            top: animate_component(&self.top, &other.top)?,\n            right: animate_component(&self.right, &other.right)?,\n            bottom: animate_component(&self.bottom, &other.bottom)?,\n            left: animate_component(&self.left, &other.left)?,\n        })\n    }\n}\n\n<%\n    FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',\n                         'HueRotate', 'Invert', 'Opacity', 'Saturate',\n                         'Sepia' ]\n%>\n\n/// <https://drafts.fxtf.org/filters/#animation-of-filters>\nimpl Animate for AnimatedFilter {\n    fn animate(\n        &self,\n        other: &Self,\n        procedure: Procedure,\n    ) -> Result<Self, ()> {\n        use crate::values::animated::animate_multiplicative_factor;\n        match (self, other) {\n            % for func in ['Blur', 'DropShadow', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:\n            (&Filter::${func}(ref this), &Filter::${func}(ref other)) => {\n                Ok(Filter::${func}(this.animate(other, procedure)?))\n            },\n            % endfor\n            % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:\n            (&Filter::${func}(this), &Filter::${func}(other)) => {\n                Ok(Filter::${func}(animate_multiplicative_factor(this.0, other.0, procedure)?.into()))\n            },\n            % endfor\n            _ => Err(()),\n        }\n    }\n}\n\n/// <http://dev.w3.org/csswg/css-transforms/#none-transform-animation>\nimpl ToAnimatedZero for AnimatedFilter {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        match *self {\n            % for func in ['Blur', 'DropShadow', 'Grayscale', 'HueRotate', 'Invert', 'Sepia']:\n            Filter::${func}(ref this) => Ok(Filter::${func}(this.to_animated_zero()?)),\n            % endfor\n            % for func in ['Brightness', 'Contrast', 'Opacity', 'Saturate']:\n            Filter::${func}(_) => Ok(Filter::${func}(1.0.into())),\n            % endfor\n            _ => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "style/properties/helpers.mako.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n<%! from data import to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS %>\n\n<%def name=\"longhand(property)\">\n/// ${property.spec}\npub mod ${property.ident} {\n    #[allow(unused_imports)]\n    use crate::derives::*;\n    #[allow(unused_imports)]\n    use crate::values::{computed, generics, specified};\n    #[allow(unused_imports)]\n    use crate::values::computed::ToComputedValue;\n    #[allow(unused_imports)]\n    use servo_arc::Arc;\n    use cssparser::Parser;\n    #[allow(unused_imports)]\n    use crate::parser::{Parse, ParserContext};\n    use style_traits::ParseError;\n    #[allow(unused_imports)]\n    use crate::properties::{longhands, LonghandId, CSSWideKeyword, PropertyDeclaration};\n\n    #[allow(unused_variables)]\n    pub unsafe fn cascade_property(\n        declaration: &PropertyDeclaration,\n        context: &mut computed::Context,\n    ) {\n        % if property.logical:\n        declaration.debug_crash(\"Should physicalize before entering here\");\n        % else:\n        context.for_non_inherited_property = ${\"false\" if property.style_struct.inherited else \"true\"};\n        % if property.logical_group:\n        debug_assert_eq!(\n            declaration.id().as_longhand().unwrap().logical_group(),\n            LonghandId::${property.camel_case}.logical_group(),\n        );\n        % else:\n        debug_assert_eq!(\n            declaration.id().as_longhand().unwrap(),\n            LonghandId::${property.camel_case},\n        );\n        % endif\n        let specified_value = match *declaration {\n            PropertyDeclaration::CSSWideKeyword(ref wk) => {\n                match wk.keyword {\n                    % if not property.style_struct.inherited:\n                    CSSWideKeyword::Unset |\n                    % endif\n                    CSSWideKeyword::Initial => {\n                        % if not property.style_struct.inherited:\n                            declaration.debug_crash(\"Unexpected initial or unset for non-inherited property\");\n                        % else:\n                            context.builder.reset_${property.ident}();\n                        % endif\n                    },\n                    % if property.style_struct.inherited:\n                    CSSWideKeyword::Unset |\n                    % endif\n                    CSSWideKeyword::Inherit => {\n                        % if not property.style_struct.inherited:\n                            context.rule_cache_conditions.borrow_mut().set_uncacheable();\n                        % endif\n                        % if property.is_zoom_dependent():\n                            if !context.builder.effective_zoom_for_inheritance.is_one() {\n                                let old_zoom = context.builder.effective_zoom;\n                                context.builder.effective_zoom = context.builder.effective_zoom_for_inheritance;\n                                let computed = context.builder.inherited_style.clone_${property.ident}();\n                                let specified = computed::ToComputedValue::from_computed_value(&computed);\n                                % if property.boxed:\n                                let specified = Box::new(specified);\n                                % endif\n                                let decl = PropertyDeclaration::${property.camel_case}(specified);\n                                cascade_property(&decl, context);\n                                context.builder.effective_zoom = old_zoom;\n                                return;\n                            }\n                        % endif\n                        % if property.style_struct.inherited:\n                            declaration.debug_crash(\"Unexpected inherit or unset for non-zoom-dependent inherited property\");\n                        % else:\n                            context.builder.inherit_${property.ident}();\n                        % endif\n                    }\n                    CSSWideKeyword::RevertRule |\n                    CSSWideKeyword::RevertLayer |\n                    CSSWideKeyword::Revert => {\n                        declaration.debug_crash(\"Found revert* not dealt with\");\n                    },\n                }\n                return;\n            },\n            #[cfg(debug_assertions)]\n            PropertyDeclaration::WithVariables(..) => {\n                declaration.debug_crash(\"Found variables not substituted\");\n                return;\n            },\n            _ => unsafe {\n                declaration.unchecked_value_as::<${property.specified_type()}>()\n            },\n        };\n\n        % if property.ident in SYSTEM_FONT_LONGHANDS and engine == \"gecko\":\n        if let Some(sf) = specified_value.get_system() {\n            crate::properties::gecko::system_font::resolve_system_font(sf, context);\n        }\n        % endif\n\n        % if property.vector and not property.vector.simple_bindings and engine == \"gecko\":\n            // In the case of a vector property we want to pass down an\n            // iterator so that this can be computed without allocation.\n            //\n            // However, computing requires a context, but the style struct\n            // being mutated is on the context. We temporarily remove it,\n            // mutate it, and then put it back. Vector longhands cannot\n            // touch their own style struct whilst computing, else this will\n            // panic.\n            let mut s =\n                context.builder.take_${property.style_struct.name_lower}();\n            {\n                let iter = specified_value.compute_iter(context);\n                s.set_${property.ident}(iter);\n            }\n            context.builder.put_${property.style_struct.name_lower}(s);\n        % else:\n            % if property.boxed:\n            let computed = (**specified_value).to_computed_value(context);\n            % else:\n            let computed = specified_value.to_computed_value(context);\n            % endif\n            context.builder.set_${property.ident}(computed)\n        % endif\n        % endif\n    }\n\n    pub fn parse_declared<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<PropertyDeclaration, ParseError<'i>> {\n        parse(context, input)\n        % if property.boxed:\n            .map(Box::new)\n        % endif\n            .map(PropertyDeclaration::${property.camel_case})\n    }\n\n    % if property.vector:\n    pub mod single_value {\n        use super::*;\n    % endif\n    % if property.predefined_type:\n    #[allow(unused_imports)]\n    use app_units::Au;\n    #[allow(unused_imports)]\n    use crate::values::specified::AllowQuirks;\n    #[allow(unused_imports)]\n    use crate::Zero;\n    #[allow(unused_imports)]\n    use smallvec::SmallVec;\n    pub use crate::values::specified::${property.predefined_type} as SpecifiedValue;\n    pub mod computed_value {\n        pub use crate::values::computed::${property.predefined_type} as T;\n    }\n    % if property.initial_value:\n    #[inline]\n    pub fn get_initial_value() -> computed_value::T { ${property.initial_value} }\n    #[inline]\n    pub fn get_initial_specified_value() -> SpecifiedValue {\n    % if property.initial_specified_value:\n        ${property.initial_specified_value}\n    % else:\n        ToComputedValue::from_computed_value(&get_initial_value())\n    % endif\n    }\n    % endif\n    #[allow(unused_variables)]\n    #[inline]\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<SpecifiedValue, ParseError<'i>> {\n        % if property.allow_quirks:\n        specified::${property.predefined_type}::${property.parse_method}_quirky(context, input, AllowQuirks::Yes)\n        % elif property.parse_method != \"parse\":\n        specified::${property.predefined_type}::${property.parse_method}(context, input)\n        % else:\n        <specified::${property.predefined_type} as crate::parser::Parse>::parse(context, input)\n        % endif\n    }\n    % elif property.keyword:\n    pub use self::computed_value::T as SpecifiedValue;\n    pub mod computed_value {\n        #[allow(unused_imports)]\n        use crate::derives::*;\n        #[cfg_attr(feature = \"servo\", derive(Deserialize, Hash, Serialize))]\n        #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem, ToTyped)]\n        pub enum T {\n        % for variant in property.keyword.values_for(engine):\n        <%\n            aliases = []\n            for alias, v in property.keyword.aliases_for(engine).items():\n                if variant == v:\n                    aliases.append(alias)\n        %>\n        % if aliases:\n        #[parse(aliases = \"${','.join(sorted(aliases))}\")]\n        % endif\n        ${to_camel_case(variant)},\n        % endfor\n        }\n    }\n    #[inline]\n    pub fn get_initial_value() -> computed_value::T {\n        computed_value::T::${to_camel_case(property.keyword.values[0])}\n    }\n    #[inline]\n    pub fn get_initial_specified_value() -> SpecifiedValue {\n        SpecifiedValue::${to_camel_case(property.keyword.values[0])}\n    }\n    #[inline]\n    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)\n                         -> Result<SpecifiedValue, ParseError<'i>> {\n        SpecifiedValue::parse(input)\n    }\n\n    #[cfg(feature = \"gecko\")]\n    impl SpecifiedValue {\n        /// Obtain a specified value from a Gecko keyword value\n        ///\n        /// Intended for use with presentation attributes, not style structs\n        pub fn from_gecko_keyword(kw: u32) -> Self {\n            use crate::gecko_bindings::structs;\n            % for value in property.keyword.values_for(engine):\n            // We can't match on enum values if we're matching on a u32\n            const ${to_rust_ident(value).upper()}: u32 = structs::${property.keyword.gecko_constant(value)} as u32;\n            % endfor\n            match kw {\n                % for value in property.keyword.values_for(engine):\n                ${to_rust_ident(value).upper()} => Self::${to_camel_case(value)},\n                % endfor\n                _ => panic!(\"Found unexpected value in style struct for ${property.name} property\"),\n            }\n        }\n    }\n    % endif\n    % if property.vector:\n    } // single_value\n    % endif\n\n    // The setup here is roughly:\n    //\n    //  * UnderlyingList is the list that is stored in the computed value. This may\n    //    be a shared ArcSlice if the property is inherited.\n    //  * UnderlyingOwnedList is the list that is used for animation.\n    //  * Specified values always use OwnedSlice, since it's more compact.\n    //  * computed_value::List is just a convenient alias that you can use for the\n    //    computed value list, since this is in the computed_value module.\n    //\n    // If vector.simple_bindings is true, then we don't use the complex iterator\n    // machinery and set_foo_from, and just compute the value like any other\n    // longhand.\n    % if property.vector:\n    <% allow_empty = not property.initial_value and not property.keyword %>\n    #[allow(unused_imports)]\n    use smallvec::SmallVec;\n\n    /// The definition of the computed value for ${property.name}.\n    pub mod computed_value {\n        #[allow(unused_imports)]\n        use crate::values::animated::ToAnimatedValue;\n        #[allow(unused_imports)]\n        use crate::values::resolved::ToResolvedValue;\n        #[allow(unused_imports)]\n        use crate::derives::*;\n        pub use super::single_value::computed_value as single_value;\n        pub use self::single_value::T as SingleComputedValue;\n        % if not allow_empty:\n        use smallvec::SmallVec;\n        % endif\n        use crate::values::computed::ComputedVecIter;\n\n        <% is_shared_list = allow_empty and property.style_struct.inherited %>\n\n        // FIXME(emilio): Add an OwnedNonEmptySlice type, and figure out\n        // something for transition-name, which is the only remaining user\n        // of NotInitial.\n        pub type UnderlyingList<T> =\n            % if allow_empty:\n            % if property.style_struct.inherited:\n                crate::ArcSlice<T>;\n            % else:\n                crate::OwnedSlice<T>;\n            % endif\n            % else:\n                SmallVec<[T; 1]>;\n            % endif\n\n        pub type UnderlyingOwnedList<T> =\n            % if allow_empty:\n                crate::OwnedSlice<T>;\n            % else:\n                SmallVec<[T; 1]>;\n            % endif\n\n\n        /// The generic type defining the animated and resolved values for\n        /// this property.\n        ///\n        /// Making this type generic allows the compiler to figure out the\n        /// animated value for us, instead of having to implement it\n        /// manually for every type we care about.\n        #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToResolvedValue, ToCss, ToTyped)]\n        % if property.vector.separator == \"Comma\":\n        #[css(comma)]\n        % endif\n        pub struct OwnedList<T>(\n            % if not allow_empty:\n            #[css(iterable)]\n            % else:\n            #[css(if_empty = \"none\", iterable)]\n            % endif\n            pub UnderlyingOwnedList<T>,\n        );\n\n        /// The computed value for this property.\n        % if not is_shared_list:\n        pub type ComputedList = OwnedList<single_value::T>;\n        pub use self::OwnedList as List;\n        % else:\n        pub use self::ComputedList as List;\n\n        #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToTyped)]\n        % if property.vector.separator == \"Comma\":\n        #[css(comma)]\n        % endif\n        pub struct ComputedList(\n            % if not allow_empty:\n            #[css(iterable)]\n            % else:\n            #[css(if_empty = \"none\", iterable)]\n            % endif\n            % if is_shared_list:\n            #[ignore_malloc_size_of = \"Arc\"]\n            % endif\n            pub UnderlyingList<single_value::T>,\n        );\n\n        type ResolvedList = <OwnedList<single_value::T> as ToResolvedValue>::ResolvedValue;\n        impl ToResolvedValue for ComputedList {\n            type ResolvedValue = ResolvedList;\n\n            fn to_resolved_value(self, context: &crate::values::resolved::Context) -> Self::ResolvedValue {\n                OwnedList(\n                    self.0\n                        .iter()\n                        .cloned()\n                        .map(|v| v.to_resolved_value(context))\n                        .collect()\n                )\n            }\n\n            fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n                % if not is_shared_list:\n                use std::iter::FromIterator;\n                % endif\n                let iter =\n                    resolved.0.into_iter().map(ToResolvedValue::from_resolved_value);\n                ComputedList(UnderlyingList::from_iter(iter))\n            }\n        }\n        % endif\n\n        % if property.vector.simple_bindings:\n        impl From<ComputedList> for UnderlyingList<single_value::T> {\n            #[inline]\n            fn from(l: ComputedList) -> Self {\n                l.0\n            }\n        }\n        impl From<UnderlyingList<single_value::T>> for ComputedList {\n            #[inline]\n            fn from(l: UnderlyingList<single_value::T>) -> Self {\n                List(l)\n            }\n        }\n        % endif\n\n        % if property.vector.animation_type:\n        use crate::values::animated::{Animate, ToAnimatedZero, Procedure, lists};\n        use crate::values::distance::{SquaredDistance, ComputeSquaredDistance};\n\n        // FIXME(emilio): For some reason rust thinks that this alias is\n        // unused, even though it's clearly used below?\n        #[allow(unused)]\n        type AnimatedList = <OwnedList<single_value::T> as ToAnimatedValue>::AnimatedValue;\n        % if is_shared_list:\n        impl ToAnimatedValue for ComputedList {\n            type AnimatedValue = AnimatedList;\n\n            fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {\n                OwnedList(\n                    self.0.iter().map(|v| v.clone().to_animated_value(context)).collect()\n                )\n            }\n\n            fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n                let iter =\n                    animated.0.into_iter().map(ToAnimatedValue::from_animated_value);\n                ComputedList(UnderlyingList::from_iter(iter))\n            }\n        }\n        % endif\n\n        impl ToAnimatedZero for AnimatedList {\n            fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }\n        }\n\n        impl Animate for AnimatedList {\n            fn animate(\n                &self,\n                other: &Self,\n                procedure: Procedure,\n            ) -> Result<Self, ()> {\n                Ok(OwnedList(\n                    lists::${property.vector.animation_type}::animate(&self.0, &other.0, procedure)?\n                ))\n            }\n        }\n        impl ComputeSquaredDistance for AnimatedList {\n            fn compute_squared_distance(\n                &self,\n                other: &Self,\n            ) -> Result<SquaredDistance, ()> {\n                lists::${property.vector.animation_type}::squared_distance(&self.0, &other.0)\n            }\n        }\n        % endif\n\n        /// The computed value, effectively a list of single values.\n        pub use self::ComputedList as T;\n\n        pub type Iter<'a, 'cx, 'cx_a> = ComputedVecIter<'a, 'cx, 'cx_a, super::single_value::SpecifiedValue>;\n    }\n\n    /// The specified value of ${property.name}.\n    #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\n    % if property.vector.none_value:\n    #[value_info(other_values = \"none\")]\n    % endif\n    % if property.vector.separator == \"Comma\":\n    #[css(comma)]\n    % endif\n    pub struct SpecifiedValue(\n        % if not allow_empty:\n        #[css(iterable)]\n        % else:\n        #[css(if_empty = \"none\", iterable)]\n        % endif\n        pub crate::OwnedSlice<single_value::SpecifiedValue>,\n    );\n\n    pub fn get_initial_value() -> computed_value::T {\n        % if allow_empty:\n            computed_value::List(Default::default())\n        % else:\n            let mut v = SmallVec::new();\n            v.push(single_value::get_initial_value());\n            computed_value::List(v)\n        % endif\n    }\n\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<SpecifiedValue, ParseError<'i>> {\n        use style_traits::Separator;\n\n        % if allow_empty or property.vector.none_value:\n        if input.try_parse(|input| input.expect_ident_matching(\"none\")).is_ok() {\n            % if allow_empty:\n            return Ok(SpecifiedValue(Default::default()))\n            % else:\n            return Ok(SpecifiedValue(crate::OwnedSlice::from(vec![${property.vector.none_value}])))\n            % endif\n        }\n        % endif\n\n        let v = style_traits::${property.vector.separator}::parse(input, |parser| {\n            single_value::parse(context, parser)\n        })?;\n        Ok(SpecifiedValue(v.into()))\n    }\n\n    pub use self::single_value::SpecifiedValue as SingleSpecifiedValue;\n\n    % if not property.vector.simple_bindings and engine == \"gecko\":\n    impl SpecifiedValue {\n        fn compute_iter<'a, 'cx, 'cx_a>(\n            &'a self,\n            context: &'cx computed::Context<'cx_a>,\n        ) -> computed_value::Iter<'a, 'cx, 'cx_a> {\n            computed_value::Iter::new(context, &self.0)\n        }\n    }\n    % endif\n\n    impl ToComputedValue for SpecifiedValue {\n        type ComputedValue = computed_value::T;\n\n        #[inline]\n        fn to_computed_value(&self, context: &computed::Context) -> computed_value::T {\n            % if not is_shared_list:\n            use std::iter::FromIterator;\n            % endif\n            computed_value::List(computed_value::UnderlyingList::from_iter(\n                self.0.iter().map(|i| i.to_computed_value(context))\n            ))\n        }\n\n        #[inline]\n        fn from_computed_value(computed: &computed_value::T) -> Self {\n            let iter = computed.0.iter().map(ToComputedValue::from_computed_value);\n            SpecifiedValue(iter.collect())\n        }\n    }\n    % endif\n}\n</%def>\n\n<%def name=\"shorthand(shorthand)\">\n    /// ${shorthand.spec}\n    pub mod ${shorthand.ident} {\n        #[allow(unused_imports)]\n        use crate::derives::*;\n        use crate::parser::ParserContext;\n        use crate::properties::{PropertyDeclaration, SourcePropertyDeclaration, longhands};\n        use cssparser::Parser;\n        #[allow(unused_imports)]\n        use std::fmt::{self, Write};\n        use style_traits::{CssWriter, ToCss, ParseError};\n\n        % if shorthand.derive_value_info:\n        #[derive(SpecifiedValueInfo)]\n        % endif\n        pub struct Longhands {\n            % for sub_property in shorthand.sub_properties:\n                pub ${sub_property.ident}:\n                    % if sub_property.boxed:\n                        Box<\n                    % endif\n                    longhands::${sub_property.ident}::SpecifiedValue\n                    % if sub_property.boxed:\n                        >\n                    % endif\n                    ,\n            % endfor\n        }\n\n        /// Represents a serializable set of all of the longhand properties that\n        /// correspond to a shorthand.\n        % if shorthand.derive_serialize:\n        #[derive(ToCss)]\n        % endif\n        pub struct LonghandsToSerialize<'a> {\n            % for sub_property in shorthand.sub_properties:\n                pub ${sub_property.ident}:\n                % if sub_property.may_be_disabled_in(shorthand, engine):\n                    Option<\n                % endif\n                    &'a longhands::${sub_property.ident}::SpecifiedValue,\n                % if sub_property.may_be_disabled_in(shorthand, engine):\n                    >,\n                % endif\n            % endfor\n        }\n\n        impl<'a> LonghandsToSerialize<'a> {\n            /// Tries to get a serializable set of longhands given a set of\n            /// property declarations.\n            pub fn from_iter(iter: impl Iterator<Item = &'a PropertyDeclaration>) -> Result<Self, ()> {\n                // Define all of the expected variables that correspond to the shorthand\n                % for sub_property in shorthand.sub_properties:\n                    let mut ${sub_property.ident} =\n                        None::<&'a longhands::${sub_property.ident}::SpecifiedValue>;\n                % endfor\n\n                // Attempt to assign the incoming declarations to the expected variables\n                for declaration in iter {\n                    match *declaration {\n                        % for sub_property in shorthand.sub_properties:\n                            PropertyDeclaration::${sub_property.camel_case}(ref value) => {\n                                ${sub_property.ident} = Some(value)\n                            },\n                        % endfor\n                        _ => {}\n                    };\n                }\n\n                // If any of the expected variables are missing, return an error\n                match (\n                    % for sub_property in shorthand.sub_properties:\n                        ${sub_property.ident},\n                    % endfor\n                ) {\n\n                    (\n                    % for sub_property in shorthand.sub_properties:\n                        % if sub_property.may_be_disabled_in(shorthand, engine):\n                        ${sub_property.ident},\n                        % else:\n                        Some(${sub_property.ident}),\n                        % endif\n                    % endfor\n                    ) =>\n                    Ok(LonghandsToSerialize {\n                        % for sub_property in shorthand.sub_properties:\n                            ${sub_property.ident},\n                        % endfor\n                    }),\n                    _ => Err(())\n                }\n            }\n        }\n\n        /// Parse the given shorthand and fill the result into the\n        /// `declarations` vector.\n        pub fn parse_into<'i, 't>(\n            declarations: &mut SourcePropertyDeclaration,\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n        ) -> Result<(), ParseError<'i>> {\n            #[allow(unused_imports)]\n            use crate::properties::{NonCustomPropertyId, LonghandId};\n            % if not shorthand.kind:\n            use crate::properties::shorthands::${shorthand.ident}::parse_value;\n            % endif\n            input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {\n                % for sub_property in shorthand.sub_properties:\n                % if sub_property.may_be_disabled_in(shorthand, engine):\n                if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case})\n                    .allowed_in_ignoring_rule_type(context) {\n                % endif\n                    declarations.push(PropertyDeclaration::${sub_property.camel_case}(\n                        longhands.${sub_property.ident}\n                    ));\n                % if sub_property.may_be_disabled_in(shorthand, engine):\n                }\n                % endif\n                % endfor\n            })\n        }\n\n        /// Try to serialize a given shorthand to a string.\n        pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut style_traits::CssStringWriter) -> fmt::Result {\n            match LonghandsToSerialize::from_iter(declarations.iter().cloned()) {\n                Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)),\n                Err(_) => Ok(())\n            }\n        }\n        % if shorthand.kind == \"two_properties\":\n        ${self.two_properties_shorthand(shorthand)}\n        % endif\n        % if shorthand.kind == \"four_sides\":\n        ${self.four_sides_shorthand(shorthand)}\n        % endif\n        % if shorthand.kind == \"single_border\":\n        ${self.single_border_shorthand(shorthand)}\n        % endif\n    }\n</%def>\n\n// A shorthand of kind `<property-1> <property-2>?` where both properties have\n// the same type.\n<%def name=\"two_properties_shorthand(shorthand)\">\n    type Single = crate::properties::longhands::${shorthand.sub_properties[0].ident}::SpecifiedValue;\n\n    fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let first = <Single as crate::parser::Parse>::parse(context, input)?;\n        let second =\n            input.try_parse(|input| <Single as crate::parser::Parse>::parse(context, input)).unwrap_or_else(|_| first.clone());\n        Ok(crate::properties::shorthands::expanded! {\n            ${shorthand.sub_properties[0].ident}: first,\n            ${shorthand.sub_properties[1].ident}: second,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a>  {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {\n            let first = &self.${shorthand.sub_properties[0].ident};\n            let second = &self.${shorthand.sub_properties[1].ident};\n\n            first.to_css(dest)?;\n            if first != second {\n                dest.write_char(' ')?;\n                second.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n</%def>\n\n<%def name=\"four_sides_shorthand(shorthand)\">\n    use crate::values::generics::rect::Rect;\n\n    type Single = crate::properties::longhands::${shorthand.sub_properties[0].ident}::SpecifiedValue;\n    fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let rect = Rect::parse_with(context, input, |c, i| -> Result<Single, ParseError<'i> > {\n        % if shorthand.allow_quirks:\n            Single::parse_quirky(c, i, crate::values::specified::AllowQuirks::Yes)\n        % else:\n            <Single as crate::parser::Parse>::parse(c, i)\n        % endif\n        })?;\n        Ok(crate::properties::shorthands::expanded! {\n        % for index in range(4):\n            ${shorthand.sub_properties[index].ident}: rect.${index},\n        % endfor\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let rect = Rect::new(\n                % for index in range(4):\n                    &self.${shorthand.sub_properties[index].ident},\n                % endfor\n            );\n            rect.to_css(dest)\n        }\n    }\n</%def>\n\n<%def name=\"single_border_shorthand(shorthand)\">\n    fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let (width, style, color) = crate::properties::shorthands::parse_border(context, input)?;\n        Ok(crate::properties::shorthands::expanded! {\n            ${shorthand.sub_properties[0].ident}: width,\n            ${shorthand.sub_properties[1].ident}: style,\n            ${shorthand.sub_properties[2].ident}: color,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a>  {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {\n            crate::properties::shorthands::serialize_directional_border(\n                dest,\n                % for i in range(3):\n                self.${shorthand.sub_properties[i].ident},\n                % endfor\n            )\n        }\n    }\n</%def>\n"
  },
  {
    "path": "style/properties/longhands.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# CSS Longhand Properties defined via predefined_type\n#\n# This file contains property definitions that are processed by the build system\n# to generate the property module code.\n#\n# Each section `[property-name]` defines a longhand property with the following fields:\n#\n# Required fields:\n#   type                - The Rust type name (e.g., \"Display\", \"Color\")\n#   struct              - Which style struct this belongs to (\"box\", \"font\", \"position\", etc.)\n#   engine              - If this property is specific to one engine, the engine that it applies to\n#   spec                - URL to the specification\n#   affects             - What does this property affect? (\"layout\", \"overflow\", \"paint\", \"\")\n#\n# Optional fields:\n#   initial                     - The computed initial value expression\n#   keyword                     - Description of the keyword.\n#   initial_specified_value     - Initial specified value (if different from initial)\n#   animation_type              - \"none\", \"normal\", or \"discrete\" (default: \"normal\")\n#   parse_method                - Parse method name (default: \"parse\")\n#   allow_quirks                - true/false (default: false)\n#   boxed                       - true/false - whether to box the value\n#   vector                      - A dictionary with:\n#     animation_type  - repeatable_list / with_zero / none\n#     simple_bindings - true/false\n#     need_index      - true/false\n#     separator       - \"Comma\" or \"Space\" (for vector properties, default comma)\n#   keyword                     - A dictionary with:\n#     values                 - Space separated list of valid values\n#     extra_gecko_values\n#     gecko_enum_prefix\n#     gecko_constant_prefix\n#     custom_consts - map with name -> enum mapping\n#     gecko_inexhaustive - Whether the gecko enum doesn't cover all keywords\n#   gecko_pref                  - Gecko preference name\n#   servo_pref                  - Servo preference name\n#   gecko_ffi_name              - Gecko FFI name (defaults to \"m\" + CamelCase)\n#   enabled_in                  - \"content\", \"chrome\", \"ua\", or \"\"\n#   logical                     - true/false - whether this is a logical property\n#   logical_group               - Logical property group name\n#   flags                       - Space-separated flags (e.g., \"CAN_ANIMATE_ON_COMPOSITOR\")\n#   aliases                     - Property aliases\n#   extra_gecko_aliases         - Additional property aliases for Gecko, not for Servo.\n#   extra_prefixes              - Vendor prefixes\n#   ignored_when_colors_disabled - true/false\n#   has_effect_on_gecko_scrollbars - true/false\n#   rule_types_allowed          - Which rule types allow this property\n#   servo_restyle_damage        - Valid values are \"repaint\", \"rebuild_stacking_context\", \"recalculate_overflow\", \"rebuild_box\".\n\n[-moz-box-flex]\ntype = \"NonNegativeNumber\"\ninitial = \"From::from(0.)\"\nstruct = \"xul\"\nengine = \"gecko\"\ngecko_ffi_name = \"mBoxFlex\"\naliases = [\"-webkit-box-flex\"]\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)\"\naffects = \"layout\"\n\n# NOTE(heycam): Odd that the initial value is 1 yet 0 is a valid value.\n[-moz-box-ordinal-group]\ntype = \"Integer\"\ninitial = \"1\"\nstruct = \"xul\"\nengine = \"gecko\"\nparse_method = \"parse_non_negative\"\naliases = [\"-webkit-box-ordinal-group\"]\ngecko_ffi_name = \"mBoxOrdinal\"\nanimation_type = \"discrete\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)\"\naffects = \"layout\"\n\n[-moz-default-appearance]\ntype = \"Appearance\"\ninitial = \"computed::Appearance::None\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"none\"\nspec = \"Internal (not web-exposed)\"\nenabled_in = \"chrome\"\ngecko_ffi_name = \"mDefaultAppearance\"\naffects = \"paint\"\n\n[-moz-min-font-size-ratio]\ntype = \"Percentage\"\ninitial = \"computed::Percentage::hundred()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"none\"\ngecko_ffi_name = \"mMinFontSizeRatio\"\nenabled_in = \"ua\"\nspec = \"Nonstandard (Internal-only)\"\naffects = \"layout\"\n\n[-webkit-line-clamp]\ntype = \"LineClamp\"\ninitial = \"computed::LineClamp::none()\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#line-clamp\"\naffects = \"layout\"\n\n[-x-lang]\ntype = \"XLang\"\ninitial = \"computed::XLang::get_initial_value()\"\nstruct = \"font\"\nanimation_type = \"none\"\nenabled_in = \"\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"Internal (not web-exposed)\"\naffects = \"layout\"\n\n[-x-text-scale]\ntype = \"XTextScale\"\ninitial = \"computed::XTextScale::All\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"none\"\nenabled_in = \"\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"Internal (not web-exposed)\"\naffects = \"layout\"\n\n[accent-color]\ntype = \"ColorOrAuto\"\ninitial = \"generics::color::ColorOrAuto::Auto\"\nstruct = \"inherited_ui\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-ui-4/#widget-accent\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[align-content]\ntype = \"ContentDistribution\"\ninitial = \"specified::ContentDistribution::normal()\"\nstruct = \"position\"\nparse_method = \"parse_block\"\nspec = \"https://drafts.csswg.org/css-align/#propdef-align-content\"\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[align-items]\ntype = \"ItemPlacement\"\ninitial = \"specified::ItemPlacement::normal()\"\nstruct = \"position\"\nparse_method = \"parse_block\"\nspec = \"https://drafts.csswg.org/css-align/#propdef-align-items\"\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[align-self]\ntype = \"SelfAlignment\"\ninitial = \"specified::SelfAlignment::auto()\"\nstruct = \"position\"\nparse_method = \"parse_block\"\nspec = \"https://drafts.csswg.org/css-align/#align-self-property\"\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\naffects = \"layout\"\n\n[alignment-baseline]\ntype = \"AlignmentBaseline\"\ninitial = \"computed::AlignmentBaseline::Baseline\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-inline-3/#alignment-baseline\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[anchor-name]\ntype = \"AnchorName\"\ninitial = \"computed::AnchorName::none()\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#propdef-anchor-name\"\naffects = \"\"\n\n[anchor-scope]\ntype = \"ScopedName\"\ninitial = \"computed::ScopedName::none()\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#propdef-scope\"\naffects = \"\"\n\n[animation-delay]\ntype = \"Time\"\ninitial = \"computed::Time::zero()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-delay\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-direction]\ntype = \"AnimationDirection\"\ninitial = \"computed::AnimationDirection::Normal\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-direction\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-duration]\ntype = \"AnimationDuration\"\ninitial = \"computed::AnimationDuration::auto()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-animations-2/#animation-duration\"\naffects = \"\"\n\n[animation-fill-mode]\ntype = \"AnimationFillMode\"\ninitial = \"computed::AnimationFillMode::None\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-iteration-count]\ntype = \"AnimationIterationCount\"\ninitial = \"computed::AnimationIterationCount::one()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nrule_types_allowed = [\"style\", \"scope\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count\"\naffects = \"\"\n\n[animation-name]\ntype = \"AnimationName\"\ninitial = \"computed::AnimationName::none()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nrule_types_allowed = [\"style\", \"scope\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-name\"\naffects = \"\"\n\n[animation-play-state]\ntype = \"AnimationPlayState\"\ninitial = \"computed::AnimationPlayState::Running\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation-play-state\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-range-end]\ntype = \"AnimationRangeEnd\"\ninitial = \"computed::AnimationRangeEnd::normal()\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#animation-range-end\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-range-start]\ntype = \"AnimationRangeStart\"\ninitial = \"computed::AnimationRangeStart::normal()\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#animation-range-start\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-timeline]\ntype = \"AnimationTimeline\"\ninitial = \"computed::AnimationTimeline::auto()\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/css-animations-2/#propdef-animation-timeline\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[animation-timing-function]\ntype = \"TimingFunction\"\ninitial = \"computed::TimingFunction::ease()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function\"\naffects = \"\"\n\n[appearance]\ntype = \"Appearance\"\ninitial = \"computed::Appearance::None\"\nstruct = \"box\"\nengine = \"gecko\"\nextra_prefixes = [\"moz:layout.css.moz-appearance.enabled\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-ui-4/#propdef-appearance\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mAppearance\"\naffects = \"paint\"\n\n[aspect-ratio]\ntype = \"AspectRatio\"\ninitial = \"computed::AspectRatio::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-sizing-4/#aspect-ratio\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[backdrop-filter]\ntype = \"Filter\"\nstruct = \"effects\"\nvector = { simple_bindings = true, separator = \"Space\", animation_type = \"with_zero\" }\ngecko_ffi_name = \"mBackdropFilters\"\ngecko_pref = \"layout.css.backdrop-filter.enabled\"\nservo_pref = \"layout.unimplemented\"\nspec = \"https://drafts.fxtf.org/filter-effects-2/#propdef-backdrop-filter\"\naffects = \"overflow\"\n\n[background-color]\ntype = \"Color\"\ninitial = \"computed::Color::TRANSPARENT_BLACK\"\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#background-color\"\nignored_when_colors_disabled = true\nallow_quirks = true\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[background-image]\ntype = \"Image\"\ninitial = \"computed::Image::None\"\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-image\"\nvector = {}\nanimation_type = \"discrete\"\nignored_when_colors_disabled = true\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[background-position-x]\ntype = \"position::HorizontalPosition\"\ninitial = \"computed::LengthPercentage::zero_percent()\"\nstruct = \"background\"\ninitial_specified_value = \"SpecifiedValue::initial_specified_value()\"\nspec = \"https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-x\"\nvector = { animation_type = \"repeatable_list\" }\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[background-position-y]\ntype = \"position::VerticalPosition\"\ninitial = \"computed::LengthPercentage::zero_percent()\"\nstruct = \"background\"\ninitial_specified_value = \"SpecifiedValue::initial_specified_value()\"\nspec = \"https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-y\"\nvector = { animation_type = \"repeatable_list\" }\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[background-repeat]\ntype = \"BackgroundRepeat\"\ninitial = \"computed::BackgroundRepeat::repeat()\"\nstruct = \"background\"\nanimation_type = \"discrete\"\nvector = {}\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-repeat\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[background-size]\ntype = \"BackgroundSize\"\ninitial = \"computed::BackgroundSize::auto()\"\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-size\"\nvector = { animation_type = \"repeatable_list\" }\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[baseline-shift]\ntype = \"BaselineShift\"\ninitial = \"computed::BaselineShift::zero()\"\nstruct = \"box\"\nspec = \"https://drafts.csswg.org/css-inline-3/#baseline-shift\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[baseline-source]\ntype = \"BaselineSource\"\ninitial = \"computed::BaselineSource::Auto\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-inline-3/#baseline-source\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[block-size]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"size\"\nparse_method = \"parse_size_for_width_or_height\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-block-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-spacing]\ntype = \"BorderSpacing\"\ninitial = \"computed::BorderSpacing::zero()\"\nstruct = \"inherited_table\"\nboxed = true\nspec = \"https://drafts.csswg.org/css-tables/#propdef-border-spacing\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[bottom]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://www.w3.org/TR/CSS2/visuren.html#propdef-bottom\"\nallow_quirks = true\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_ffi_name = \"mOffset.2\"\nservo_restyle_damage = \"rebuild_box\"\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[box-shadow]\ntype = \"BoxShadow\"\nstruct = \"effects\"\nvector = { simple_bindings = true, animation_type = \"with_zero\" }\nextra_prefixes = [\"webkit\"]\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-backgrounds/#box-shadow\"\naffects = \"overflow\"\n\n[break-after]\ntype = \"BreakBetween\"\ninitial = \"computed::BreakBetween::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-break/#propdef-break-after\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[break-before]\ntype = \"BreakBetween\"\ninitial = \"computed::BreakBetween::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-break/#propdef-break-before\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[break-inside]\ntype = \"BreakWithin\"\ninitial = \"computed::BreakWithin::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-break/#propdef-break-inside\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[caption-side]\ntype = \"table::CaptionSide\"\ninitial = \"computed::table::CaptionSide::Top\"\nstruct = \"inherited_table\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-tables/#propdef-caption-side\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[caret-color]\ntype = \"color::CaretColor\"\ninitial = \"generics::color::CaretColor::auto()\"\nstruct = \"inherited_ui\"\nspec = \"https://drafts.csswg.org/css-ui/#caret-color\"\nignored_when_colors_disabled = true\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[clear]\ntype = \"Clear\"\ninitial = \"computed::Clear::None\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css2/#propdef-clear\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[clip]\ntype = \"ClipRectOrAuto\"\ninitial = \"computed::ClipRectOrAuto::auto()\"\nstruct = \"effects\"\nboxed = true\nallow_quirks = true\nspec = \"https://drafts.fxtf.org/css-masking/#clip-property\"\naffects = \"overflow\"\n\n[clip-path]\ntype = \"basic_shape::ClipPath\"\ninitial = \"generics::basic_shape::ClipPath::None\"\nstruct = \"svg\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-clip-path\"\naffects = \"paint\"\nservo_restyle_damage = \"repaint\"\n\n[clip-rule]\ntype = \"FillRule\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/masking.html#ClipRuleProperty\"\naffects = \"paint\"\n\n[color]\ntype = \"ColorPropertyValue\"\ninitial = \"crate::color::AbsoluteColor::BLACK\"\nstruct = \"inherited_text\"\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-color/#color\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[color-scheme]\ntype = \"ColorScheme\"\ninitial = \"specified::color::ColorScheme::normal()\"\nstruct = \"inherited_ui\"\nservo_pref = \"layout.unimplemented\"\nspec = \"https://drafts.csswg.org/css-color-adjust/#color-scheme-prop\"\nanimation_type = \"discrete\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[column-count]\ntype = \"ColumnCount\"\ninitial = \"computed::ColumnCount::Auto\"\nstruct = \"column\"\nservo_pref = \"layout.columns.enabled\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-count\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[column-gap]\ntype = \"length::NonNegativeLengthPercentageOrNormal\"\ninitial = \"computed::length::NonNegativeLengthPercentageOrNormal::normal()\"\nstruct = \"position\"\naliases = [\"grid-column-gap\"]\nspec = \"https://drafts.csswg.org/css-align-3/#propdef-column-gap\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[column-rule-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"column\"\nengine = \"gecko\"\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-rule-color\"\naffects = \"paint\"\n\n[column-rule-style]\ntype = \"BorderStyle\"\ninitial = \"computed::BorderStyle::None\"\nstruct = \"column\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-rule-style\"\naffects = \"paint\"\n\n[column-rule-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"column\"\nengine = \"gecko\"\ninitial_specified_value = \"specified::BorderSideWidth::medium()\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-rule-width\"\naffects = \"layout\"\n\n[column-width]\ntype = \"length::NonNegativeLengthOrAuto\"\ninitial = \"computed::length::NonNegativeLengthOrAuto::auto()\"\nstruct = \"column\"\nservo_pref = \"layout.columns.enabled\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-width\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[contain]\ntype = \"Contain\"\ninitial = \"specified::Contain::empty()\"\nstruct = \"box\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"none\"\nspec = \"https://drafts.csswg.org/css-contain/#contain-property\"\naffects = \"layout\"\n\n[contain-intrinsic-block-size]\ntype = \"ContainIntrinsicSize\"\ninitial = \"computed::ContainIntrinsicSize::None\"\nstruct = \"position\"\nengine = \"gecko\"\nlogical_group = \"contain-intrinsic-size\"\nlogical = true\nspec = \"https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\"\naffects = \"layout\"\n\n[contain-intrinsic-height]\ntype = \"ContainIntrinsicSize\"\ninitial = \"computed::ContainIntrinsicSize::None\"\nstruct = \"position\"\nengine = \"gecko\"\nlogical_group = \"contain-intrinsic-size\"\nspec = \"https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\"\naffects = \"layout\"\n\n[contain-intrinsic-inline-size]\ntype = \"ContainIntrinsicSize\"\ninitial = \"computed::ContainIntrinsicSize::None\"\nstruct = \"position\"\nengine = \"gecko\"\nlogical_group = \"contain-intrinsic-size\"\nlogical = true\nspec = \"https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\"\naffects = \"layout\"\n\n[contain-intrinsic-width]\ntype = \"ContainIntrinsicSize\"\ninitial = \"computed::ContainIntrinsicSize::None\"\nstruct = \"position\"\nengine = \"gecko\"\nlogical_group = \"contain-intrinsic-size\"\nspec = \"https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\"\naffects = \"layout\"\n\n[container-name]\ntype = \"ContainerName\"\ninitial = \"computed::ContainerName::none()\"\nstruct = \"box\"\nanimation_type = \"none\"\nservo_pref = \"layout.container-queries.enabled\"\nspec = \"https://drafts.csswg.org/css-contain-3/#container-name\"\naffects = \"\"\n\n[container-type]\ntype = \"ContainerType\"\ninitial = \"computed::ContainerType::NORMAL\"\nstruct = \"box\"\nanimation_type = \"none\"\nservo_pref = \"layout.container-queries.enabled\"\nspec = \"https://drafts.csswg.org/css-contain-3/#container-type\"\naffects = \"layout\"\n\n[content]\ntype = \"Content\"\ninitial = \"computed::Content::normal()\"\nstruct = \"counters\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-content/#propdef-content\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[content-visibility]\ntype = \"ContentVisibility\"\ninitial = \"computed::ContentVisibility::Visible\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-contain/#content-visibility\"\naffects = \"layout\"\n\n[counter-increment]\ntype = \"CounterIncrement\"\ninitial = \"Default::default()\"\nstruct = \"counters\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-lists/#propdef-counter-increment\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[counter-reset]\ntype = \"CounterReset\"\ninitial = \"Default::default()\"\nstruct = \"counters\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-lists-3/#propdef-counter-reset\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[counter-set]\ntype = \"CounterSet\"\ninitial = \"Default::default()\"\nstruct = \"counters\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-lists-3/#propdef-counter-set\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[cursor]\ntype = \"Cursor\"\ninitial = \"computed::Cursor::auto()\"\nstruct = \"inherited_ui\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-ui/#cursor\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[d]\ntype = \"DProperty\"\ninitial = \"specified::DProperty::none()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/paths.html#TheDProperty\"\naffects = \"layout\"\n\n[display]\ntype = \"Display\"\ninitial = \"computed::Display::inline()\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-display/#propdef-display\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[fill]\ntype = \"SVGPaint\"\ninitial = \"crate::values::computed::SVGPaint::BLACK\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nboxed = true\nspec = \"https://svgwg.org/svg2-draft/painting.html#SpecifyingFillPaint\"\naffects = \"paint\"\n\n[fill-opacity]\ntype = \"SVGOpacity\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#FillOpacity\"\naffects = \"paint\"\n\n[fill-rule]\ntype = \"FillRule\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#FillRuleProperty\"\naffects = \"paint\"\n\n[filter]\ntype = \"Filter\"\nstruct = \"effects\"\nvector = { simple_bindings = true, separator = \"Space\", animation_type = \"with_zero\" }\ngecko_ffi_name = \"mFilters\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.fxtf.org/filters/#propdef-filter\"\naffects = \"overflow\"\nservo_restyle_damage = \"repaint\"\n\n[flex-basis]\ntype = \"FlexBasis\"\ninitial = \"computed::FlexBasis::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-basis-property\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\nboxed = true\naffects = \"layout\"\n\n[flex-grow]\ntype = \"NonNegativeNumber\"\ninitial = \"From::from(0.0)\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-grow-property\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[flex-shrink]\ntype = \"NonNegativeNumber\"\ninitial = \"From::from(1.0)\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-shrink-property\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[float]\ntype = \"Float\"\ninitial = \"computed::Float::None\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-float\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[flood-color]\ntype = \"Color\"\ninitial = \"computed::Color::BLACK\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/filter-effects-1/#FloodColorProperty\"\naffects = \"paint\"\n\n[flood-opacity]\ntype = \"Opacity\"\ninitial = \"1.0\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/filter-effects-1/#FloodOpacityProperty\"\naffects = \"paint\"\n\n[font-family]\ntype = \"FontFamily\"\ninitial = \"computed::FontFamily::serif()\"\nstruct = \"font\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-family\"\ngecko_ffi_name = \"mFont.family\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[font-feature-settings]\ntype = \"FontFeatureSettings\"\ninitial = \"computed::FontFeatureSettings::normal()\"\nstruct = \"font\"\nengine = \"gecko\"\nextra_prefixes = [\"moz:layout.css.prefixes.font-features\", \"webkit\"]\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-feature-settings\"\naffects = \"layout\"\n\n[font-language-override]\ntype = \"FontLanguageOverride\"\ninitial = \"computed::FontLanguageOverride::normal()\"\nstruct = \"font\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mFont.languageOverride\"\nextra_prefixes = [\"moz:layout.css.prefixes.font-features\"]\nspec = \"https://drafts.csswg.org/css-fonts-3/#propdef-font-language-override\"\naffects = \"layout\"\n\n[font-palette]\ntype = \"FontPalette\"\ninitial = \"computed::FontPalette::normal()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.font-palette.enabled\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"https://drafts.csswg.org/css-fonts/#font-palette-prop\"\naffects = \"layout\"\n\n[font-size]\ntype = \"FontSize\"\ninitial = \"computed::FontSize::medium()\"\nstruct = \"font\"\ninitial_specified_value = \"specified::FontSize::medium()\"\nallow_quirks = true\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-size\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[font-size-adjust]\ntype = \"FontSizeAdjust\"\ninitial = \"computed::FontSizeAdjust::None\"\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust\"\ngecko_ffi_name = \"mFont.sizeAdjust\"\naffects = \"layout\"\n\n[font-stretch]\ntype = \"FontStretch\"\ninitial = \"computed::FontStretch::hundred()\"\nstruct = \"font\"\ninitial_specified_value = \"specified::FontStretch::normal()\"\ngecko_ffi_name = \"mFont.stretch\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-stretch\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[font-style]\ntype = \"FontStyle\"\ninitial = \"computed::FontStyle::normal()\"\nstruct = \"font\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-style\"\ngecko_ffi_name = \"mFont.style\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[font-synthesis-position]\ntype = \"FontSynthesis\"\ninitial = \"computed::FontSynthesis::Auto\"\nstruct = \"font\"\nengine = \"gecko\"\ngecko_ffi_name = \"mFont.synthesisPosition\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts-4/#font-synthesis-position\"\naffects = \"layout\"\n\n[font-synthesis-small-caps]\ntype = \"FontSynthesis\"\ninitial = \"computed::FontSynthesis::Auto\"\nstruct = \"font\"\nengine = \"gecko\"\ngecko_ffi_name = \"mFont.synthesisSmallCaps\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts-4/#font-synthesis-small-caps\"\naffects = \"layout\"\n\n[font-synthesis-style]\ntype = \"FontSynthesisStyle\"\ninitial = \"computed::FontSynthesisStyle::Auto\"\nstruct = \"font\"\nengine = \"gecko\"\ngecko_ffi_name = \"mFont.synthesisStyle\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts-4/#font-synthesis-style\"\naffects = \"layout\"\n\n[font-synthesis-weight]\ntype = \"FontSynthesis\"\ninitial = \"computed::FontSynthesis::Auto\"\nstruct = \"font\"\ngecko_ffi_name = \"mFont.synthesisWeight\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts-4/#font-synthesis-weight\"\naffects = \"layout\"\n\n[font-variant-alternates]\ntype = \"FontVariantAlternates\"\ninitial = \"computed::FontVariantAlternates::default()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates\"\ngecko_ffi_name = \"mFont.variantAlternates\"\naffects = \"layout\"\n\n[font-variant-east-asian]\ntype = \"FontVariantEastAsian\"\ninitial = \"computed::FontVariantEastAsian::empty()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mFont.variantEastAsian\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian\"\naffects = \"layout\"\n\n[font-variant-ligatures]\ntype = \"FontVariantLigatures\"\ninitial = \"computed::FontVariantLigatures::empty()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mFont.variantLigatures\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures\"\naffects = \"layout\"\n\n[font-variant-numeric]\ntype = \"FontVariantNumeric\"\ninitial = \"computed::FontVariantNumeric::empty()\"\nstruct = \"font\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mFont.variantNumeric\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric\"\naffects = \"layout\"\n\n[font-variation-settings]\ntype = \"FontVariationSettings\"\ninitial = \"computed::FontVariationSettings::normal()\"\nstruct = \"font\"\nservo_pref = \"layout.variable_fonts.enabled\"\ngecko_pref = \"layout.css.font-variations.enabled\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"https://drafts.csswg.org/css-fonts-4/#propdef-font-variation-settings\"\naffects = \"layout\"\n\n[font-weight]\ntype = \"FontWeight\"\ninitial = \"computed::FontWeight::normal()\"\nstruct = \"font\"\ninitial_specified_value = \"specified::FontWeight::normal()\"\ngecko_ffi_name = \"mFont.weight\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-weight\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[grid-auto-columns]\ntype = \"ImplicitGridTracks\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-auto-columns\"\naffects = \"layout\"\n\n[grid-auto-flow]\ntype = \"GridAutoFlow\"\ninitial = \"computed::GridAutoFlow::ROW\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow\"\naffects = \"layout\"\n\n[grid-auto-rows]\ntype = \"ImplicitGridTracks\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-auto-rows\"\naffects = \"layout\"\n\n[grid-column-end]\ntype = \"GridLine\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-column-end\"\naffects = \"layout\"\n\n[grid-column-start]\ntype = \"GridLine\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-column-start\"\naffects = \"layout\"\n\n[grid-row-end]\ntype = \"GridLine\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-row-end\"\naffects = \"layout\"\n\n[grid-row-start]\ntype = \"GridLine\"\ninitial = \"Default::default()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-row-start\"\naffects = \"layout\"\n\n[grid-template-areas]\ntype = \"GridTemplateAreas\"\ninitial = \"computed::GridTemplateAreas::none()\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-template-areas\"\naffects = \"layout\"\n\n[grid-template-columns]\ntype = \"GridTemplateComponent\"\ninitial = \"specified::GenericGridTemplateComponent::None\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-template-columns\"\naffects = \"layout\"\n\n[grid-template-rows]\ntype = \"GridTemplateComponent\"\ninitial = \"specified::GenericGridTemplateComponent::None\"\nstruct = \"position\"\nservo_pref = \"layout.grid.enabled\"\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-template-rows\"\naffects = \"layout\"\n\n[height]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical_group = \"size\"\nallow_quirks = true\nparse_method = \"parse_size_for_width_or_height\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-height\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[image-rendering]\ntype = \"ImageRendering\"\ninitial = \"computed::ImageRendering::Auto\"\nstruct = \"inherited_box\"\nspec = \"https://drafts.csswg.org/css-images/#propdef-image-rendering\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[inline-size]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"size\"\nparse_method = \"parse_size_for_width_or_height\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inline-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[inset-block-end]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inset-block-end\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nlogical = true\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[inset-block-start]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inset-block-start\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nlogical = true\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[inset-inline-end]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inset-inline-end\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nlogical = true\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[inset-inline-start]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inset-inline-start\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nlogical = true\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[justify-content]\ntype = \"ContentDistribution\"\ninitial = \"specified::ContentDistribution::normal()\"\nstruct = \"position\"\nparse_method = \"parse_inline\"\nspec = \"https://drafts.csswg.org/css-align/#propdef-justify-content\"\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[justify-items]\ntype = \"JustifyItems\"\ninitial = \"computed::JustifyItems::legacy()\"\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-align/#propdef-justify-items\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[justify-self]\ntype = \"SelfAlignment\"\ninitial = \"specified::SelfAlignment::auto()\"\nstruct = \"position\"\nparse_method = \"parse_inline\"\nspec = \"https://drafts.csswg.org/css-align/#justify-self-property\"\nanimation_type = \"discrete\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\naffects = \"layout\"\n\n[left]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://www.w3.org/TR/CSS2/visuren.html#propdef-left\"\nallow_quirks = true\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_ffi_name = \"mOffset.3\"\nservo_restyle_damage = \"rebuild_box\"\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[letter-spacing]\ntype = \"LetterSpacing\"\ninitial = \"computed::LetterSpacing::normal()\"\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-letter-spacing\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[lighting-color]\ntype = \"Color\"\ninitial = \"computed::Color::WHITE\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/filter-effects-1#LightingColorProperty\"\naffects = \"paint\"\n\n[line-height]\ntype = \"LineHeight\"\ninitial = \"computed::LineHeight::normal()\"\nstruct = \"font\"\nspec = \"https://drafts.csswg.org/css2/visudet.html#propdef-line-height\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[list-style-image]\ntype = \"Image\"\ninitial = \"computed::Image::None\"\nstruct = \"list\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-lists/#propdef-list-style-image\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[list-style-type]\ntype = \"ListStyleType\"\ninitial = \"computed::ListStyleType::disc()\"\nstruct = \"list\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-lists/#propdef-list-style-type\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[marker-end]\ntype = \"url::UrlOrNone\"\ninitial = \"computed::url::UrlOrNone::none()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#VertexMarkerProperties\"\naffects = \"layout\"\n\n[marker-mid]\ntype = \"url::UrlOrNone\"\ninitial = \"computed::url::UrlOrNone::none()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#VertexMarkerProperties\"\naffects = \"layout\"\n\n[marker-start]\ntype = \"url::UrlOrNone\"\ninitial = \"computed::url::UrlOrNone::none()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#VertexMarkerProperties\"\naffects = \"layout\"\n\n[masonry-auto-flow]\ntype = \"MasonryAutoFlow\"\ninitial = \"computed::MasonryAutoFlow::initial()\"\nstruct = \"position\"\nengine = \"gecko\"\ngecko_pref = \"layout.css.grid-template-masonry-value.enabled\"\nanimation_type = \"discrete\"\nspec = \"https://github.com/w3c/csswg-drafts/issues/4650\"\naffects = \"layout\"\n\n[math-depth]\ntype = \"MathDepth\"\ninitial = \"0\"\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property\"\naffects = \"\"\n\n[max-block-size]\ntype = \"MaxSize\"\ninitial = \"computed::MaxSize::none()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"max-size\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-block-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[max-height]\ntype = \"MaxSize\"\ninitial = \"computed::MaxSize::none()\"\nstruct = \"position\"\nlogical_group = \"max-size\"\nallow_quirks = true\nspec = \"https://drafts.csswg.org/css-box/#propdef-height\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[max-inline-size]\ntype = \"MaxSize\"\ninitial = \"computed::MaxSize::none()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"max-size\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inline-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[max-width]\ntype = \"MaxSize\"\ninitial = \"computed::MaxSize::none()\"\nstruct = \"position\"\nlogical_group = \"max-size\"\nallow_quirks = true\nspec = \"https://drafts.csswg.org/css-box/#propdef-width\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[min-block-size]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"min-size\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-block-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[min-height]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical_group = \"min-size\"\nallow_quirks = true\nspec = \"https://drafts.csswg.org/css-box/#propdef-height\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[min-inline-size]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical = true\nlogical_group = \"min-size\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-inline-size\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[min-width]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical_group = \"min-size\"\nallow_quirks = true\nspec = \"https://drafts.csswg.org/css-box/#propdef-width\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[object-position]\ntype = \"Position\"\ninitial = \"computed::Position::center()\"\nstruct = \"position\"\nboxed = true\nspec = \"https://drafts.csswg.org/css-images-3/#the-object-position\"\naffects = \"layout\"\n\n[offset-anchor]\ntype = \"PositionOrAuto\"\ninitial = \"computed::PositionOrAuto::auto()\"\nstruct = \"box\"\nengine = \"gecko\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.fxtf.org/motion-1/#offset-anchor-property\"\nservo_restyle_damage = \"rebuild_box\"\nboxed = true\naffects = \"overflow\"\n\n[offset-distance]\ntype = \"LengthPercentage\"\ninitial = \"computed::LengthPercentage::zero()\"\nstruct = \"box\"\nengine = \"gecko\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.fxtf.org/motion-1/#offset-distance-property\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"overflow\"\n\n[offset-path]\ntype = \"OffsetPath\"\ninitial = \"computed::OffsetPath::none()\"\nstruct = \"box\"\nservo_pref = \"layout.unimplemented\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.fxtf.org/motion-1/#offset-path-property\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"overflow\"\n\n[offset-position]\ntype = \"OffsetPosition\"\ninitial = \"computed::OffsetPosition::normal()\"\nstruct = \"box\"\nengine = \"gecko\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.fxtf.org/motion-1/#offset-position-property\"\nservo_restyle_damage = \"rebuild_box\"\nboxed = true\naffects = \"overflow\"\n\n[offset-rotate]\ntype = \"OffsetRotate\"\ninitial = \"computed::OffsetRotate::auto()\"\nstruct = \"box\"\nengine = \"gecko\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.fxtf.org/motion-1/#offset-rotate-property\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"overflow\"\n\n[opacity]\ntype = \"Opacity\"\ninitial = \"1.0\"\nstruct = \"effects\"\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.csswg.org/css-color/#transparency\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[order]\ntype = \"Integer\"\ninitial = \"0\"\nstruct = \"position\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.csswg.org/css-flexbox/#order-property\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[outline-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"outline\"\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-ui/#propdef-outline-color\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[outline-offset]\ntype = \"BorderSideOffset\"\ninitial = \"app_units::Au(0)\"\nstruct = \"outline\"\nspec = \"https://drafts.csswg.org/css-ui/#propdef-outline-offset\"\nservo_restyle_damage = \"repaint\"\naffects = \"overflow\"\n\n[outline-style]\ntype = \"OutlineStyle\"\ninitial = \"computed::OutlineStyle::none()\"\nstruct = \"outline\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-ui/#propdef-outline-style\"\nservo_restyle_damage = \"repaint\"\naffects = \"overflow\"\n\n[outline-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"outline\"\ninitial_specified_value = \"specified::BorderSideWidth::medium()\"\nspec = \"https://drafts.csswg.org/css-ui/#propdef-outline-width\"\nservo_restyle_damage = \"repaint\"\naffects = \"overflow\"\n\n[overflow-anchor]\ntype = \"OverflowAnchor\"\ninitial = \"computed::OverflowAnchor::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\ngecko_pref = \"layout.css.scroll-anchoring.enabled\"\nspec = \"https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api\"\nanimation_type = \"discrete\"\naffects = \"\"\n\n[overflow-block]\ntype = \"Overflow\"\ninitial = \"computed::Overflow::Visible\"\nstruct = \"box\"\nlogical_group = \"overflow\"\nlogical = true\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#propdef-overflow-block\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[overflow-clip-margin]\ntype = \"OverflowClipMargin\"\ninitial = \"computed::OverflowClipMargin::zero()\"\nstruct = \"margin\"\nspec = \"https://drafts.csswg.org/css-overflow/#propdef-overflow-clip-margin\"\naffects = \"overflow\"\n\n[overflow-inline]\ntype = \"Overflow\"\ninitial = \"computed::Overflow::Visible\"\nstruct = \"box\"\nlogical_group = \"overflow\"\nlogical = true\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#propdef-overflow-inline\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[overflow-wrap]\ntype = \"OverflowWrap\"\ninitial = \"computed::OverflowWrap::Normal\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-overflow-wrap\"\naliases = [\"word-wrap\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[overflow-x]\ntype = \"Overflow\"\ninitial = \"computed::Overflow::Visible\"\nstruct = \"box\"\nlogical_group = \"overflow\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#propdef-overflow-x\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[overflow-y]\ntype = \"Overflow\"\ninitial = \"computed::Overflow::Visible\"\nstruct = \"box\"\nlogical_group = \"overflow\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#propdef-overflow-y\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[overscroll-behavior-block]\ntype = \"OverscrollBehavior\"\ninitial = \"computed::OverscrollBehavior::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nlogical_group = \"overscroll-behavior\"\nlogical = true\nspec = \"https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[overscroll-behavior-inline]\ntype = \"OverscrollBehavior\"\ninitial = \"computed::OverscrollBehavior::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nlogical_group = \"overscroll-behavior\"\nlogical = true\nspec = \"https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[overscroll-behavior-x]\ntype = \"OverscrollBehavior\"\ninitial = \"computed::OverscrollBehavior::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nlogical_group = \"overscroll-behavior\"\nspec = \"https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[overscroll-behavior-y]\ntype = \"OverscrollBehavior\"\ninitial = \"computed::OverscrollBehavior::Auto\"\nstruct = \"box\"\nengine = \"gecko\"\nlogical_group = \"overscroll-behavior\"\nspec = \"https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[page]\ntype = \"PageName\"\ninitial = \"computed::PageName::auto()\"\nstruct = \"page\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-page-3/#using-named-pages\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[page-orientation]\ntype = \"PageOrientation\"\ninitial = \"computed::PageOrientation::Upright\"\nstruct = \"page\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-page-3/#page-orientation-prop\"\nanimation_type = \"none\"\nrule_types_allowed = [\"page\"]\naffects = \"layout\"\n\n[paint-order]\ntype = \"SVGPaintOrder\"\ninitial = \"computed::SVGPaintOrder::normal()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#PaintOrder\"\naffects = \"paint\"\n\n[-moz-context-properties]\ntype = \"MozContextProperties\"\ninitial = \"computed::MozContextProperties::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nenabled_in = \"chrome\"\ngecko_pref = \"svg.context-properties.content.enabled\"\nhas_effect_on_gecko_scrollbars = false\nanimation_type = \"none\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)\"\naffects = \"paint\"\n\n[perspective]\ntype = \"Perspective\"\ninitial = \"computed::Perspective::none()\"\nstruct = \"box\"\ngecko_ffi_name = \"mChildPerspective\"\nspec = \"https://drafts.csswg.org/css-transforms/#perspective\"\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\"]\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[perspective-origin]\ntype = \"Position\"\ninitial = \"computed::position::Position::center()\"\nstruct = \"box\"\nboxed = true\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transforms-2/#perspective-origin-property\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[pointer-events]\ntype = \"PointerEvents\"\ninitial = \"specified::PointerEvents::Auto\"\nstruct = \"inherited_ui\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[position]\ntype = \"PositionProperty\"\ninitial = \"computed::PositionProperty::Static\"\nstruct = \"box\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-position/#position-property\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[position-anchor]\ntype = \"PositionAnchor\"\ninitial = \"computed::PositionAnchor::normal()\"\nstruct = \"position\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#propdef-position-anchor\"\naffects = \"layout\"\n\n[position-area]\ntype = \"PositionArea\"\ninitial = \"computed::PositionArea::none()\"\nstruct = \"position\"\nanimation_type = \"discrete\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nservo_pref = \"layout.unimplemented\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#typedef-position-area\"\naffects = \"layout\"\n\n[position-try-fallbacks]\ntype = \"PositionTryFallbacks\"\ninitial = \"computed::PositionTryFallbacks::none()\"\nstruct = \"position\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nservo_pref = \"layout.unimplemented\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#position-try-fallbacks\"\naffects = \"layout\"\n\n[position-try-order]\ntype = \"PositionTryOrder\"\ninitial = \"computed::PositionTryOrder::normal()\"\nstruct = \"position\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.anchor-positioning.position-try-order.enabled\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property\"\naffects = \"layout\"\n\n[position-visibility]\ntype = \"PositionVisibility\"\ninitial = \"computed::PositionVisibility::ANCHORS_VISIBLE\"\nstruct = \"position\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#propdef-position-visibility\"\naffects = \"layout\"\n\n[print-color-adjust]\ntype = \"PrintColorAdjust\"\ninitial = \"computed::PrintColorAdjust::Economy\"\nstruct = \"inherited_box\"\nengine = \"gecko\"\naliases = [\"color-adjust\"]\nspec = \"https://drafts.csswg.org/css-color-adjust/#print-color-adjust\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[quotes]\ntype = \"Quotes\"\ninitial = \"computed::Quotes::get_initial_value()\"\nstruct = \"list\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-content/#propdef-quotes\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[resize]\ntype = \"Resize\"\ninitial = \"computed::Resize::None\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mResize\"\nspec = \"https://drafts.csswg.org/css-ui/#propdef-resize\"\naffects = \"layout\"\n\n[right]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://www.w3.org/TR/CSS2/visuren.html#propdef-right\"\nallow_quirks = true\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_ffi_name = \"mOffset.1\"\nservo_restyle_damage = \"rebuild_box\"\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[rotate]\ntype = \"Rotate\"\ninitial = \"generics::transform::Rotate::None\"\nstruct = \"box\"\nboxed = true\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.csswg.org/css-transforms-2/#individual-transforms\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[row-gap]\ntype = \"length::NonNegativeLengthPercentageOrNormal\"\ninitial = \"computed::length::NonNegativeLengthPercentageOrNormal::normal()\"\nstruct = \"position\"\naliases = [\"grid-row-gap\"]\nspec = \"https://drafts.csswg.org/css-align-3/#propdef-row-gap\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[scale]\ntype = \"Scale\"\ninitial = \"generics::transform::Scale::None\"\nstruct = \"box\"\nboxed = true\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.csswg.org/css-transforms-2/#individual-transforms\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[scroll-snap-align]\ntype = \"ScrollSnapAlign\"\ninitial = \"computed::ScrollSnapAlign::none()\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[scroll-snap-stop]\ntype = \"ScrollSnapStop\"\ninitial = \"computed::ScrollSnapStop::Normal\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-stop\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[scroll-snap-type]\ntype = \"ScrollSnapType\"\ninitial = \"computed::ScrollSnapType::none()\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type\"\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[scrollbar-color]\ntype = \"ScrollbarColor\"\ninitial = \"Default::default()\"\nstruct = \"inherited_ui\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color\"\nboxed = true\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[-moz-inert]\ntype = \"Inert\"\ninitial = \"specified::Inert::None\"\nstruct = \"inherited_ui\"\nengine = \"gecko\"\ngecko_ffi_name = \"mInert\"\nanimation_type = \"discrete\"\nenabled_in = \"ua\"\nspec = \"Nonstandard (https://html.spec.whatwg.org/multipage/#inert-subtrees)\"\naffects = \"paint\"\n\n[-moz-user-focus]\ntype = \"UserFocus\"\ninitial = \"specified::UserFocus::Normal\"\nstruct = \"inherited_ui\"\nengine = \"gecko\"\ngecko_ffi_name = \"mUserFocus\"\nanimation_type = \"discrete\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)\"\nenabled_in = \"chrome\"\naffects = \"\"\n\n[-moz-theme]\ntype = \"MozTheme\"\ninitial = \"specified::MozTheme::Auto\"\nstruct = \"inherited_ui\"\nengine = \"gecko\"\nenabled_in = \"chrome\"\nanimation_type = \"discrete\"\nspec = \"Internal\"\naffects = \"paint\"\n\n[scrollbar-gutter]\ntype = \"ScrollbarGutter\"\ninitial = \"computed::ScrollbarGutter::AUTO\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property\"\naffects = \"layout\"\n\n[shape-image-threshold]\ntype = \"Opacity\"\ninitial = \"0.0\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-shapes/#shape-image-threshold-property\"\naffects = \"layout\"\n\n[shape-margin]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-shapes/#shape-margin-property\"\naffects = \"layout\"\n\n[shape-outside]\ntype = \"basic_shape::ShapeOutside\"\ninitial = \"generics::basic_shape::ShapeOutside::None\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-shapes/#shape-outside-property\"\naffects = \"layout\"\n\n[size]\ntype = \"PageSize\"\ninitial = \"computed::PageSize::auto()\"\nstruct = \"page\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-page-3/#page-size-prop\"\nboxed = true\nanimation_type = \"none\"\nrule_types_allowed = [\"page\"]\naffects = \"layout\"\n\n[stop-color]\ntype = \"Color\"\ninitial = \"computed::Color::BLACK\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/pservers.html#StopColorProperties\"\naffects = \"paint\"\n\n[stop-opacity]\ntype = \"Opacity\"\ninitial = \"1.0\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty\"\naffects = \"paint\"\n\n[stroke]\ntype = \"SVGPaint\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nboxed = true\nspec = \"https://svgwg.org/svg2-draft/painting.html#SpecifyingStrokePaint\"\naffects = \"paint\"\n\n[stroke-dasharray]\ntype = \"SVGStrokeDashArray\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeDashing\"\naffects = \"paint\"\n\n[stroke-dashoffset]\ntype = \"SVGLength\"\ninitial = \"computed::SVGLength::zero()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeDashing\"\naffects = \"paint\"\n\n[stroke-miterlimit]\ntype = \"NonNegativeNumber\"\ninitial = \"From::from(4.0)\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeMiterlimitProperty\"\naffects = \"layout\"\n\n[stroke-opacity]\ntype = \"SVGOpacity\"\ninitial = \"Default::default()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeOpacity\"\naffects = \"paint\"\n\n[stroke-width]\ntype = \"SVGWidth\"\ninitial = \"computed::SVGWidth::one()\"\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeWidth\"\naffects = \"layout\"\n\n[text-align]\ntype = \"TextAlign\"\ninitial = \"computed::TextAlign::Start\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-text-align\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[text-align-last]\ntype = \"TextAlignLast\"\ninitial = \"computed::text::TextAlignLast::Auto\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-text-align-last\"\naffects = \"layout\"\n\n[text-indent]\ntype = \"TextIndent\"\ninitial = \"computed::TextIndent::zero()\"\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-text-indent\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[text-justify]\ntype = \"TextJustify\"\ninitial = \"computed::TextJustify::Auto\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-text-justify\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[text-shadow]\ntype = \"SimpleShadow\"\nstruct = \"inherited_text\"\nvector = { simple_bindings = true, animation_type = \"with_zero\" }\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-text-decor-3/#text-shadow-property\"\nservo_restyle_damage = \"repaint\"\naffects = \"overflow\"\n\n[text-transform]\ntype = \"TextTransform\"\ninitial = \"computed::TextTransform::none()\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-text-transform\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[top]\ntype = \"Inset\"\ninitial = \"computed::Inset::auto()\"\nstruct = \"position\"\nspec = \"https://www.w3.org/TR/CSS2/visuren.html#propdef-top\"\nallow_quirks = true\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\ngecko_ffi_name = \"mOffset.0\"\nservo_restyle_damage = \"rebuild_box\"\nlogical_group = \"inset\"\naffects = \"layout\"\n\n[touch-action]\ntype = \"TouchAction\"\ninitial = \"computed::TouchAction::auto()\"\nstruct = \"box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://compat.spec.whatwg.org/#touch-action\"\naffects = \"paint\"\n\n[transform]\ntype = \"Transform\"\ninitial = \"generics::transform::Transform::none()\"\nstruct = \"box\"\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\"]\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.csswg.org/css-transforms/#propdef-transform\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[transform-box]\ntype = \"TransformBox\"\ninitial = \"computed::TransformBox::ViewBox\"\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-transforms/#transform-box\"\nanimation_type = \"discrete\"\naffects = \"overflow\"\n\n[transform-origin]\ntype = \"TransformOrigin\"\ninitial = \"computed::TransformOrigin::initial_value()\"\nstruct = \"box\"\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\", \"webkit\"]\ngecko_ffi_name = \"mTransformOrigin\"\nboxed = true\nspec = \"https://drafts.csswg.org/css-transforms/#transform-origin-property\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[transform-style]\ntype = \"TransformStyle\"\ninitial = \"computed::TransformStyle::Flat\"\nstruct = \"box\"\nspec = \"https://drafts.csswg.org/css-transforms-2/#transform-style-property\"\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\", \"webkit\"]\nanimation_type = \"discrete\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[transition-behavior]\ntype = \"TransitionBehavior\"\ninitial = \"computed::TransitionBehavior::normal()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nspec = \"https://drafts.csswg.org/css-transitions-2/#transition-behavior-property\"\naffects = \"\"\n\n[transition-delay]\ntype = \"Time\"\ninitial = \"computed::Time::zero()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.transitions\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-transition-delay\"\naffects = \"\"\n\n[transition-duration]\ntype = \"Time\"\ninitial = \"computed::Time::zero()\"\nstruct = \"ui\"\nparse_method = \"parse_non_negative\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.transitions\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-transition-duration\"\naffects = \"\"\n\n[transition-property]\ntype = \"TransitionProperty\"\ninitial = \"computed::TransitionProperty::all()\"\nstruct = \"ui\"\nvector = { none_value = \"computed::TransitionProperty::none()\", need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.transitions\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-transition-property\"\naffects = \"\"\n\n[transition-timing-function]\ntype = \"TimingFunction\"\ninitial = \"computed::TimingFunction::ease()\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nextra_prefixes = [\"moz:layout.css.prefixes.transitions\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function\"\naffects = \"\"\n\n[translate]\ntype = \"Translate\"\ninitial = \"generics::transform::Translate::None\"\nstruct = \"box\"\nboxed = true\nflags = \"CAN_ANIMATE_ON_COMPOSITOR\"\nspec = \"https://drafts.csswg.org/css-transforms-2/#individual-transforms\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[user-select]\ntype = \"UserSelect\"\ninitial = \"computed::UserSelect::Auto\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nextra_prefixes = [\"moz\", \"webkit\"]\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-ui-4/#propdef-user-select\"\naffects = \"\"\n\n[-moz-force-broken-image-icon]\ntype = \"BoolInteger\"\ninitial = \"computed::BoolInteger::zero()\"\nstruct = \"ui\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"None (Nonstandard Firefox-only property)\"\naffects = \"layout\"\n\n[-moz-subtree-hidden-only-visually]\ntype = \"BoolInteger\"\ninitial = \"computed::BoolInteger::zero()\"\nstruct = \"ui\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"None (Nonstandard internal property)\"\nenabled_in = \"chrome\"\naffects = \"paint\"\n\n[-moz-window-input-region-margin]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"None (Nonstandard internal property)\"\nenabled_in = \"chrome\"\naffects = \"\"\n\n[-moz-window-opacity]\ntype = \"Opacity\"\ninitial = \"1.0\"\nstruct = \"ui\"\nengine = \"gecko\"\ngecko_ffi_name = \"mWindowOpacity\"\nspec = \"None (Nonstandard internal property)\"\nenabled_in = \"chrome\"\naffects = \"paint\"\n\n[-moz-window-transform]\ntype = \"Transform\"\ninitial = \"generics::transform::Transform::none()\"\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"None (Nonstandard internal property)\"\nenabled_in = \"chrome\"\naffects = \"overflow\"\n\n[animation-composition]\ntype = \"AnimationComposition\"\ninitial = \"computed::AnimationComposition::Replace\"\nstruct = \"ui\"\nvector = { need_index = true }\nanimation_type = \"none\"\nservo_pref = \"layout.unimplemented\"\nspec = \"https://drafts.csswg.org/css-animations-2/#animation-composition\"\naffects = \"\"\n\n[scroll-timeline-axis]\ntype = \"ScrollAxis\"\ninitial = \"computed::ScrollAxis::default()\"\nstruct = \"ui\"\nengine = \"gecko\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[scroll-timeline-name]\ntype = \"TimelineName\"\ninitial = \"computed::TimelineName::none()\"\nstruct = \"ui\"\nengine = \"gecko\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[view-timeline-axis]\ntype = \"ScrollAxis\"\ninitial = \"computed::ScrollAxis::default()\"\nstruct = \"ui\"\nengine = \"gecko\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[view-timeline-inset]\ntype = \"ViewTimelineInset\"\ninitial = \"computed::ViewTimelineInset::default()\"\nstruct = \"ui\"\nengine = \"gecko\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[view-timeline-name]\ntype = \"TimelineName\"\ninitial = \"computed::TimelineName::none()\"\nstruct = \"ui\"\nengine = \"gecko\"\nvector = { need_index = true }\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#view-timeline-name\"\nrule_types_allowed = [\"style\", \"scope\"]\naffects = \"\"\n\n[view-transition-class]\ntype = \"ViewTransitionClass\"\ninitial = \"computed::ViewTransitionClass::none()\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"discrete\"\ngecko_pref = \"dom.viewTransitions.enabled\"\nspec = \"https://drafts.csswg.org/css-view-transitions-2/#view-transition-class-prop\"\naffects = \"\"\nenabled_in = \"ua\"\n\n[view-transition-name]\ntype = \"ViewTransitionName\"\ninitial = \"computed::ViewTransitionName::none()\"\nstruct = \"ui\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"discrete\"\ngecko_pref = \"dom.viewTransitions.enabled\"\nspec = \"https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop\"\naffects = \"\"\nenabled_in = \"ua\"\n\n[vector-effect]\ntype = \"VectorEffect\"\ninitial = \"computed::VectorEffect::none()\"\nstruct = \"svg\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://svgwg.org/svg2-draft/coords.html#VectorEffects\"\naffects = \"layout\"\n\n[cx]\ntype = \"LengthPercentage\"\ninitial = \"computed::LengthPercentage::zero()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#CX\"\naffects = \"layout\"\n\n[cy]\ntype = \"LengthPercentage\"\ninitial = \"computed::LengthPercentage::zero()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#CY\"\naffects = \"layout\"\n\n[mask-image]\ntype = \"Image\"\ninitial = \"computed::Image::None\"\nstruct = \"svg\"\nservo_pref = \"layout.unimplemented\"\nparse_method = \"parse_with_cors_anonymous\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-image\"\nvector = {}\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\naffects = \"paint\"\n\n[mask-position-x]\ntype = \"position::HorizontalPosition\"\ninitial = \"computed::LengthPercentage::zero_percent()\"\nstruct = \"svg\"\nengine = \"gecko\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-position\"\nvector = { animation_type = \"repeatable_list\" }\naffects = \"paint\"\n\n[mask-position-y]\ntype = \"position::VerticalPosition\"\ninitial = \"computed::LengthPercentage::zero_percent()\"\nstruct = \"svg\"\nengine = \"gecko\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-position\"\nvector = { animation_type = \"repeatable_list\" }\naffects = \"paint\"\n\n[mask-repeat]\ntype = \"BackgroundRepeat\"\ninitial = \"computed::BackgroundRepeat::repeat()\"\nstruct = \"svg\"\nengine = \"gecko\"\nextra_prefixes = [\"webkit\"]\nanimation_type = \"discrete\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-repeat\"\nvector = {}\naffects = \"paint\"\n\n[mask-size]\ntype = \"background::BackgroundSize\"\ninitial = \"computed::BackgroundSize::auto()\"\nstruct = \"svg\"\nengine = \"gecko\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-size\"\nvector = { animation_type = \"repeatable_list\" }\naffects = \"paint\"\n\n[r]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#R\"\naffects = \"layout\"\n\n[rx]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#RX\"\naffects = \"layout\"\n\n[ry]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#RY\"\naffects = \"layout\"\n\n[x]\ntype = \"LengthPercentage\"\ninitial = \"computed::LengthPercentage::zero()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#X\"\naffects = \"layout\"\n\n[y]\ntype = \"LengthPercentage\"\ninitial = \"computed::LengthPercentage::zero()\"\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/geometry.html#Y\"\naffects = \"layout\"\n\n[width]\ntype = \"Size\"\ninitial = \"computed::Size::auto()\"\nstruct = \"position\"\nlogical_group = \"size\"\nallow_quirks = true\nparse_method = \"parse_size_for_width_or_height\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-width\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[will-change]\ntype = \"WillChange\"\ninitial = \"computed::WillChange::auto()\"\nstruct = \"box\"\nanimation_type = \"none\"\nspec = \"https://drafts.csswg.org/css-will-change/#will-change\"\naffects = \"layout\"\n\n[word-break]\ntype = \"WordBreak\"\ninitial = \"computed::WordBreak::Normal\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-word-break\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[word-spacing]\ntype = \"WordSpacing\"\ninitial = \"computed::WordSpacing::zero()\"\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-word-spacing\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[-moz-control-character-visibility]\ntype = \"text::MozControlCharacterVisibility\"\ninitial = \"Default::default()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nenabled_in = \"chrome\"\ngecko_pref = \"layout.css.moz-control-character-visibility.enabled\"\nhas_effect_on_gecko_scrollbars = false\nanimation_type = \"none\"\nspec = \"Nonstandard\"\naffects = \"layout\"\n\n[-webkit-text-fill-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nignored_when_colors_disabled = true\nspec = \"https://compat.spec.whatwg.org/#the-webkit-text-fill-color\"\naffects = \"paint\"\n\n[-webkit-text-stroke-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nignored_when_colors_disabled = true\nspec = \"https://compat.spec.whatwg.org/#the-webkit-text-stroke-color\"\naffects = \"paint\"\n\n[-webkit-text-stroke-width]\ntype = \"LineWidth\"\ninitial = \"app_units::Au(0)\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://compat.spec.whatwg.org/#the-webkit-text-stroke-width\"\nanimation_type = \"discrete\"\naffects = \"overflow\"\n\n[forced-color-adjust]\ntype = \"ForcedColorAdjust\"\ninitial = \"computed::ForcedColorAdjust::Auto\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-color-adjust-1/#forced-color-adjust-prop\"\naffects = \"paint\"\n\n[hyphenate-character]\ntype = \"HyphenateCharacter\"\ninitial = \"computed::HyphenateCharacter::Auto\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://www.w3.org/TR/css-text-4/#hyphenate-character\"\naffects = \"layout\"\n\n[hyphenate-limit-chars]\ntype = \"HyphenateLimitChars\"\ninitial = \"computed::HyphenateLimitChars::auto()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-text-4/#hyphenate-char-limits\"\naffects = \"layout\"\nboxed = true\n\n[line-break]\ntype = \"LineBreak\"\ninitial = \"computed::LineBreak::Auto\"\nstruct = \"inherited_text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-3/#line-break-property\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\n\n[ruby-position]\ntype = \"RubyPosition\"\ninitial = \"computed::RubyPosition::AlternateOver\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-ruby/#ruby-position-property\"\nanimation_type = \"discrete\"\naffects = \"layout\"\n\n[tab-size]\ntype = \"NonNegativeLengthOrNumber\"\ninitial = \"generics::length::LengthOrNumber::Number(From::from(8.0))\"\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text-3/#tab-size-property\"\nextra_gecko_aliases = [\"-moz-tab-size\"]\naffects = \"layout\"\n\n[text-autospace]\ntype = \"TextAutospace\"\ninitial = \"computed::text::TextAutospace::NO_AUTOSPACE\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.text-autospace.enabled\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"https://drafts.csswg.org/css-text-4/#text-autospace-property\"\naffects = \"layout\"\n\n[text-box-edge]\ntype = \"TextBoxEdge\"\ninitial = \"computed::TextBoxEdge::Auto\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.text-box.enabled\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"https://drafts.csswg.org/css-inline/#text-box-edge\"\naffects = \"layout\"\n\n[text-box-trim]\ntype = \"TextBoxTrim\"\ninitial = \"computed::TextBoxTrim::none()\"\nstruct = \"text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.text-box.enabled\"\nhas_effect_on_gecko_scrollbars = false\nspec = \"https://drafts.csswg.org/css-inline/#text-box-trim\"\naffects = \"layout\"\n\n[text-decoration-skip-ink]\ntype = \"TextDecorationSkipInk\"\ninitial = \"computed::TextDecorationSkipInk::Auto\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property\"\naffects = \"overflow\"\n\n[text-emphasis-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-color\"\naffects = \"paint\"\n\n[text-emphasis-position]\ntype = \"TextEmphasisPosition\"\ninitial = \"computed::TextEmphasisPosition::AUTO\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-position\"\naffects = \"layout\"\n\n[text-emphasis-style]\ntype = \"TextEmphasisStyle\"\ninitial = \"computed::TextEmphasisStyle::None\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style\"\naffects = \"overflow\"\n\n[text-underline-offset]\ntype = \"LengthPercentageOrAuto\"\ninitial = \"computed::LengthPercentageOrAuto::auto()\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-text-decor-4/#underline-offset\"\naffects = \"overflow\"\n\n[text-underline-position]\ntype = \"TextUnderlinePosition\"\ninitial = \"computed::TextUnderlinePosition::AUTO\"\nstruct = \"inherited_text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property\"\naffects = \"overflow\"\n\n[writing-mode]\ntype = \"WritingModeProperty\"\ninitial = \"computed::WritingModeProperty::HorizontalTb\"\nstruct = \"inherited_box\"\nspec = \"https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode\"\nservo_pref = \"layout.writing-mode.enabled\"\nanimation_type = \"none\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[z-index]\ntype = \"ZIndex\"\ninitial = \"computed::ZIndex::auto()\"\nstruct = \"position\"\nspec = \"https://www.w3.org/TR/CSS2/visuren.html#z-index\"\naffects = \"paint\"\nservo_restyle_damage = \"rebuild_stacking_context\"\n\n[zoom]\ntype = \"Zoom\"\ninitial = \"computed::box_::Zoom::ONE\"\nstruct = \"box\"\nspec = \"Non-standard (https://github.com/atanassov/css-zoom/ is the closest)\"\ngecko_pref = \"layout.css.zoom.enabled\"\nservo_pref = \"layout.unimplemented\"\naffects = \"layout\"\nenabled_in = \"chrome\"\n\n[border-image-outset]\ntype = \"NonNegativeLengthOrNumberRect\"\ninitial = \"generics::rect::Rect::all(computed::NonNegativeLengthOrNumber::zero())\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-image-outset\"\nboxed = true\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[border-image-repeat]\ntype = \"BorderImageRepeat\"\ninitial = \"computed::BorderImageRepeat::stretch()\"\nstruct = \"border\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat\"\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[border-image-slice]\ntype = \"BorderImageSlice\"\ninitial = \"computed::BorderImageSlice::hundred_percent()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-image-slice\"\nboxed = true\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[border-image-source]\ntype = \"Image\"\ninitial = \"computed::Image::None\"\nstruct = \"border\"\nanimation_type = \"discrete\"\nignored_when_colors_disabled = true\nservo_restyle_damage = \"repaint\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-image\"\naffects = \"paint\"\n\n[border-image-width]\ntype = \"BorderImageWidth\"\ninitial = \"computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-image-width\"\nboxed = true\nservo_restyle_damage = \"repaint\"\naffects = \"paint\"\n\n[text-overflow]\ntype = \"TextOverflow\"\ninitial = \"computed::TextOverflow::get_initial_value()\"\nstruct = \"text\"\nservo_pref = \"layout.unimplemented\"\nanimation_type = \"discrete\"\nboxed = true\nspec = \"https://drafts.csswg.org/css-ui/#propdef-text-overflow\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"paint\"\n\n[text-decoration-line]\ntype = \"TextDecorationLine\"\ninitial = \"specified::TextDecorationLine::none()\"\nstruct = \"text\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"overflow\"\n\n[text-decoration-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"text\"\nignored_when_colors_disabled = true\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color\"\nservo_restyle_damage = \"recalculate_overflow\"\naffects = \"paint\"\n\n[text-decoration-inset]\ntype = \"TextDecorationInset\"\ninitial = \"computed::text::TextDecorationInset::get_initial_value()\"\nstruct = \"text\"\nengine = \"gecko\"\nboxed = true\ngecko_pref = \"layout.css.text-decoration-inset.enabled\"\nspec = \"https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-inset-property\"\naffects = \"overflow\"\n\n[initial-letter]\ntype = \"InitialLetter\"\ninitial = \"computed::InitialLetter::normal()\"\nstruct = \"text\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\ngecko_pref = \"layout.css.initial-letter.enabled\"\nspec = \"https://drafts.csswg.org/css-inline/#sizing-drop-initials\"\naffects = \"layout\"\n\n[text-decoration-thickness]\ntype = \"TextDecorationLength\"\ninitial = \"generics::text::GenericTextDecorationLength::Auto\"\nstruct = \"text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property\"\naffects = \"overflow\"\n\n[-x-span]\ntype = \"Integer\"\ninitial = \"1\"\nstruct = \"table\"\nengine = \"gecko\"\nspec = \"Internal-only (for `<col span>` pres attr)\"\nanimation_type = \"none\"\nenabled_in = \"\"\naffects = \"layout\"\n\n[border-top-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-top-color\"\nlogical_group = \"border-color\"\nallow_quirks = true\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-right-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-right-color\"\nlogical_group = \"border-color\"\nallow_quirks = true\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-bottom-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-bottom-color\"\nlogical_group = \"border-color\"\nallow_quirks = true\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-left-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-left-color\"\nlogical_group = \"border-color\"\nallow_quirks = true\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-block-start-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-color\"\nlogical = true\nlogical_group = \"border-color\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-block-end-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-color\"\nlogical = true\nlogical_group = \"border-color\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-inline-start-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-start-color\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-color\"\nlogical = true\nlogical_group = \"border-color\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-inline-end-color]\ntype = \"Color\"\ninitial = \"computed_value::T::currentcolor()\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-color\"\nlogical = true\nlogical_group = \"border-color\"\nignored_when_colors_disabled = true\naffects = \"paint\"\n\n[border-top-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-top-style\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mBorderStyle.0\"\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-right-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-right-style\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mBorderStyle.1\"\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-bottom-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-bottom-style\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mBorderStyle.2\"\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-left-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-left-style\"\nanimation_type = \"discrete\"\ngecko_ffi_name = \"mBorderStyle.3\"\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-block-start-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-style\"\nanimation_type = \"none\"\ngecko_ffi_name = \"mBorderStyle.4\"\nlogical = true\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-block-end-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-style\"\nanimation_type = \"none\"\ngecko_ffi_name = \"mBorderStyle.5\"\nlogical = true\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-inline-start-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-start-style\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-style\"\nanimation_type = \"none\"\ngecko_ffi_name = \"mBorderStyle.6\"\nlogical = true\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-inline-end-style]\ntype = \"BorderStyle\"\ninitial = \"specified::BorderStyle::None\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-end-style\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-style\"\nanimation_type = \"none\"\ngecko_ffi_name = \"mBorderStyle.7\"\nlogical = true\nlogical_group = \"border-style\"\naffects = \"layout\"\n\n[border-top-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-top-width\"\nlogical_group = \"border-width\"\nallow_quirks = true\ngecko_ffi_name = \"mBorder.0\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-right-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-right-width\"\nlogical_group = \"border-width\"\nallow_quirks = true\ngecko_ffi_name = \"mBorder.1\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-bottom-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-bottom-width\"\nlogical_group = \"border-width\"\nallow_quirks = true\ngecko_ffi_name = \"mBorder.2\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-left-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-left-width\"\nlogical_group = \"border-width\"\nallow_quirks = true\ngecko_ffi_name = \"mBorder.3\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-block-start-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-start-width\"\nlogical = true\nlogical_group = \"border-width\"\ngecko_ffi_name = \"mBorder.4\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-block-end-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-block-end-width\"\nlogical = true\nlogical_group = \"border-width\"\ngecko_ffi_name = \"mBorder.5\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-inline-start-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-start-width\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-width\"\nlogical = true\nlogical_group = \"border-width\"\ngecko_ffi_name = \"mBorder.6\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-inline-end-width]\ntype = \"BorderSideWidth\"\ninitial = \"computed::BorderSideWidth::medium()\"\nstruct = \"border\"\nextra_gecko_aliases = [\"-moz-border-end-width\"]\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-width\"\nlogical = true\nlogical_group = \"border-width\"\ngecko_ffi_name = \"mBorder.7\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[border-top-left-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-top-left-radius\"\ngecko_ffi_name = \"mBorderRadius.top_left\"\nboxed = true\nlogical_group = \"border-radius\"\naffects = \"paint\"\n\n[border-top-right-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-top-right-radius\"\ngecko_ffi_name = \"mBorderRadius.top_right\"\nboxed = true\nlogical_group = \"border-radius\"\naffects = \"paint\"\n\n[border-bottom-right-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-bottom-right-radius\"\ngecko_ffi_name = \"mBorderRadius.bottom_right\"\nboxed = true\nlogical_group = \"border-radius\"\naffects = \"paint\"\n\n[border-bottom-left-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nextra_prefixes = [\"webkit\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-bottom-left-radius\"\ngecko_ffi_name = \"mBorderRadius.bottom_left\"\nboxed = true\nlogical_group = \"border-radius\"\naffects = \"paint\"\n\n[border-start-start-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-start-start-radius\"\ngecko_ffi_name = \"mBorderRadius.start_start\"\nboxed = true\nlogical_group = \"border-radius\"\nlogical = true\naffects = \"paint\"\n\n[border-start-end-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-start-end-radius\"\ngecko_ffi_name = \"mBorderRadius.start_end\"\nboxed = true\nlogical_group = \"border-radius\"\nlogical = true\naffects = \"paint\"\n\n[border-end-start-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-end-start-radius\"\ngecko_ffi_name = \"mBorderRadius.end_start\"\nboxed = true\nlogical_group = \"border-radius\"\nlogical = true\naffects = \"paint\"\n\n[border-end-end-radius]\ntype = \"BorderCornerRadius\"\ninitial = \"computed::BorderCornerRadius::zero()\"\nstruct = \"border\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-border-end-end-radius\"\ngecko_ffi_name = \"mBorderRadius.end_end\"\nboxed = true\nlogical_group = \"border-radius\"\nlogical = true\naffects = \"paint\"\n\n[margin-top]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nallow_quirks = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.0\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-margin-top\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-right]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nallow_quirks = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.1\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-margin-right\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-bottom]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nallow_quirks = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.2\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-margin-bottom\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-left]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nallow_quirks = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.3\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-margin-left\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-block-start]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nlogical = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.4\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-margin-block-start\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-block-end]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nlogical = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.5\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-margin-block-end\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-inline-start]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nextra_gecko_aliases = [\"-moz-margin-start\"]\nlogical = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.6\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-margin-inline-start\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[margin-inline-end]\ntype = \"Margin\"\ninitial = \"computed::Margin::zero()\"\nstruct = \"margin\"\nextra_gecko_aliases = [\"-moz-margin-end\"]\nlogical = true\nlogical_group = \"margin\"\ngecko_ffi_name = \"mMargin.7\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-margin-inline-end\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[scroll-margin-top]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.0\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-top\"\naffects = \"\"\n\n[scroll-margin-right]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.1\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-right\"\naffects = \"\"\n\n[scroll-margin-bottom]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.2\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-bottom\"\naffects = \"\"\n\n[scroll-margin-left]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.3\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-left\"\naffects = \"\"\n\n[scroll-margin-block-start]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.4\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block-start\"\naffects = \"\"\n\n[scroll-margin-block-end]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.5\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block-end\"\naffects = \"\"\n\n[scroll-margin-inline-start]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.6\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline-start\"\naffects = \"\"\n\n[scroll-margin-inline-end]\ntype = \"Length\"\ninitial = \"computed::Length::zero()\"\nstruct = \"margin\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-margin\"\ngecko_ffi_name = \"mScrollMargin.7\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline-end\"\naffects = \"\"\n\n[padding-top]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-padding-top\"\ngecko_ffi_name = \"mPadding.0\"\nallow_quirks = true\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-right]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-padding-right\"\ngecko_ffi_name = \"mPadding.1\"\nallow_quirks = true\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-bottom]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-padding-bottom\"\ngecko_ffi_name = \"mPadding.2\"\nallow_quirks = true\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-left]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-padding-left\"\ngecko_ffi_name = \"mPadding.3\"\nallow_quirks = true\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-block-start]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical = true\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-padding-block-start\"\ngecko_ffi_name = \"mPadding.4\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-block-end]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nlogical = true\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-padding-block-end\"\ngecko_ffi_name = \"mPadding.5\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-inline-start]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nextra_gecko_aliases = [\"-moz-padding-start\"]\nlogical = true\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-start\"\ngecko_ffi_name = \"mPadding.6\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[padding-inline-end]\ntype = \"NonNegativeLengthPercentage\"\ninitial = \"computed::NonNegativeLengthPercentage::zero()\"\nstruct = \"padding\"\nextra_gecko_aliases = [\"-moz-padding-end\"]\nlogical = true\nlogical_group = \"padding\"\nspec = \"https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-end\"\ngecko_ffi_name = \"mPadding.7\"\nservo_restyle_damage = \"rebuild_box\"\naffects = \"layout\"\n\n[scroll-padding-top]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.0\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-top\"\naffects = \"\"\n\n[scroll-padding-right]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.1\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-right\"\naffects = \"\"\n\n[scroll-padding-bottom]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.2\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-bottom\"\naffects = \"\"\n\n[scroll-padding-left]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.3\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-left\"\naffects = \"\"\n\n[scroll-padding-block-start]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.4\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block-start\"\naffects = \"\"\n\n[scroll-padding-block-end]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.5\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block-end\"\naffects = \"\"\n\n[scroll-padding-inline-start]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.6\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline-start\"\naffects = \"\"\n\n[scroll-padding-inline-end]\ntype = \"NonNegativeLengthPercentageOrAuto\"\ninitial = \"computed::NonNegativeLengthPercentageOrAuto::auto()\"\nstruct = \"padding\"\nengine = \"gecko\"\nlogical = true\nlogical_group = \"scroll-padding\"\ngecko_ffi_name = \"mScrollPadding.7\"\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline-end\"\naffects = \"\"\n\n[background-attachment]\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-attachment\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nservo_restyle_damage = \"repaint\"\nvector = {}\nkeyword = { values = [\"scroll\", \"fixed\"], extra_gecko_values = [\"local\"], gecko_enum_prefix = \"StyleImageLayerAttachment\" }\n\n[background-clip]\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-clip\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"repaint\"\nvector = {}\nkeyword = { values = [\"border-box\", \"padding-box\", \"content-box\"], extra_gecko_values = [\"text\"], gecko_enum_prefix = \"StyleGeometryBox\", gecko_inexhaustive = true }\n\n[background-origin]\nstruct = \"background\"\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background-origin\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"repaint\"\nvector = {}\nkeyword = { values = [\"padding-box\", \"border-box\", \"content-box\"], gecko_enum_prefix = \"StyleGeometryBox\", gecko_inexhaustive = true }\n\n[background-blend-mode]\nstruct = \"background\"\nvector = {}\nanimation_type = \"discrete\"\nspec = \"https://drafts.fxtf.org/compositing/#background-blend-mode\"\naffects = \"paint\"\nservo_restyle_damage = \"repaint\"\n\n[background-blend-mode.keyword]\nvalues = [\n  \"normal\", \"multiply\", \"screen\", \"overlay\", \"darken\", \"lighten\",\n  \"color-dodge\", \"color-burn\", \"hard-light\", \"soft-light\", \"difference\",\n  \"exclusion\", \"hue\", \"saturation\", \"color\", \"luminosity\"\n]\ngecko_enum_prefix = \"StyleBlend\"\ngecko_inexhaustive = true\n\n[box-decoration-break]\nstruct = \"border\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-break/#propdef-box-decoration-break\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"slice\", \"clone\"] }\n\n[-moz-float-edge]\nstruct = \"border\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFloatEdge\"\nkeyword = { values = [\"content-box\", \"margin-box\"] }\n\n[-moz-top-layer]\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"Internal (not web-exposed)\"\nanimation_type = \"discrete\"\nenabled_in = \"ua\"\naffects = \"layout\"\ngecko_ffi_name = \"mTopLayer\"\nkeyword = { values = [\"none\", \"auto\"] }\n\n[-servo-top-layer]\nstruct = \"box\"\nengine = \"servo\"\nspec = \"Internal (not web-exposed)\"\nanimation_type = \"none\"\nenabled_in = \"ua\"\naffects = \"layout\"\nkeyword = { values = [\"none\", \"top\"] }\n\n[scroll-behavior]\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior\"\nanimation_type = \"discrete\"\naffects = \"\"\nkeyword = { values = [\"auto\", \"smooth\"] }\n\n[isolation]\nstruct = \"box\"\nspec = \"https://drafts.fxtf.org/compositing/#isolation\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nservo_restyle_damage = \"repaint\"\nkeyword = { values = [\"auto\", \"isolate\"] }\n\n[backface-visibility]\nstruct = \"box\"\nspec = \"https://drafts.csswg.org/css-transforms/#backface-visibility-property\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nextra_prefixes = [\"moz:layout.css.prefixes.transforms\", \"webkit\"]\nservo_restyle_damage = \"repaint\"\nkeyword = { values = [\"visible\", \"hidden\"] }\n\n[-moz-orient]\nstruct = \"box\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mOrient\"\nkeyword = { values = [\"inline\", \"block\", \"horizontal\", \"vertical\"] }\n\n[column-fill]\nstruct = \"column\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-fill\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"balance\", \"auto\"] }\n\n[column-span]\nstruct = \"column\"\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-span\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_pref = \"layout.columns.enabled\"\nkeyword = { values = [\"none\", \"all\"] }\n\n[font-variant-caps]\nstruct = \"font\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-caps\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFont.variantCaps\"\nservo_restyle_damage = \"rebuild_box\"\n\n[font-variant-caps.keyword]\nvalues = [\"normal\", \"small-caps\"]\nextra_gecko_values = [\"all-small-caps\", \"petite-caps\", \"all-petite-caps\", \"unicase\", \"titling-caps\"]\ngecko_constant_prefix = \"NS_FONT_VARIANT_CAPS\"\n\n[font-kerning]\nstruct = \"font\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-kerning\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFont.kerning\"\nkeyword = { values = [\"auto\", \"none\", \"normal\"], gecko_constant_prefix = \"NS_FONT_KERNING\" }\n\n[font-variant-emoji]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-emoji\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFont.variantEmoji\"\ngecko_pref = \"layout.css.font-variant-emoji.enabled\"\nhas_effect_on_gecko_scrollbars = false\nkeyword = { values = [\"normal\", \"text\", \"emoji\", \"unicode\"] }\n\n[font-variant-position]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-fonts/#propdef-font-variant-position\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFont.variantPosition\"\nkeyword = { values = [\"normal\", \"sub\", \"super\"], gecko_constant_prefix = \"NS_FONT_VARIANT_POSITION\" }\n\n[font-optical-sizing]\nstruct = \"font\"\nspec = \"https://www.w3.org/TR/css-fonts-4/#font-optical-sizing-def\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mFont.opticalSizing\"\ngecko_pref = \"layout.css.font-variations.enabled\"\nservo_pref = \"layout.variable_fonts.enabled\"\nhas_effect_on_gecko_scrollbars = false\nkeyword = { values = [\"auto\", \"none\"], gecko_constant_prefix = \"NS_FONT_OPTICAL_SIZING\" }\n\n[math-style]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://mathml-refresh.github.io/mathml-core/#the-math-style-property\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"normal\", \"compact\"] }\n\n[math-shift]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"https://w3c.github.io/mathml-core/#the-math-shift\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"normal\", \"compact\"] }\n\n[-moz-osx-font-smoothing]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth)\"\nanimation_type = \"discrete\"\naffects = \"paint\"\ngecko_ffi_name = \"mFont.smoothing\"\naliases = [\"-webkit-font-smoothing:layout.css.osx-font-smoothing.enabled\"]\ngecko_pref = \"layout.css.osx-font-smoothing.enabled\"\nhas_effect_on_gecko_scrollbars = false\nkeyword = { values = [\"auto\", \"grayscale\", \"subpixel-antialiased\"], gecko_aliases = [\"antialiased=grayscale\"], gecko_constant_prefix = \"NS_FONT_SMOOTHING\" }\n\n[visibility]\nstruct = \"inherited_box\"\nspec = \"https://drafts.csswg.org/css-box/#propdef-visibility\"\naffects = \"paint\"\ngecko_ffi_name = \"mVisible\"\nkeyword = { values = [\"visible\", \"hidden\", \"collapse\"] }\n\n[direction]\nstruct = \"inherited_box\"\nspec = \"https://drafts.csswg.org/css-writing-modes/#propdef-direction\"\nanimation_type = \"none\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"ltr\", \"rtl\"] }\n\n[-moz-box-collapse]\nstruct = \"inherited_box\"\nengine = \"gecko\"\nspec = \"None (internal)\"\nanimation_type = \"none\"\nenabled_in = \"chrome\"\naffects = \"layout\"\nkeyword = { values = [\"flex\", \"legacy\"] }\n\n[text-orientation]\nstruct = \"inherited_box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation\"\nanimation_type = \"none\"\naffects = \"layout\"\nkeyword = { values = [\"mixed\", \"upright\", \"sideways\"], gecko_aliases = [\"sideways-right=sideways\"] }\n\n[image-orientation]\nstruct = \"inherited_box\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-images/#propdef-image-orientation\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"from-image\", \"none\"] }\n\n[text-anchor]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/text.html#TextAnchorProperty\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"start\", \"middle\", \"end\"] }\n\n[color-interpolation]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#ColorInterpolationProperty\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nkeyword = { values = [\"srgb\", \"auto\", \"linearrgb\"] }\n\n[color-interpolation-filters]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#ColorInterpolationFiltersProperty\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nkeyword = { values = [\"linearrgb\", \"auto\", \"srgb\"], gecko_enum_prefix = \"StyleColorInterpolation\" }\n\n[shape-rendering]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#ShapeRenderingProperty\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nkeyword = { values = [\"auto\", \"optimizespeed\", \"crispedges\", \"geometricprecision\"] }\n\n[stroke-linecap]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeLinecapProperty\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"butt\", \"round\", \"square\"] }\n\n[stroke-linejoin]\nstruct = \"inherited_svg\"\nengine = \"gecko\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#StrokeLinejoinProperty\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"miter\", \"round\", \"bevel\"] }\n\n[border-collapse]\nstruct = \"inherited_table\"\nspec = \"https://drafts.csswg.org/css-tables/#propdef-border-collapse\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"separate\", \"collapse\"] }\n\n[empty-cells]\nstruct = \"inherited_table\"\nspec = \"https://drafts.csswg.org/css-tables/#propdef-empty-cells\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"show\", \"hide\"] }\n\n[hyphens]\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-text/#propdef-hyphens\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nextra_prefixes = [\"moz\"]\nkeyword = { values = [\"manual\", \"none\", \"auto\"] }\n\n[-moz-text-size-adjust]\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-size-adjust/#adjustment-control\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mTextSizeAdjust\"\naliases = [\"-webkit-text-size-adjust\"]\nkeyword = { values = [\"auto\", \"none\"] }\n\n[ruby-align]\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-ruby/#ruby-align-property\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"space-around\", \"start\", \"center\", \"space-between\"] }\n\n[text-combine-upright]\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright\"\nanimation_type = \"none\"\naffects = \"layout\"\nkeyword = { values = [\"none\", \"all\"] }\n\n[text-rendering]\nstruct = \"inherited_text\"\nspec = \"https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"auto\", \"optimizespeed\", \"optimizelegibility\", \"geometricprecision\"] }\n\n[-webkit-text-security]\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text/#MISSING\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"none\", \"circle\", \"disc\", \"square\"] }\n\n[text-wrap-mode]\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text-4/#propdef-text-wrap-mode\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"wrap\", \"nowrap\"] }\n\n[text-wrap-style]\nstruct = \"inherited_text\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-text-4/#text-wrap-style\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"auto\", \"stable\", \"balance\"] }\n\n[list-style-position]\nstruct = \"list\"\nspec = \"https://drafts.csswg.org/css-lists/#propdef-list-style-position\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"outside\", \"inside\"] }\n\n[flex-direction]\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-direction-property\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"row\", \"row-reverse\", \"column\", \"column-reverse\"] }\n\n[flex-wrap]\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-wrap-property\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nextra_prefixes = [\"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"nowrap\", \"wrap\", \"wrap-reverse\"] }\n\n[box-sizing]\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-ui/#propdef-box-sizing\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nextra_prefixes = [\"moz:layout.css.prefixes.box-sizing\", \"webkit\"]\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"content-box\", \"border-box\"] }\n\n[object-fit]\nstruct = \"position\"\nspec = \"https://drafts.csswg.org/css-images/#propdef-object-fit\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"fill\", \"contain\", \"cover\", \"none\", \"scale-down\"] }\n\n[mask-type]\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-type\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nkeyword = { values = [\"luminance\", \"alpha\"] }\n\n[mask-mode]\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-mode\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nvector = {}\nkeyword = { values = [\"match-source\", \"alpha\", \"luminance\"] }\n\n[mask-clip]\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-clip\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nvector = {}\nextra_prefixes = [\"webkit\"]\nkeyword = { values = [\"border-box\", \"content-box\", \"padding-box\"], extra_gecko_values = [\"fill-box\", \"stroke-box\", \"view-box\", \"no-clip\"], gecko_enum_prefix = \"StyleGeometryBox\", gecko_inexhaustive = true }\n\n[mask-origin]\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-origin\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nvector = {}\nextra_prefixes = [\"webkit\"]\nkeyword = { values = [\"border-box\", \"content-box\", \"padding-box\"], extra_gecko_values = [\"fill-box\", \"stroke-box\", \"view-box\"], gecko_enum_prefix = \"StyleGeometryBox\", gecko_inexhaustive = true }\n\n[mask-composite]\nstruct = \"svg\"\nengine = \"gecko\"\nspec = \"https://drafts.fxtf.org/css-masking-1/#propdef-mask-composite\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nvector = {}\nextra_prefixes = [\"webkit\"]\nkeyword = { values = [\"add\", \"subtract\", \"intersect\", \"exclude\"] }\n\n[table-layout]\nstruct = \"table\"\nspec = \"https://drafts.csswg.org/css-tables/#propdef-table-layout\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mLayoutStrategy\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"auto\", \"fixed\"] }\n\n[unicode-bidi]\nstruct = \"text\"\nspec = \"https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi\"\nanimation_type = \"none\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"normal\", \"embed\", \"isolate\", \"bidi-override\", \"isolate-override\", \"plaintext\"] }\n\n[text-decoration-style]\nstruct = \"text\"\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style\"\nanimation_type = \"discrete\"\naffects = \"overflow\"\nservo_restyle_damage = \"recalculate_overflow\"\nkeyword = { values = [\"solid\", \"double\", \"dotted\", \"dashed\", \"wavy\", \"-moz-none\"] }\n\n[ime-mode]\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-ui/#input-method-editor\"\nanimation_type = \"discrete\"\naffects = \"\"\ngecko_ffi_name = \"mIMEMode\"\nkeyword = { values = [\"auto\", \"normal\", \"active\", \"disabled\", \"inactive\"] }\n\n[scrollbar-width]\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nkeyword = { values = [\"auto\", \"thin\", \"none\"] }\n\n[-moz-window-dragging]\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"None (Nonstandard Firefox-only property)\"\nanimation_type = \"discrete\"\nenabled_in = \"chrome\"\naffects = \"paint\"\ngecko_ffi_name = \"mWindowDragging\"\nkeyword = { values = [\"default\", \"drag\", \"no-drag\"] }\n\n[-moz-window-shadow]\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"None (Nonstandard internal property)\"\nanimation_type = \"discrete\"\nenabled_in = \"chrome\"\naffects = \"overflow\"\ngecko_ffi_name = \"mWindowShadow\"\nkeyword = { values = [\"auto\", \"none\"] }\n\n[field-sizing]\nstruct = \"ui\"\nengine = \"gecko\"\nspec = \"https://drafts.csswg.org/css-ui/#field-sizing\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_pref = \"layout.css.field-sizing.enabled\"\nkeyword = { values = [\"fixed\", \"content\"] }\n\n[-moz-box-align]\nstruct = \"xul\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mBoxAlign\"\naliases = [\"-webkit-box-align\"]\nkeyword = { values = [\"stretch\", \"start\", \"center\", \"baseline\", \"end\"] }\n\n[-moz-box-direction]\nstruct = \"xul\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mBoxDirection\"\naliases = [\"-webkit-box-direction\"]\nkeyword = { values = [\"normal\", \"reverse\"] }\n\n[-moz-box-orient]\nstruct = \"xul\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mBoxOrient\"\naliases = [\"-webkit-box-orient\"]\nkeyword = { values = [\"horizontal\", \"vertical\"], gecko_aliases = [\"inline-axis=horizontal\", \"block-axis=vertical\"] }\n\n[-moz-box-pack]\nstruct = \"xul\"\nengine = \"gecko\"\nspec = \"Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)\"\nanimation_type = \"discrete\"\naffects = \"layout\"\ngecko_ffi_name = \"mBoxPack\"\naliases = [\"-webkit-box-pack\"]\nkeyword = { values = [\"start\", \"center\", \"end\", \"justify\"] }\n\n[mix-blend-mode]\nstruct = \"effects\"\nspec = \"https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode\"\nanimation_type = \"discrete\"\naffects = \"paint\"\nservo_restyle_damage = \"repaint\"\nkeyword = { values = [\"normal\", \"multiply\", \"screen\", \"overlay\", \"darken\", \"lighten\", \"color-dodge\", \"color-burn\", \"hard-light\", \"soft-light\", \"difference\", \"exclusion\", \"hue\", \"saturation\", \"color\", \"luminosity\", \"plus-lighter\"], gecko_enum_prefix = \"StyleBlend\" }\n\n[dominant-baseline]\ntype = \"DominantBaseline\"\ninitial = \"computed::DominantBaseline::Auto\"\nstruct = \"inherited_box\"\nengine = \"gecko\"\nanimation_type = \"discrete\"\nspec = \"https://drafts.csswg.org/css-inline-3/#dominant-baseline-property\"\naffects = \"layout\"\n\n[-moz-math-variant]\nstruct = \"font\"\nengine = \"gecko\"\nspec = \"Internal (not web-exposed)\"\nanimation_type = \"none\"\nenabled_in = \"\"\nhas_effect_on_gecko_scrollbars = false\naffects = \"layout\"\ngecko_ffi_name = \"mMathVariant\"\nkeyword = { values = [\"none\", \"normal\", \"bold\", \"italic\", \"bold-italic\", \"script\", \"bold-script\", \"fraktur\", \"double-struck\", \"bold-fraktur\", \"sans-serif\", \"bold-sans-serif\", \"sans-serif-italic\", \"sans-serif-bold-italic\", \"monospace\", \"initial\", \"tailed\", \"looped\", \"stretched\"] }\n\n[white-space-collapse]\nstruct = \"inherited_text\"\nspec = \"https://drafts.csswg.org/css-text-4/#propdef-white-space-collapse\"\nanimation_type = \"discrete\"\naffects = \"layout\"\nservo_restyle_damage = \"rebuild_box\"\nkeyword = { values = [\"collapse\", \"preserve\", \"preserve-breaks\", \"break-spaces\"], extra_gecko_values = [\"preserve-spaces\"], gecko_aliases = [\"-moz-pre-space=preserve-spaces\"] }\n\n[timeline-scope]\ntype = \"ScopedName\"\ninitial = \"computed::ScopedName::none()\"\nstruct = \"ui\"\nengine = \"gecko\"\nanimation_type = \"none\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\nspec = \"https://drafts.csswg.org/scroll-animations-1/#timeline-scope\"\naffects = \"\"\n"
  },
  {
    "path": "style/properties/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Supported CSS properties and the cascade.\n\npub mod cascade;\npub mod declaration_block;\npub mod shorthands;\n\npub use self::cascade::*;\npub use self::declaration_block::*;\npub use self::generated::*;\n\n/// The CSS properties supported by the style system.\n/// Generated from the properties.mako.rs template by build.rs\n#[macro_use]\n#[allow(unsafe_code)]\n#[deny(missing_docs)]\npub mod generated {\n    include!(concat!(env!(\"OUT_DIR\"), \"/properties.rs\"));\n}\n\nuse crate::applicable_declarations::RevertKind;\nuse crate::custom_properties::{self, ComputedSubstitutionFunctions, SubstitutionResult};\nuse crate::derives::*;\nuse crate::dom::AttributeTracker;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::{CSSPropertyId, NonCustomCSSPropertyId, RefPtr};\nuse crate::logical_geometry::WritingMode;\nuse crate::parser::ParserContext;\nuse crate::stylesheets::CssRuleType;\nuse crate::stylesheets::Origin;\nuse crate::stylist::Stylist;\nuse crate::values::{computed, serialize_atom_name};\nuse arrayvec::{ArrayVec, Drain as ArrayVecDrain};\nuse cssparser::{match_ignore_ascii_case, Parser, ParserInput};\nuse rustc_hash::FxHashMap;\nuse servo_arc::Arc;\nuse std::{\n    borrow::Cow,\n    fmt::{self, Write},\n    mem,\n};\nuse style_traits::{\n    CssString, CssWriter, KeywordsCollectFn, ParseError, ParsingMode, SpecifiedValueInfo, ToCss,\n    ToTyped, TypedValue,\n};\nuse thin_vec::ThinVec;\n\nbitflags! {\n    /// A set of flags for properties.\n    #[derive(Clone, Copy)]\n    pub struct PropertyFlags: u16 {\n        /// This longhand property applies to ::first-letter.\n        const APPLIES_TO_FIRST_LETTER = 1 << 1;\n        /// This longhand property applies to ::first-line.\n        const APPLIES_TO_FIRST_LINE = 1 << 2;\n        /// This longhand property applies to ::placeholder.\n        const APPLIES_TO_PLACEHOLDER = 1 << 3;\n        ///  This longhand property applies to ::cue.\n        const APPLIES_TO_CUE = 1 << 4;\n        /// This longhand property applies to ::marker.\n        const APPLIES_TO_MARKER = 1 << 5;\n        /// This property is a legacy shorthand.\n        ///\n        /// https://drafts.csswg.org/css-cascade/#legacy-shorthand\n        const IS_LEGACY_SHORTHAND = 1 << 6;\n\n        /* The following flags are currently not used in Rust code, they\n         * only need to be listed in corresponding properties so that\n         * they can be checked in the C++ side via ServoCSSPropList.h. */\n\n        /// This property can be animated on the compositor.\n        const CAN_ANIMATE_ON_COMPOSITOR = 0;\n        /// See data.py's documentation about the affects_flags.\n        const AFFECTS_LAYOUT = 0;\n        #[allow(missing_docs)]\n        const AFFECTS_OVERFLOW = 0;\n        #[allow(missing_docs)]\n        const AFFECTS_PAINT = 0;\n    }\n}\n\n/// An enum to represent a CSS Wide keyword.\n#[derive(\n    Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum CSSWideKeyword {\n    /// The `initial` keyword.\n    Initial,\n    /// The `inherit` keyword.\n    Inherit,\n    /// The `unset` keyword.\n    Unset,\n    /// The `revert` keyword.\n    Revert,\n    /// The `revert-layer` keyword.\n    RevertLayer,\n    /// The `revert-rule` keyword.\n    RevertRule,\n}\n\nimpl CSSWideKeyword {\n    /// Returns the string representation of the keyword.\n    pub fn to_str(&self) -> &'static str {\n        match *self {\n            Self::Initial => \"initial\",\n            Self::Inherit => \"inherit\",\n            Self::Unset => \"unset\",\n            Self::Revert => \"revert\",\n            Self::RevertLayer => \"revert-layer\",\n            Self::RevertRule => \"revert-rule\",\n        }\n    }\n\n    /// Parses a CSS wide keyword from a CSS identifier.\n    pub fn from_ident(ident: &str) -> Result<Self, ()> {\n        Ok(match_ignore_ascii_case! { ident,\n            \"initial\" => Self::Initial,\n            \"inherit\" => Self::Inherit,\n            \"unset\" => Self::Unset,\n            \"revert\" => Self::Revert,\n            \"revert-layer\" => Self::RevertLayer,\n            \"revert-rule\" if static_prefs::pref!(\"layout.css.revert-rule.enabled\") => Self::RevertRule,\n            _ => return Err(()),\n        })\n    }\n\n    /// Parses a CSS wide keyword completely.\n    pub fn parse(input: &mut Parser) -> Result<Self, ()> {\n        let keyword = {\n            let ident = input.expect_ident().map_err(|_| ())?;\n            Self::from_ident(ident)?\n        };\n        input.expect_exhausted().map_err(|_| ())?;\n        Ok(keyword)\n    }\n\n    /// Returns the revert kind for this wide keyword.\n    pub fn revert_kind(self) -> Option<RevertKind> {\n        Some(match self {\n            Self::Initial | Self::Inherit | Self::Unset => return None,\n            Self::Revert => RevertKind::Origin,\n            Self::RevertLayer => RevertKind::Layer,\n            Self::RevertRule => RevertKind::Rule,\n        })\n    }\n}\n\n/// A declaration using a CSS-wide keyword.\n#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf)]\npub struct WideKeywordDeclaration {\n    #[css(skip)]\n    id: LonghandId,\n    /// The CSS-wide keyword.\n    pub keyword: CSSWideKeyword,\n}\n\n// XXX Switch back to ToTyped derive once it can automatically handle structs\n// Tracking in bug 1991631\nimpl ToTyped for WideKeywordDeclaration {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        self.keyword.to_typed(dest)\n    }\n}\n\n/// An unparsed declaration that contains `var()` functions.\n#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]\npub struct VariableDeclaration {\n    /// The id of the property this declaration represents.\n    #[css(skip)]\n    id: LonghandId,\n    /// The unparsed value of the variable.\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub value: Arc<UnparsedValue>,\n}\n\n/// A custom property declaration value is either an unparsed value or a CSS\n/// wide-keyword.\n#[derive(Clone, PartialEq, ToCss, ToShmem)]\npub enum CustomDeclarationValue {\n    /// An unparsed value.\n    Unparsed(Arc<custom_properties::SpecifiedValue>),\n    /// An already-parsed value.\n    Parsed(Arc<crate::properties_and_values::value::SpecifiedValue>),\n    /// A wide keyword.\n    CSSWideKeyword(CSSWideKeyword),\n}\n\n/// A custom property declaration with the property name and the declared value.\n#[derive(Clone, PartialEq, ToCss, ToShmem, MallocSizeOf, ToTyped)]\n#[typed(todo_derive_fields)]\npub struct CustomDeclaration {\n    /// The name of the custom property.\n    #[css(skip)]\n    pub name: custom_properties::Name,\n    /// The value of the custom property.\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub value: CustomDeclarationValue,\n}\n\nimpl fmt::Debug for PropertyDeclaration {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.id().to_css(&mut CssWriter::new(f))?;\n        f.write_str(\": \")?;\n\n        // Because PropertyDeclaration::to_css requires CssStringWriter, we can't write\n        // it directly to f, and need to allocate an intermediate string. This is\n        // fine for debug-only code.\n        let mut s = CssString::new();\n        self.to_css(&mut s)?;\n        write!(f, \"{}\", s)\n    }\n}\n\n/// A longhand or shorthand property.\n#[derive(\n    Clone, Copy, Debug, PartialEq, Eq, Hash, ToComputedValue, ToResolvedValue, ToShmem, MallocSizeOf,\n)]\n#[repr(C)]\npub struct NonCustomPropertyId(u16);\n\nimpl ToCss for NonCustomPropertyId {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(self.name())\n    }\n}\n\nimpl NonCustomPropertyId {\n    /// Returns the underlying index, used for use counter.\n    pub fn bit(self) -> usize {\n        self.0 as usize\n    }\n\n    /// Convert a `NonCustomPropertyId` into a `NonCustomCSSPropertyId`.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {\n        // unsafe: guaranteed by static_assert_noncustomcsspropertyid.\n        unsafe { mem::transmute(self.0) }\n    }\n\n    /// Convert an `NonCustomCSSPropertyId` into a `NonCustomPropertyId`.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn from_noncustomcsspropertyid(prop: NonCustomCSSPropertyId) -> Option<Self> {\n        let prop = prop as u16;\n        if prop >= property_counts::NON_CUSTOM as u16 {\n            return None;\n        }\n        // guaranteed by static_assert_noncustomcsspropertyid above.\n        Some(NonCustomPropertyId(prop))\n    }\n\n    /// Resolves the alias of a given property if needed.\n    pub fn unaliased(self) -> Self {\n        let Some(alias_id) = self.as_alias() else {\n            return self;\n        };\n        alias_id.aliased_property()\n    }\n\n    /// Turns this `NonCustomPropertyId` into a `PropertyId`.\n    #[inline]\n    pub fn to_property_id(self) -> PropertyId {\n        PropertyId::NonCustom(self)\n    }\n\n    /// Returns a longhand id, if this property is one.\n    #[inline]\n    pub fn as_longhand(self) -> Option<LonghandId> {\n        if self.0 < property_counts::LONGHANDS as u16 {\n            return Some(unsafe { mem::transmute(self.0 as u16) });\n        }\n        None\n    }\n\n    /// Returns a shorthand id, if this property is one.\n    #[inline]\n    pub fn as_shorthand(self) -> Option<ShorthandId> {\n        if self.0 >= property_counts::LONGHANDS as u16\n            && self.0 < property_counts::LONGHANDS_AND_SHORTHANDS as u16\n        {\n            return Some(unsafe { mem::transmute(self.0 - (property_counts::LONGHANDS as u16)) });\n        }\n        None\n    }\n\n    /// Returns an alias id, if this property is one.\n    #[inline]\n    pub fn as_alias(self) -> Option<AliasId> {\n        debug_assert!((self.0 as usize) < property_counts::NON_CUSTOM);\n        if self.0 >= property_counts::LONGHANDS_AND_SHORTHANDS as u16 {\n            return Some(unsafe {\n                mem::transmute(self.0 - (property_counts::LONGHANDS_AND_SHORTHANDS as u16))\n            });\n        }\n        None\n    }\n\n    /// Returns either a longhand or a shorthand, resolving aliases.\n    #[inline]\n    pub fn longhand_or_shorthand(self) -> Result<LonghandId, ShorthandId> {\n        let id = self.unaliased();\n        match id.as_longhand() {\n            Some(lh) => Ok(lh),\n            None => Err(id.as_shorthand().unwrap()),\n        }\n    }\n\n    /// Converts a longhand id into a non-custom property id.\n    #[inline]\n    pub const fn from_longhand(id: LonghandId) -> Self {\n        Self(id as u16)\n    }\n\n    /// Converts a shorthand id into a non-custom property id.\n    #[inline]\n    pub const fn from_shorthand(id: ShorthandId) -> Self {\n        Self((id as u16) + (property_counts::LONGHANDS as u16))\n    }\n\n    /// Converts an alias id into a non-custom property id.\n    #[inline]\n    pub const fn from_alias(id: AliasId) -> Self {\n        Self((id as u16) + (property_counts::LONGHANDS_AND_SHORTHANDS as u16))\n    }\n}\n\nimpl From<LonghandId> for NonCustomPropertyId {\n    #[inline]\n    fn from(id: LonghandId) -> Self {\n        Self::from_longhand(id)\n    }\n}\n\nimpl From<ShorthandId> for NonCustomPropertyId {\n    #[inline]\n    fn from(id: ShorthandId) -> Self {\n        Self::from_shorthand(id)\n    }\n}\n\nimpl From<AliasId> for NonCustomPropertyId {\n    #[inline]\n    fn from(id: AliasId) -> Self {\n        Self::from_alias(id)\n    }\n}\n\n/// Representation of a CSS property, that is, either a longhand, a shorthand, or a custom\n/// property.\n#[derive(Clone, Eq, PartialEq, Debug)]\npub enum PropertyId {\n    /// An alias for a shorthand property.\n    NonCustom(NonCustomPropertyId),\n    /// A custom property.\n    Custom(custom_properties::Name),\n}\n\nimpl ToCss for PropertyId {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            PropertyId::NonCustom(id) => dest.write_str(id.name()),\n            PropertyId::Custom(ref name) => {\n                dest.write_str(\"--\")?;\n                serialize_atom_name(name, dest)\n            },\n        }\n    }\n}\n\nimpl PropertyId {\n    /// Return the longhand id that this property id represents.\n    #[inline]\n    pub fn longhand_id(&self) -> Option<LonghandId> {\n        self.non_custom_non_alias_id()?.as_longhand()\n    }\n\n    /// Returns true if this property is one of the animatable properties.\n    pub fn is_animatable(&self) -> bool {\n        match self {\n            Self::NonCustom(id) => id.is_animatable(),\n            Self::Custom(_) => true,\n        }\n    }\n\n    /// Returns a given property from the given name, _regardless of whether it is enabled or\n    /// not_, or Err(()) for unknown properties.\n    ///\n    /// Do not use for non-testing purposes.\n    pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {\n        Self::parse_unchecked(name, None)\n    }\n\n    /// Parses a property name, and returns an error if it's unknown or isn't enabled for all\n    /// content.\n    #[inline]\n    pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {\n        let id = Self::parse_unchecked(name, None)?;\n\n        if !id.enabled_for_all_content() {\n            return Err(());\n        }\n\n        Ok(id)\n    }\n\n    /// Parses a property name, and returns an error if it's unknown or isn't allowed in this\n    /// context.\n    #[inline]\n    pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {\n        let id = Self::parse_unchecked(name, context.use_counters)?;\n        if !id.allowed_in(context) {\n            return Err(());\n        }\n        Ok(id)\n    }\n\n    /// Parses a property name, and returns an error if it's unknown or isn't allowed in this\n    /// context, ignoring the rule_type checks.\n    ///\n    /// This is useful for parsing stuff from CSS values, for example.\n    #[inline]\n    pub fn parse_ignoring_rule_type(name: &str, context: &ParserContext) -> Result<Self, ()> {\n        let id = Self::parse_unchecked(name, None)?;\n        if !id.allowed_in_ignoring_rule_type(context) {\n            return Err(());\n        }\n        Ok(id)\n    }\n\n    /// Returns a property id from Gecko's NonCustomCSSPropertyId.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {\n        Some(NonCustomPropertyId::from_noncustomcsspropertyid(id)?.to_property_id())\n    }\n\n    /// Returns a property id from Gecko's CSSPropertyId.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {\n        Some(\n            if property.mId == NonCustomCSSPropertyId::eCSSPropertyExtra_variable {\n                debug_assert!(!property.mCustomName.mRawPtr.is_null());\n                Self::Custom(unsafe { crate::Atom::from_raw(property.mCustomName.mRawPtr) })\n            } else {\n                Self::NonCustom(NonCustomPropertyId::from_noncustomcsspropertyid(\n                    property.mId,\n                )?)\n            },\n        )\n    }\n\n    /// Returns true if the property is a shorthand or shorthand alias.\n    #[inline]\n    pub fn is_shorthand(&self) -> bool {\n        self.as_shorthand().is_ok()\n    }\n\n    /// Given this property id, get it either as a shorthand or as a\n    /// `PropertyDeclarationId`.\n    pub fn as_shorthand(&self) -> Result<ShorthandId, PropertyDeclarationId<'_>> {\n        match *self {\n            Self::NonCustom(id) => match id.longhand_or_shorthand() {\n                Ok(lh) => Err(PropertyDeclarationId::Longhand(lh)),\n                Err(sh) => Ok(sh),\n            },\n            Self::Custom(ref name) => Err(PropertyDeclarationId::Custom(name)),\n        }\n    }\n\n    /// Returns the `NonCustomPropertyId` corresponding to this property id.\n    pub fn non_custom_id(&self) -> Option<NonCustomPropertyId> {\n        match *self {\n            Self::Custom(_) => None,\n            Self::NonCustom(id) => Some(id),\n        }\n    }\n\n    /// Returns non-alias NonCustomPropertyId corresponding to this\n    /// property id.\n    fn non_custom_non_alias_id(&self) -> Option<NonCustomPropertyId> {\n        self.non_custom_id().map(NonCustomPropertyId::unaliased)\n    }\n\n    /// Whether the property is enabled for all content regardless of the\n    /// stylesheet it was declared on (that is, in practice only checks prefs).\n    #[inline]\n    pub fn enabled_for_all_content(&self) -> bool {\n        let id = match self.non_custom_id() {\n            // Custom properties are allowed everywhere\n            None => return true,\n            Some(id) => id,\n        };\n\n        id.enabled_for_all_content()\n    }\n\n    /// Converts this PropertyId in NonCustomCSSPropertyId, resolving aliases to the\n    /// resolved property, and returning eCSSPropertyExtra_variable for custom\n    /// properties.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_noncustomcsspropertyid_resolving_aliases(&self) -> NonCustomCSSPropertyId {\n        match self.non_custom_non_alias_id() {\n            Some(id) => id.to_noncustomcsspropertyid(),\n            None => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,\n        }\n    }\n\n    fn allowed_in(&self, context: &ParserContext) -> bool {\n        let id = match self.non_custom_id() {\n            // Custom properties are allowed everywhere, except `position-try`.\n            None => {\n                return !context\n                    .nesting_context\n                    .rule_types\n                    .contains(CssRuleType::PositionTry)\n            },\n            Some(id) => id,\n        };\n        id.allowed_in(context)\n    }\n\n    #[inline]\n    fn allowed_in_ignoring_rule_type(&self, context: &ParserContext) -> bool {\n        let id = match self.non_custom_id() {\n            // Custom properties are allowed everywhere\n            None => return true,\n            Some(id) => id,\n        };\n        id.allowed_in_ignoring_rule_type(context)\n    }\n\n    /// Whether the property supports the given CSS type.\n    /// `ty` should a bitflags of constants in style_traits::CssType.\n    pub fn supports_type(&self, ty: u8) -> bool {\n        let id = self.non_custom_non_alias_id();\n        id.map_or(0, |id| id.supported_types()) & ty != 0\n    }\n\n    /// Collect supported starting word of values of this property.\n    ///\n    /// See style_traits::SpecifiedValueInfo::collect_completion_keywords for more\n    /// details.\n    pub fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {\n        if let Some(id) = self.non_custom_non_alias_id() {\n            id.collect_property_completion_keywords(f);\n        }\n        CSSWideKeyword::collect_completion_keywords(f);\n    }\n}\n\nimpl ToCss for LonghandId {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(self.name())\n    }\n}\n\nimpl fmt::Debug for LonghandId {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        formatter.write_str(self.name())\n    }\n}\n\nimpl LonghandId {\n    /// Get the name of this longhand property.\n    #[inline]\n    pub fn name(&self) -> &'static str {\n        NonCustomPropertyId::from(*self).name()\n    }\n\n    /// Returns whether the longhand property is inherited by default.\n    #[inline]\n    pub fn inherited(self) -> bool {\n        !LonghandIdSet::reset().contains(self)\n    }\n\n    /// Returns whether the longhand property is zoom-dependent.\n    #[inline]\n    pub fn zoom_dependent(self) -> bool {\n        LonghandIdSet::zoom_dependent().contains(self)\n    }\n\n    /// Returns true if the property is one that is ignored when document\n    /// colors are disabled.\n    #[inline]\n    pub fn ignored_when_document_colors_disabled(self) -> bool {\n        LonghandIdSet::ignored_when_colors_disabled().contains(self)\n    }\n\n    /// Returns whether this longhand is `non_custom` or is a longhand of it.\n    pub fn is_or_is_longhand_of(self, non_custom: NonCustomPropertyId) -> bool {\n        match non_custom.longhand_or_shorthand() {\n            Ok(lh) => self == lh,\n            Err(sh) => self.is_longhand_of(sh),\n        }\n    }\n\n    /// Returns whether this longhand is a longhand of `shorthand`.\n    pub fn is_longhand_of(self, shorthand: ShorthandId) -> bool {\n        self.shorthands().any(|s| s == shorthand)\n    }\n\n    /// Returns whether this property is animatable.\n    #[inline]\n    pub fn is_animatable(self) -> bool {\n        NonCustomPropertyId::from(self).is_animatable()\n    }\n\n    /// Returns whether this property is animatable in a discrete way.\n    #[inline]\n    pub fn is_discrete_animatable(self) -> bool {\n        LonghandIdSet::discrete_animatable().contains(self)\n    }\n\n    /// Converts from a LonghandId to an adequate NonCustomCSSPropertyId.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {\n        NonCustomPropertyId::from(self).to_noncustomcsspropertyid()\n    }\n\n    #[cfg(feature = \"gecko\")]\n    /// Returns a longhand id from Gecko's NonCustomCSSPropertyId.\n    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {\n        NonCustomPropertyId::from_noncustomcsspropertyid(id)?\n            .unaliased()\n            .as_longhand()\n    }\n\n    /// Return whether this property is logical.\n    #[inline]\n    pub fn is_logical(self) -> bool {\n        LonghandIdSet::logical().contains(self)\n    }\n}\n\nimpl ToCss for ShorthandId {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(self.name())\n    }\n}\n\nimpl ShorthandId {\n    /// Get the name for this shorthand property.\n    #[inline]\n    pub fn name(&self) -> &'static str {\n        NonCustomPropertyId::from(*self).name()\n    }\n\n    /// Converts from a ShorthandId to an adequate NonCustomCSSPropertyId.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {\n        NonCustomPropertyId::from(self).to_noncustomcsspropertyid()\n    }\n\n    /// Converts from a NonCustomCSSPropertyId to a ShorthandId.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn from_noncustomcsspropertyid(id: NonCustomCSSPropertyId) -> Option<Self> {\n        NonCustomPropertyId::from_noncustomcsspropertyid(id)?\n            .unaliased()\n            .as_shorthand()\n    }\n\n    /// Finds and returns an appendable value for the given declarations.\n    ///\n    /// Returns the optional appendable value.\n    pub fn get_shorthand_appendable_value<'a, 'b: 'a>(\n        self,\n        declarations: &'a [&'b PropertyDeclaration],\n    ) -> Option<AppendableValue<'a, 'b>> {\n        let first_declaration = declarations.get(0)?;\n        let rest = || declarations.iter().skip(1);\n\n        // https://drafts.csswg.org/css-variables/#variables-in-shorthands\n        if let Some(css) = first_declaration.with_variables_from_shorthand(self) {\n            if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) {\n                return Some(AppendableValue::Css(css));\n            }\n            return None;\n        }\n\n        // Check whether they are all the same CSS-wide keyword.\n        if let Some(keyword) = first_declaration.get_css_wide_keyword() {\n            if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) {\n                return Some(AppendableValue::Css(keyword.to_str()));\n            }\n            return None;\n        }\n\n        if self == ShorthandId::All {\n            // 'all' only supports variables and CSS wide keywords.\n            return None;\n        }\n\n        // Check whether all declarations can be serialized as part of shorthand.\n        if declarations\n            .iter()\n            .all(|d| d.may_serialize_as_part_of_shorthand())\n        {\n            return Some(AppendableValue::DeclarationsForShorthand(\n                self,\n                declarations,\n            ));\n        }\n\n        None\n    }\n\n    /// Returns whether this property is a legacy shorthand.\n    #[inline]\n    pub fn is_legacy_shorthand(self) -> bool {\n        self.flags().contains(PropertyFlags::IS_LEGACY_SHORTHAND)\n    }\n}\n\n/// Return the names of arbitrary substitution functions that are enabled.\npub fn enabled_arbitrary_substitution_functions() -> &'static [&'static str] {\n    if static_prefs::pref!(\"layout.css.attr.enabled\") {\n        &[\"var\", \"env\", \"attr\"]\n    } else {\n        &[\"var\", \"env\"]\n    }\n}\n\nfn parse_non_custom_property_declaration_value_into<'i>(\n    declarations: &mut SourcePropertyDeclaration,\n    context: &ParserContext,\n    input: &mut Parser<'i, '_>,\n    start: &cssparser::ParserState,\n    parse_entirely_into: impl FnOnce(\n        &mut SourcePropertyDeclaration,\n        &mut Parser<'i, '_>,\n    ) -> Result<(), ParseError<'i>>,\n    parsed_wide_keyword: impl FnOnce(&mut SourcePropertyDeclaration, CSSWideKeyword),\n    parsed_custom: impl FnOnce(&mut SourcePropertyDeclaration, custom_properties::VariableValue),\n) -> Result<(), ParseError<'i>> {\n    let mut starts_with_curly_block = false;\n    if let Ok(token) = input.next() {\n        match token {\n            cssparser::Token::Ident(ref ident) => match CSSWideKeyword::from_ident(ident) {\n                Ok(wk) => {\n                    if input.expect_exhausted().is_ok() {\n                        return Ok(parsed_wide_keyword(declarations, wk));\n                    }\n                },\n                Err(()) => {},\n            },\n            cssparser::Token::CurlyBracketBlock => {\n                starts_with_curly_block = true;\n            },\n            _ => {},\n        }\n    };\n\n    input.reset(&start);\n    input.look_for_arbitrary_substitution_functions(enabled_arbitrary_substitution_functions());\n\n    let err = match parse_entirely_into(declarations, input) {\n        Ok(()) => {\n            input.seen_arbitrary_substitution_functions();\n            return Ok(());\n        },\n        Err(e) => e,\n    };\n\n    // Look for var(), env() and top-level curly blocks after the error.\n    let start_pos = start.position();\n    let mut at_start = start_pos == input.position();\n    let mut invalid = false;\n    while let Ok(token) = input.next() {\n        if matches!(token, cssparser::Token::CurlyBracketBlock) {\n            if !starts_with_curly_block || !at_start {\n                invalid = true;\n                break;\n            }\n        } else if starts_with_curly_block {\n            invalid = true;\n            break;\n        }\n        at_start = false;\n    }\n    if !input.seen_arbitrary_substitution_functions() || invalid {\n        return Err(err);\n    }\n    input.reset(start);\n    let value = custom_properties::VariableValue::parse(\n        input,\n        Some(&context.namespaces.prefixes),\n        &context.url_data,\n    )?;\n    parsed_custom(declarations, value);\n    Ok(())\n}\n\nimpl PropertyDeclaration {\n    fn with_variables_from_shorthand(&self, shorthand: ShorthandId) -> Option<&str> {\n        match *self {\n            PropertyDeclaration::WithVariables(ref declaration) => {\n                let s = declaration.value.from_shorthand?;\n                if s != shorthand {\n                    return None;\n                }\n                Some(&*declaration.value.variable_value.css)\n            },\n            _ => None,\n        }\n    }\n\n    /// Returns a CSS-wide keyword declaration for a given property.\n    #[inline]\n    pub fn css_wide_keyword(id: LonghandId, keyword: CSSWideKeyword) -> Self {\n        Self::CSSWideKeyword(WideKeywordDeclaration { id, keyword })\n    }\n\n    /// Returns a CSS-wide keyword if the declaration's value is one.\n    #[inline]\n    pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {\n        match *self {\n            PropertyDeclaration::CSSWideKeyword(ref declaration) => Some(declaration.keyword),\n            _ => None,\n        }\n    }\n\n    /// Returns whether the declaration may be serialized as part of a shorthand.\n    ///\n    /// This method returns false if this declaration contains variable or has a\n    /// CSS-wide keyword value, since these values cannot be serialized as part\n    /// of a shorthand.\n    ///\n    /// Caller should check `with_variables_from_shorthand()` and whether all\n    /// needed declarations has the same CSS-wide keyword first.\n    ///\n    /// Note that, serialization of a shorthand may still fail because of other\n    /// property-specific requirement even when this method returns true for all\n    /// the longhand declarations.\n    pub fn may_serialize_as_part_of_shorthand(&self) -> bool {\n        match *self {\n            PropertyDeclaration::CSSWideKeyword(..) | PropertyDeclaration::WithVariables(..) => {\n                false\n            },\n            PropertyDeclaration::Custom(..) => {\n                unreachable!(\"Serializing a custom property as part of shorthand?\")\n            },\n            _ => true,\n        }\n    }\n\n    /// Returns true if this property declaration is for one of the animatable properties.\n    pub fn is_animatable(&self) -> bool {\n        self.id().is_animatable()\n    }\n\n    /// Returns true if this property is a custom property, false\n    /// otherwise.\n    pub fn is_custom(&self) -> bool {\n        matches!(*self, PropertyDeclaration::Custom(..))\n    }\n\n    /// The `context` parameter controls this:\n    ///\n    /// <https://drafts.csswg.org/css-animations/#keyframes>\n    /// > The <declaration-list> inside of <keyframe-block> accepts any CSS property\n    /// > except those defined in this specification,\n    /// > but does accept the `animation-play-state` property and interprets it specially.\n    ///\n    /// This will not actually parse Importance values, and will always set things\n    /// to Importance::Normal. Parsing Importance values is the job of PropertyDeclarationParser,\n    /// we only set them here so that we don't have to reallocate\n    pub fn parse_into<'i, 't>(\n        declarations: &mut SourcePropertyDeclaration,\n        id: PropertyId,\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(), ParseError<'i>> {\n        assert!(declarations.is_empty());\n        debug_assert!(id.allowed_in(context), \"{:?}\", id);\n        input.skip_whitespace();\n\n        let start = input.state();\n        let non_custom_id = match id {\n            PropertyId::Custom(property_name) => {\n                let value = match input.try_parse(CSSWideKeyword::parse) {\n                    Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword),\n                    Err(()) => CustomDeclarationValue::Unparsed(Arc::new(\n                        custom_properties::VariableValue::parse(\n                            input,\n                            Some(&context.namespaces.prefixes),\n                            &context.url_data,\n                        )?,\n                    )),\n                };\n                declarations.push(PropertyDeclaration::Custom(CustomDeclaration {\n                    name: property_name,\n                    value,\n                }));\n                return Ok(());\n            },\n            PropertyId::NonCustom(id) => id,\n        };\n        match non_custom_id.longhand_or_shorthand() {\n            Ok(longhand_id) => {\n                parse_non_custom_property_declaration_value_into(\n                    declarations,\n                    context,\n                    input,\n                    &start,\n                    |declarations, input| {\n                        let decl = input\n                            .parse_entirely(|input| longhand_id.parse_value(context, input))?;\n                        declarations.push(decl);\n                        Ok(())\n                    },\n                    |declarations, wk| {\n                        declarations.push(PropertyDeclaration::css_wide_keyword(longhand_id, wk));\n                    },\n                    |declarations, variable_value| {\n                        declarations.push(PropertyDeclaration::WithVariables(VariableDeclaration {\n                            id: longhand_id,\n                            value: Arc::new(UnparsedValue {\n                                variable_value,\n                                from_shorthand: None,\n                            }),\n                        }))\n                    },\n                )?;\n            },\n            Err(shorthand_id) => {\n                parse_non_custom_property_declaration_value_into(\n                    declarations,\n                    context,\n                    input,\n                    &start,\n                    // Not using parse_entirely here: each ShorthandId::parse_into function needs\n                    // to do so *before* pushing to `declarations`.\n                    |declarations, input| shorthand_id.parse_into(declarations, context, input),\n                    |declarations, wk| {\n                        if shorthand_id == ShorthandId::All {\n                            declarations.all_shorthand = AllShorthand::CSSWideKeyword(wk)\n                        } else {\n                            for longhand in shorthand_id.longhands() {\n                                declarations\n                                    .push(PropertyDeclaration::css_wide_keyword(longhand, wk));\n                            }\n                        }\n                    },\n                    |declarations, variable_value| {\n                        let unparsed = Arc::new(UnparsedValue {\n                            variable_value,\n                            from_shorthand: Some(shorthand_id),\n                        });\n                        if shorthand_id == ShorthandId::All {\n                            declarations.all_shorthand = AllShorthand::WithVariables(unparsed)\n                        } else {\n                            for id in shorthand_id.longhands() {\n                                declarations.push(PropertyDeclaration::WithVariables(\n                                    VariableDeclaration {\n                                        id,\n                                        value: unparsed.clone(),\n                                    },\n                                ))\n                            }\n                        }\n                    },\n                )?;\n            },\n        }\n        if let Some(use_counters) = context.use_counters {\n            use_counters.non_custom_properties.record(non_custom_id);\n        }\n        Ok(())\n    }\n}\n\n/// A PropertyDeclarationId without references, for use as a hash map key.\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub enum OwnedPropertyDeclarationId {\n    /// A longhand.\n    Longhand(LonghandId),\n    /// A custom property declaration.\n    Custom(custom_properties::Name),\n}\n\nimpl OwnedPropertyDeclarationId {\n    /// Return whether this property is logical.\n    #[inline]\n    pub fn is_logical(&self) -> bool {\n        self.as_borrowed().is_logical()\n    }\n\n    /// Returns the corresponding PropertyDeclarationId.\n    #[inline]\n    pub fn as_borrowed(&self) -> PropertyDeclarationId<'_> {\n        match self {\n            Self::Longhand(id) => PropertyDeclarationId::Longhand(*id),\n            Self::Custom(name) => PropertyDeclarationId::Custom(name),\n        }\n    }\n\n    /// Convert an `CSSPropertyId` into an `OwnedPropertyDeclarationId`.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn from_gecko_css_property_id(property: &CSSPropertyId) -> Option<Self> {\n        Some(match PropertyId::from_gecko_css_property_id(property)? {\n            PropertyId::Custom(name) => Self::Custom(name),\n            PropertyId::NonCustom(id) => Self::Longhand(id.as_longhand()?),\n        })\n    }\n}\n\n/// An identifier for a given property declaration, which can be either a\n/// longhand or a custom property.\n#[derive(Clone, Copy, Debug, PartialEq, MallocSizeOf)]\npub enum PropertyDeclarationId<'a> {\n    /// A longhand.\n    Longhand(LonghandId),\n    /// A custom property declaration.\n    Custom(&'a custom_properties::Name),\n}\n\nimpl<'a> ToCss for PropertyDeclarationId<'a> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            PropertyDeclarationId::Longhand(id) => dest.write_str(id.name()),\n            PropertyDeclarationId::Custom(name) => {\n                dest.write_str(\"--\")?;\n                serialize_atom_name(name, dest)\n            },\n        }\n    }\n}\n\nimpl<'a> PropertyDeclarationId<'a> {\n    /// Returns PropertyFlags for given property.\n    #[inline(always)]\n    pub fn flags(&self) -> PropertyFlags {\n        match self {\n            Self::Longhand(id) => id.flags(),\n            Self::Custom(_) => PropertyFlags::empty(),\n        }\n    }\n\n    /// Convert to an OwnedPropertyDeclarationId.\n    pub fn to_owned(&self) -> OwnedPropertyDeclarationId {\n        match self {\n            PropertyDeclarationId::Longhand(id) => OwnedPropertyDeclarationId::Longhand(*id),\n            PropertyDeclarationId::Custom(name) => {\n                OwnedPropertyDeclarationId::Custom((*name).clone())\n            },\n        }\n    }\n\n    /// Whether a given declaration id is either the same as `other`, or a\n    /// longhand of it.\n    pub fn is_or_is_longhand_of(&self, other: &PropertyId) -> bool {\n        match *self {\n            PropertyDeclarationId::Longhand(id) => match *other {\n                PropertyId::NonCustom(non_custom_id) => id.is_or_is_longhand_of(non_custom_id),\n                PropertyId::Custom(_) => false,\n            },\n            PropertyDeclarationId::Custom(name) => {\n                matches!(*other, PropertyId::Custom(ref other_name) if name == other_name)\n            },\n        }\n    }\n\n    /// Whether a given declaration id is a longhand belonging to this\n    /// shorthand.\n    pub fn is_longhand_of(&self, shorthand: ShorthandId) -> bool {\n        match *self {\n            PropertyDeclarationId::Longhand(ref id) => id.is_longhand_of(shorthand),\n            _ => false,\n        }\n    }\n\n    /// Returns the name of the property without CSS escaping.\n    pub fn name(&self) -> Cow<'static, str> {\n        match *self {\n            PropertyDeclarationId::Longhand(id) => id.name().into(),\n            PropertyDeclarationId::Custom(name) => {\n                let mut s = String::new();\n                write!(&mut s, \"--{}\", name).unwrap();\n                s.into()\n            },\n        }\n    }\n\n    /// Returns longhand id if it is, None otherwise.\n    #[inline]\n    pub fn as_longhand(&self) -> Option<LonghandId> {\n        match *self {\n            PropertyDeclarationId::Longhand(id) => Some(id),\n            _ => None,\n        }\n    }\n\n    /// Return whether this property is logical.\n    #[inline]\n    pub fn is_logical(&self) -> bool {\n        match self {\n            PropertyDeclarationId::Longhand(id) => id.is_logical(),\n            PropertyDeclarationId::Custom(_) => false,\n        }\n    }\n\n    /// If this is a logical property, return the corresponding physical one in\n    /// the given writing mode.\n    ///\n    /// Otherwise, return unchanged.\n    #[inline]\n    pub fn to_physical(&self, wm: WritingMode) -> Self {\n        match self {\n            Self::Longhand(id) => Self::Longhand(id.to_physical(wm)),\n            Self::Custom(_) => self.clone(),\n        }\n    }\n\n    /// Returns whether this property is animatable.\n    #[inline]\n    pub fn is_animatable(&self) -> bool {\n        match self {\n            Self::Longhand(id) => id.is_animatable(),\n            Self::Custom(_) => true,\n        }\n    }\n\n    /// Returns whether this property is animatable in a discrete way.\n    #[inline]\n    pub fn is_discrete_animatable(&self) -> bool {\n        match self {\n            Self::Longhand(longhand) => longhand.is_discrete_animatable(),\n            // TODO(bug 1885995): Refine this.\n            Self::Custom(_) => true,\n        }\n    }\n\n    /// Converts from a to an adequate NonCustomCSSPropertyId, returning\n    /// eCSSPropertyExtra_variable for custom properties.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_noncustomcsspropertyid(self) -> NonCustomCSSPropertyId {\n        match self {\n            PropertyDeclarationId::Longhand(id) => id.to_noncustomcsspropertyid(),\n            PropertyDeclarationId::Custom(_) => NonCustomCSSPropertyId::eCSSPropertyExtra_variable,\n        }\n    }\n\n    /// Convert a `PropertyDeclarationId` into an `CSSPropertyId`\n    ///\n    /// FIXME(emilio, bug 1870107): We should consider using cbindgen to generate the property id\n    /// representation or so.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_gecko_css_property_id(&self) -> CSSPropertyId {\n        match self {\n            Self::Longhand(id) => CSSPropertyId {\n                mId: id.to_noncustomcsspropertyid(),\n                mCustomName: RefPtr::null(),\n            },\n            Self::Custom(name) => {\n                let mut property_id = CSSPropertyId {\n                    mId: NonCustomCSSPropertyId::eCSSPropertyExtra_variable,\n                    mCustomName: RefPtr::null(),\n                };\n                property_id.mCustomName.mRawPtr = (*name).clone().into_addrefed();\n                property_id\n            },\n        }\n    }\n}\n\n/// A set of all properties.\n#[derive(Clone, PartialEq, Default)]\npub struct NonCustomPropertyIdSet {\n    storage: [u32; ((property_counts::NON_CUSTOM as usize) - 1 + 32) / 32],\n}\n\nimpl NonCustomPropertyIdSet {\n    /// Creates an empty `NonCustomPropertyIdSet`.\n    pub fn new() -> Self {\n        Self {\n            storage: Default::default(),\n        }\n    }\n\n    /// Insert a non-custom-property in the set.\n    #[inline]\n    pub fn insert(&mut self, id: NonCustomPropertyId) {\n        let bit = id.0 as usize;\n        self.storage[bit / 32] |= 1 << (bit % 32);\n    }\n\n    /// Return whether the given property is in the set\n    #[inline]\n    pub fn contains(&self, id: NonCustomPropertyId) -> bool {\n        let bit = id.0 as usize;\n        (self.storage[bit / 32] & (1 << (bit % 32))) != 0\n    }\n}\n\n/// A set of longhand properties\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]\npub struct LonghandIdSet {\n    storage: [u32; ((property_counts::LONGHANDS as usize) - 1 + 32) / 32],\n}\n\nto_shmem::impl_trivial_to_shmem!(LonghandIdSet);\n\nimpl LonghandIdSet {\n    /// Return an empty LonghandIdSet.\n    #[inline]\n    pub fn new() -> Self {\n        Self {\n            storage: Default::default(),\n        }\n    }\n\n    /// Iterate over the current longhand id set.\n    pub fn iter(&self) -> LonghandIdSetIterator<'_> {\n        LonghandIdSetIterator {\n            chunks: &self.storage,\n            cur_chunk: 0,\n            cur_bit: 0,\n        }\n    }\n\n    /// Returns whether this set contains at least every longhand that `other`\n    /// also contains.\n    pub fn contains_all(&self, other: &Self) -> bool {\n        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {\n            if (*self_cell & *other_cell) != *other_cell {\n                return false;\n            }\n        }\n        true\n    }\n\n    /// Returns whether this set contains any longhand that `other` also contains.\n    pub fn contains_any(&self, other: &Self) -> bool {\n        for (self_cell, other_cell) in self.storage.iter().zip(other.storage.iter()) {\n            if (*self_cell & *other_cell) != 0 {\n                return true;\n            }\n        }\n        false\n    }\n\n    /// Remove all the given properties from the set.\n    #[inline]\n    pub fn remove_all(&mut self, other: &Self) {\n        for (self_cell, other_cell) in self.storage.iter_mut().zip(other.storage.iter()) {\n            *self_cell &= !*other_cell;\n        }\n    }\n\n    /// Return whether the given property is in the set\n    #[inline]\n    pub fn contains(&self, id: LonghandId) -> bool {\n        let bit = id as usize;\n        (self.storage[bit / 32] & (1 << (bit % 32))) != 0\n    }\n\n    /// Return whether this set contains any reset longhand.\n    #[inline]\n    pub fn contains_any_reset(&self) -> bool {\n        self.contains_any(Self::reset())\n    }\n\n    /// Add the given property to the set\n    #[inline]\n    pub fn insert(&mut self, id: LonghandId) {\n        let bit = id as usize;\n        self.storage[bit / 32] |= 1 << (bit % 32);\n    }\n\n    /// Remove the given property from the set\n    #[inline]\n    pub fn remove(&mut self, id: LonghandId) {\n        let bit = id as usize;\n        self.storage[bit / 32] &= !(1 << (bit % 32));\n    }\n\n    /// Clear all bits\n    #[inline]\n    pub fn clear(&mut self) {\n        for cell in &mut self.storage {\n            *cell = 0\n        }\n    }\n\n    /// Returns whether the set is empty.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.storage.iter().all(|c| *c == 0)\n    }\n}\n\n/// An iterator over a set of longhand ids.\npub struct LonghandIdSetIterator<'a> {\n    chunks: &'a [u32],\n    cur_chunk: u32,\n    cur_bit: u32, // [0..31], note that zero means the end-most bit\n}\n\nimpl<'a> Iterator for LonghandIdSetIterator<'a> {\n    type Item = LonghandId;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            debug_assert!(self.cur_bit < 32);\n            let cur_chunk = self.cur_chunk;\n            let cur_bit = self.cur_bit;\n            let chunk = *self.chunks.get(cur_chunk as usize)?;\n            let next_bit = (chunk >> cur_bit).trailing_zeros();\n            if next_bit == 32 {\n                // Totally empty chunk, skip it.\n                self.cur_bit = 0;\n                self.cur_chunk += 1;\n                continue;\n            }\n            debug_assert!(cur_bit + next_bit < 32);\n            let longhand_id = cur_chunk * 32 + cur_bit + next_bit;\n            debug_assert!(longhand_id as usize <= property_counts::LONGHANDS);\n            let id: LonghandId = unsafe { mem::transmute(longhand_id as u16) };\n            self.cur_bit += next_bit + 1;\n            if self.cur_bit == 32 {\n                self.cur_bit = 0;\n                self.cur_chunk += 1;\n            }\n            return Some(id);\n        }\n    }\n}\n\n/// An ArrayVec of subproperties, contains space for the longest shorthand except all.\npub type SubpropertiesVec<T> = ArrayVec<T, { property_counts::MAX_SHORTHAND_EXPANDED }>;\n\n/// A stack-allocated vector of `PropertyDeclaration`\n/// large enough to parse one CSS `key: value` declaration.\n/// (Shorthands expand to multiple `PropertyDeclaration`s.)\n#[derive(Default)]\npub struct SourcePropertyDeclaration {\n    /// The storage for the actual declarations (except for all).\n    pub declarations: SubpropertiesVec<PropertyDeclaration>,\n    /// Stored separately to keep SubpropertiesVec smaller.\n    pub all_shorthand: AllShorthand,\n}\n\n// This is huge, but we allocate it on the stack and then never move it,\n// we only pass `&mut SourcePropertyDeclaration` references around.\n#[cfg(feature = \"gecko\")]\nsize_of_test!(SourcePropertyDeclaration, 632);\n#[cfg(feature = \"servo\")]\nsize_of_test!(SourcePropertyDeclaration, 568);\n\nimpl SourcePropertyDeclaration {\n    /// Create one with a single PropertyDeclaration.\n    #[inline]\n    pub fn with_one(decl: PropertyDeclaration) -> Self {\n        let mut result = Self::default();\n        result.declarations.push(decl);\n        result\n    }\n\n    /// Similar to Vec::drain: leaves this empty when the return value is dropped.\n    pub fn drain(&mut self) -> SourcePropertyDeclarationDrain<'_> {\n        SourcePropertyDeclarationDrain {\n            declarations: self.declarations.drain(..),\n            all_shorthand: mem::replace(&mut self.all_shorthand, AllShorthand::NotSet),\n        }\n    }\n\n    /// Reset to initial state\n    pub fn clear(&mut self) {\n        self.declarations.clear();\n        self.all_shorthand = AllShorthand::NotSet;\n    }\n\n    /// Whether we're empty.\n    pub fn is_empty(&self) -> bool {\n        self.declarations.is_empty() && matches!(self.all_shorthand, AllShorthand::NotSet)\n    }\n\n    /// Push a single declaration.\n    pub fn push(&mut self, declaration: PropertyDeclaration) {\n        let _result = self.declarations.try_push(declaration);\n        debug_assert!(_result.is_ok());\n    }\n}\n\n/// Return type of SourcePropertyDeclaration::drain\npub struct SourcePropertyDeclarationDrain<'a> {\n    /// A drain over the non-all declarations.\n    pub declarations:\n        ArrayVecDrain<'a, PropertyDeclaration, { property_counts::MAX_SHORTHAND_EXPANDED }>,\n    /// The all shorthand that was set.\n    pub all_shorthand: AllShorthand,\n}\n\n/// An unparsed property value that contains `var()` functions.\n#[derive(Debug, Eq, PartialEq, ToShmem)]\npub struct UnparsedValue {\n    /// The variable value, references and so on.\n    pub(super) variable_value: custom_properties::VariableValue,\n    /// The shorthand this came from.\n    from_shorthand: Option<ShorthandId>,\n}\n\nimpl ToCss for UnparsedValue {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        // https://drafts.csswg.org/css-variables/#variables-in-shorthands\n        if self.from_shorthand.is_none() {\n            self.variable_value.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl ToTyped for UnparsedValue {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        if self.from_shorthand.is_none() {\n            self.variable_value.to_typed(dest)?;\n            return Ok(());\n        }\n        Err(())\n    }\n}\n\n/// A simple cache for properties that come from a shorthand and have variable\n/// references.\n///\n/// This cache works because of the fact that you can't have competing values\n/// for a given longhand coming from the same shorthand (but note that this is\n/// why the shorthand needs to be part of the cache key).\npub type ShorthandsWithPropertyReferencesCache =\n    FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;\n\nimpl UnparsedValue {\n    fn substitute_variables<'cache>(\n        &self,\n        longhand_id: LonghandId,\n        substitution_functions: &ComputedSubstitutionFunctions,\n        stylist: &Stylist,\n        computed_context: &computed::Context,\n        shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> Cow<'cache, PropertyDeclaration> {\n        let invalid_at_computed_value_time = || {\n            let keyword = if longhand_id.inherited() {\n                CSSWideKeyword::Inherit\n            } else {\n                CSSWideKeyword::Initial\n            };\n            Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))\n        };\n\n        if computed_context\n            .builder\n            .invalid_non_custom_properties\n            .contains(longhand_id)\n        {\n            return invalid_at_computed_value_time();\n        }\n\n        if let Some(shorthand_id) = self.from_shorthand {\n            let key = (shorthand_id, longhand_id);\n            if shorthand_cache.contains_key(&key) {\n                // FIXME: This double lookup should be avoidable, but rustc\n                // doesn't like that, see:\n                //\n                // https://github.com/rust-lang/rust/issues/82146\n                return Cow::Borrowed(&shorthand_cache[&key]);\n            }\n        }\n\n        let SubstitutionResult { css, attr_taint } = match custom_properties::substitute(\n            &self.variable_value,\n            substitution_functions,\n            stylist,\n            computed_context,\n            attribute_tracker,\n        ) {\n            Ok(css) => css,\n            Err(..) => return invalid_at_computed_value_time(),\n        };\n\n        // As of this writing, only the base URL is used for property\n        // values.\n        //\n        // NOTE(emilio): we intentionally pase `None` as the rule type here.\n        // If something starts depending on it, it's probably a bug, since\n        // it'd change how values are parsed depending on whether we're in a\n        // @keyframes rule or not, for example... So think twice about\n        // whether you want to do this!\n        //\n        // FIXME(emilio): ParsingMode is slightly fishy...\n        let context = ParserContext::new(\n            Origin::Author,\n            &self.variable_value.url_data,\n            None,\n            ParsingMode::DEFAULT,\n            computed_context.quirks_mode,\n            /* namespaces = */ Default::default(),\n            None,\n            None,\n            attr_taint,\n        );\n\n        let mut input = ParserInput::new(&css);\n        let mut input = Parser::new(&mut input);\n        input.skip_whitespace();\n\n        if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {\n            return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));\n        }\n\n        let shorthand = match self.from_shorthand {\n            None => {\n                return match input.parse_entirely(|input| longhand_id.parse_value(&context, input))\n                {\n                    Ok(decl) => Cow::Owned(decl),\n                    Err(..) => invalid_at_computed_value_time(),\n                }\n            },\n            Some(shorthand) => shorthand,\n        };\n\n        let mut decls = SourcePropertyDeclaration::default();\n        // parse_into takes care of doing `parse_entirely` for us.\n        if shorthand\n            .parse_into(&mut decls, &context, &mut input)\n            .is_err()\n        {\n            return invalid_at_computed_value_time();\n        }\n\n        for declaration in decls.declarations.drain(..) {\n            let longhand = declaration.id().as_longhand().unwrap();\n            if longhand.is_logical() {\n                let writing_mode = computed_context.builder.writing_mode;\n                shorthand_cache.insert(\n                    (shorthand, longhand.to_physical(writing_mode)),\n                    declaration.clone(),\n                );\n            }\n            shorthand_cache.insert((shorthand, longhand), declaration);\n        }\n\n        let key = (shorthand, longhand_id);\n        match shorthand_cache.get(&key) {\n            Some(decl) => Cow::Borrowed(decl),\n            // NOTE: Under normal circumstances we should always have a value, but when prefs\n            // change we might hit this case. Consider something like `animation-timeline`, which\n            // is a conditionally-enabled longhand of `animation`:\n            //\n            // If we have a sheet with `animation: var(--foo)`, and the `animation-timeline` pref\n            // enabled, then that expands to an `animation-timeline` declaration at parse time.\n            //\n            // If the user disables the pref and, some time later, we get here wanting to compute\n            // `animation-timeline`, parse_into won't generate any declaration for it anymore, so\n            // we haven't inserted in the cache. Computing to invalid / initial seems like the most\n            // sensible thing to do here.\n            None => invalid_at_computed_value_time(),\n        }\n    }\n}\n/// A parsed all-shorthand value.\npub enum AllShorthand {\n    /// Not present.\n    NotSet,\n    /// A CSS-wide keyword.\n    CSSWideKeyword(CSSWideKeyword),\n    /// An all shorthand with var() references that we can't resolve right now.\n    WithVariables(Arc<UnparsedValue>),\n}\n\nimpl Default for AllShorthand {\n    fn default() -> Self {\n        Self::NotSet\n    }\n}\n\nimpl AllShorthand {\n    /// Iterates property declarations from the given all shorthand value.\n    #[inline]\n    pub fn declarations(&self) -> AllShorthandDeclarationIterator<'_> {\n        AllShorthandDeclarationIterator {\n            all_shorthand: self,\n            longhands: ShorthandId::All.longhands(),\n        }\n    }\n}\n\n/// An iterator over the all shorthand's shorthand declarations.\npub struct AllShorthandDeclarationIterator<'a> {\n    all_shorthand: &'a AllShorthand,\n    longhands: NonCustomPropertyIterator<LonghandId>,\n}\n\nimpl<'a> Iterator for AllShorthandDeclarationIterator<'a> {\n    type Item = PropertyDeclaration;\n\n    #[inline]\n    fn next(&mut self) -> Option<Self::Item> {\n        match *self.all_shorthand {\n            AllShorthand::NotSet => None,\n            AllShorthand::CSSWideKeyword(ref keyword) => Some(\n                PropertyDeclaration::css_wide_keyword(self.longhands.next()?, *keyword),\n            ),\n            AllShorthand::WithVariables(ref unparsed) => {\n                Some(PropertyDeclaration::WithVariables(VariableDeclaration {\n                    id: self.longhands.next()?,\n                    value: unparsed.clone(),\n                }))\n            },\n        }\n    }\n}\n\n/// An iterator over all the property ids that are enabled for a given\n/// shorthand, if that shorthand is enabled for all content too.\npub struct NonCustomPropertyIterator<Item: 'static> {\n    filter: bool,\n    iter: std::slice::Iter<'static, Item>,\n}\n\nimpl<Item> Iterator for NonCustomPropertyIterator<Item>\nwhere\n    Item: 'static + Copy + Into<NonCustomPropertyId>,\n{\n    type Item = Item;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        loop {\n            let id = *self.iter.next()?;\n            if !self.filter || id.into().enabled_for_all_content() {\n                return Some(id);\n            }\n        }\n    }\n}\n\n/// An iterator over all the properties that transition on a given style.\npub struct TransitionPropertyIterator<'a> {\n    style: &'a ComputedValues,\n    index_range: core::ops::Range<usize>,\n    longhand_iterator: Option<NonCustomPropertyIterator<LonghandId>>,\n}\n\nimpl<'a> TransitionPropertyIterator<'a> {\n    /// Create a `TransitionPropertyIterator` for the given style.\n    pub fn from_style(style: &'a ComputedValues) -> Self {\n        Self {\n            style,\n            index_range: 0..style.get_ui().transition_property_count(),\n            longhand_iterator: None,\n        }\n    }\n}\n\n/// A single iteration of the TransitionPropertyIterator.\npub struct TransitionPropertyIteration {\n    /// The id of the longhand for this property.\n    pub property: OwnedPropertyDeclarationId,\n    /// The index of this property in the list of transition properties for this iterator's\n    /// style.\n    pub index: usize,\n}\n\nimpl<'a> Iterator for TransitionPropertyIterator<'a> {\n    type Item = TransitionPropertyIteration;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        use crate::values::computed::TransitionProperty;\n        loop {\n            if let Some(ref mut longhand_iterator) = self.longhand_iterator {\n                if let Some(longhand_id) = longhand_iterator.next() {\n                    return Some(TransitionPropertyIteration {\n                        property: OwnedPropertyDeclarationId::Longhand(longhand_id),\n                        index: self.index_range.start - 1,\n                    });\n                }\n                self.longhand_iterator = None;\n            }\n\n            let index = self.index_range.next()?;\n            match self.style.get_ui().transition_property_at(index) {\n                TransitionProperty::NonCustom(id) => {\n                    match id.longhand_or_shorthand() {\n                        Ok(longhand_id) => {\n                            return Some(TransitionPropertyIteration {\n                                property: OwnedPropertyDeclarationId::Longhand(longhand_id),\n                                index,\n                            });\n                        },\n                        Err(shorthand_id) => {\n                            // In the other cases, we set up our state so that we are ready to\n                            // compute the next value of the iterator and then loop (equivalent\n                            // to calling self.next()).\n                            self.longhand_iterator = Some(shorthand_id.longhands());\n                        },\n                    }\n                },\n                TransitionProperty::Custom(name) => {\n                    return Some(TransitionPropertyIteration {\n                        property: OwnedPropertyDeclarationId::Custom(name),\n                        index,\n                    })\n                },\n                TransitionProperty::Unsupported(..) => {},\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "style/properties/properties.html.mako",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Supported CSS properties in Servo</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../normalize.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../rustdoc.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"../light.css\">\n</head>\n<body class=\"rustdoc\">\n    <section id='main' class=\"content mod\">\n      <h1 class='fqn'><span class='in-band'>CSS properties currently supported in Servo</span></h1>\n      % for kind, props in sorted(properties.items()):\n      <h2>${kind.capitalize()}</h2>\n      <table>\n        <tr>\n          <th>Name</th>\n          <th>Pref</th>\n        </tr>\n        % for name, data in sorted(props.items()):\n          <tr>\n            <td><code>${name}</code></td>\n            <td><code>${data['pref'] or ''}</code></td>\n          </tr>\n        % endfor\n      </table>\n      % endfor\n    </section>\n</body>\n</html>\n"
  },
  {
    "path": "style/properties/properties.mako.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n// This file is a Mako template: http://www.makotemplates.org/\n\n<%namespace name=\"helpers\" file=\"/helpers.mako.rs\" />\n<% from itertools import groupby %>\n<% from data import PropertyRestrictions, to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS, PRIORITARY_PROPERTIES %>\n\nuse servo_arc::{Arc, UniqueArc};\nuse std::{ops, ptr, fmt, mem};\n\n#[cfg(feature = \"servo\")] use euclid::SideOffsets2D;\n#[cfg(feature = \"gecko\")] use crate::gecko_bindings::structs::{self, NonCustomCSSPropertyId};\n#[cfg(feature = \"servo\")] use crate::logical_geometry::LogicalMargin;\n#[cfg(feature = \"servo\")] use crate::computed_values;\n#[cfg(feature = \"servo\")] use crate::dom::AttributeReferences;\nuse crate::logical_geometry::WritingMode;\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps};\nuse crate::computed_value_flags::*;\nuse cssparser::Parser;\nuse crate::device::Device;\nuse crate::parser::ParserContext;\nuse crate::selector_parser::PseudoElement;\nuse crate::stylist::Stylist;\nuse style_traits::{CssStringWriter, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, TypedValueList, ToTyped};\nuse crate::derives::*;\nuse crate::stylesheets::{CssRuleType, CssRuleTypes, Origin};\nuse crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide};\nuse crate::use_counters::UseCounters;\nuse crate::rule_tree::StrongRuleNode;\nuse crate::values::{\n    computed::{self, ToComputedValue},\n    resolved::{self, ToResolvedValue},\n    specified::{font::SystemFont, length::LineHeightBase, color::ColorSchemeFlags},\n};\nuse std::cell::Cell;\nuse super::{\n    PropertyDeclarationId, PropertyId, NonCustomPropertyId,\n    NonCustomPropertyIdSet, PropertyFlags, SourcePropertyDeclaration,\n    LonghandIdSet, VariableDeclaration, CustomDeclaration,\n    WideKeywordDeclaration, NonCustomPropertyIterator, TransitionPropertyIterator,\n};\nuse debug_unreachable::debug_unreachable;\n\n/// Conversion with fewer impls than From/Into\npub trait MaybeBoxed<Out> {\n    /// Convert\n    fn maybe_boxed(self) -> Out;\n}\n\nimpl<T> MaybeBoxed<T> for T {\n    #[inline]\n    fn maybe_boxed(self) -> T { self }\n}\n\nimpl<T> MaybeBoxed<Box<T>> for T {\n    #[inline]\n    fn maybe_boxed(self) -> Box<T> { Box::new(self) }\n}\n\n/// A module with all the code for longhand properties.\n#[allow(missing_docs)]\npub mod longhands {\n<%\n    for longhand in data.longhands:\n        helpers.longhand(longhand)\n%>\n}\n\n\n% if engine == \"gecko\":\n#[allow(unsafe_code, missing_docs)]\npub mod gecko {\n    <%include file=\"/gecko.mako.rs\" />\n}\n% endif\n\n\n/// A module with code for all the shorthand css properties, and a few\n/// serialization helpers.\n#[allow(missing_docs)]\npub mod shorthands {\n<%\n    for shorthand in data.shorthands_except_all():\n        helpers.shorthand(shorthand)\n%>\n}\n\n/// Servo's representation for a property declaration.\n#[derive(ToShmem)]\n#[repr(u16)]\npub enum PropertyDeclaration {\n    % for variant in data.declaration_variants:\n    /// ${variant[\"doc\"]}\n    ${variant[\"name\"]}(${variant[\"type\"]}),\n    % endfor\n}\n\n// There's one of these for each parsed declaration so it better be small.\nsize_of_test!(PropertyDeclaration, 32);\n\n#[repr(C)]\nstruct PropertyDeclarationVariantRepr<T> {\n    tag: u16,\n    value: T\n}\n\nimpl Clone for PropertyDeclaration {\n    #[inline]\n    fn clone(&self) -> Self {\n        use self::PropertyDeclaration::*;\n\n        <%\n            [copy, others] = [list(g) for _, g in groupby(data.declaration_variants, key=lambda x: not x[\"copy\"])]\n        %>\n\n        let self_tag = unsafe {\n            (*(self as *const _ as *const PropertyDeclarationVariantRepr<()>)).tag\n        };\n        if self_tag <= LonghandId::${copy[-1][\"name\"]} as u16 {\n            #[derive(Clone, Copy)]\n            #[repr(u16)]\n            enum CopyVariants {\n                % for v in copy:\n                _${v[\"name\"]}(${v[\"type\"]}),\n                % endfor\n            }\n\n            unsafe {\n                let mut out = mem::MaybeUninit::uninit();\n                ptr::write(\n                    out.as_mut_ptr() as *mut CopyVariants,\n                    *(self as *const _ as *const CopyVariants),\n                );\n                return out.assume_init();\n            }\n        }\n\n        // This function ensures that all properties not handled above\n        // do not have a specified value implements Copy. If you hit\n        // compile error here, you may want to add the type name into\n        // Longhand.specified_is_copy in data.py.\n        fn _static_assert_others_are_not_copy() {\n            struct Helper<T>(T);\n            trait AssertCopy { fn assert() {} }\n            trait AssertNotCopy { fn assert() {} }\n            impl<T: Copy> AssertCopy for Helper<T> {}\n            % for ty in sorted(set(x[\"type\"] for x in others)):\n            impl AssertNotCopy for Helper<${ty}> {}\n            Helper::<${ty}>::assert();\n            % endfor\n        }\n\n        match *self {\n            ${\" |\\n\".join(\"{}(..)\".format(v[\"name\"]) for v in copy)} => {\n                unsafe { debug_unreachable!() }\n            }\n            % for ty, vs in groupby(others, key=lambda x: x[\"type\"]):\n            <%\n                vs = list(vs)\n            %>\n            % if len(vs) == 1:\n            ${vs[0][\"name\"]}(ref value) => {\n                ${vs[0][\"name\"]}(value.clone())\n            }\n            % else:\n            ${\" |\\n\".join(\"{}(ref value)\".format(v[\"name\"]) for v in vs)} => {\n                unsafe {\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${ty}>,\n                        PropertyDeclarationVariantRepr {\n                            tag: *(self as *const _ as *const u16),\n                            value: value.clone(),\n                        },\n                    );\n                    out.assume_init()\n                }\n            }\n            % endif\n            % endfor\n        }\n    }\n}\n\nimpl PartialEq for PropertyDeclaration {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        use self::PropertyDeclaration::*;\n\n        unsafe {\n            let this_repr =\n                &*(self as *const _ as *const PropertyDeclarationVariantRepr<()>);\n            let other_repr =\n                &*(other as *const _ as *const PropertyDeclarationVariantRepr<()>);\n            if this_repr.tag != other_repr.tag {\n                return false;\n            }\n            match *self {\n                % for ty, vs in groupby(data.declaration_variants, key=lambda x: x[\"type\"]):\n                ${\" |\\n\".join(\"{}(ref this)\".format(v[\"name\"]) for v in vs)} => {\n                    let other_repr =\n                        &*(other as *const _ as *const PropertyDeclarationVariantRepr<${ty}>);\n                    *this == other_repr.value\n                }\n                % endfor\n            }\n        }\n    }\n}\n\nimpl MallocSizeOf for PropertyDeclaration {\n    #[inline]\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        use self::PropertyDeclaration::*;\n\n        match *self {\n            % for ty, vs in groupby(data.declaration_variants, key=lambda x: x[\"type\"]):\n            ${\" | \".join(\"{}(ref value)\".format(v[\"name\"]) for v in vs)} => {\n                value.size_of(ops)\n            }\n            % endfor\n        }\n    }\n}\n\n\nimpl PropertyDeclaration {\n    /// Returns the given value for this declaration as a particular type.\n    /// It's the caller's responsibility to guarantee that the longhand id has the right specified\n    /// value representation.\n    pub(crate) unsafe fn unchecked_value_as<T>(&self) -> &T {\n        &(*(self as *const _ as *const PropertyDeclarationVariantRepr<T>)).value\n    }\n\n    /// Dumps the property declaration before crashing.\n    #[cold]\n    #[cfg(debug_assertions)]\n    pub(crate) fn debug_crash(&self, reason: &str) {\n        panic!(\"{}: {:?}\", reason, self);\n    }\n    #[cfg(not(debug_assertions))]\n    #[inline(always)]\n    pub(crate) fn debug_crash(&self, _reason: &str) {}\n\n    /// Returns whether this is a variant of the Longhand(Value) type, rather\n    /// than one of the special variants in extra_variants.\n    fn is_longhand_value(&self) -> bool {\n        match *self {\n            % for v in data.declaration_extra_variants:\n            PropertyDeclaration::${v[\"name\"]}(..) => false,\n            % endfor\n            _ => true,\n        }\n    }\n\n    /// Like the method on ToCss, but without the type parameter to avoid\n    /// accidentally monomorphizing this large function multiple times for\n    /// different writers.\n    pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {\n        use self::PropertyDeclaration::*;\n\n        let mut dest = CssWriter::new(dest);\n        match *self {\n            % for ty, vs in groupby(data.declaration_variants, key=lambda x: x[\"type\"]):\n            ${\" | \".join(\"{}(ref value)\".format(v[\"name\"]) for v in vs)} => {\n                value.to_css(&mut dest)\n            }\n            % endfor\n        }\n    }\n\n    /// Like the method on ToTyped.\n    pub fn to_typed_value_list(&self) -> Option<TypedValueList> {\n        use self::PropertyDeclaration::*;\n\n        match *self {\n            % for ty, vs in groupby(data.declaration_variants, key=lambda x: x[\"type\"]):\n            ${\" | \".join(\"{}(ref value)\".format(v[\"name\"]) for v in vs)} => {\n                value.to_typed_value_list()\n            }\n            % endfor\n        }\n    }\n\n    /// Returns the color value of a given property, for high-contrast-mode tweaks.\n    pub(super) fn color_value(&self) -> Option<&crate::values::specified::Color> {\n        ${static_longhand_id_set(\"COLOR_PROPERTIES\", lambda p: p.predefined_type == \"Color\")}\n        <%\n            # sanity check\n            assert data.longhands_by_name[\"background-color\"].predefined_type == \"Color\"\n\n            color_specified_type = data.longhands_by_name[\"background-color\"].specified_type()\n        %>\n        let id = self.id().as_longhand()?;\n        if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() {\n            return None;\n        }\n        let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>;\n        Some(unsafe { &(*repr).value })\n    }\n}\n\n/// A module with all the code related to animated properties.\n///\n/// This needs to be \"included\" by mako at least after all longhand modules,\n/// given they populate the global data.\npub mod animated_properties {\n    <%include file=\"/helpers/animated_properties.mako.rs\" />\n}\n\n/// A module to group various interesting property counts.\npub mod property_counts {\n    /// The number of (non-alias) longhand properties.\n    pub const LONGHANDS: usize = ${len(data.longhands)};\n    /// The number of (non-alias) shorthand properties.\n    pub const SHORTHANDS: usize = ${len(data.shorthands)};\n    /// The number of aliases.\n    pub const ALIASES: usize = ${len(data.all_aliases())};\n    /// The number of counted unknown properties.\n    pub const COUNTED_UNKNOWN: usize = ${len(data.counted_unknown_properties)};\n    /// The number of (non-alias) longhands and shorthands.\n    pub const LONGHANDS_AND_SHORTHANDS: usize = LONGHANDS + SHORTHANDS;\n    /// The number of non-custom properties.\n    pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES;\n    /// The number of prioritary properties that we have.\n    <% longhand_property_names = set(list(map(lambda p: p.name, data.longhands))) %>\n    <% enabled_prioritary_properties = PRIORITARY_PROPERTIES.intersection(longhand_property_names) %>\n    pub const PRIORITARY: usize = ${len(enabled_prioritary_properties)};\n    /// The max number of longhands that a shorthand other than \"all\" expands to.\n    pub const MAX_SHORTHAND_EXPANDED: usize =\n        ${max(len(s.sub_properties) for s in data.shorthands_except_all())};\n    /// The max amount of longhands that the `all` shorthand will ever contain.\n    pub const ALL_SHORTHAND_EXPANDED: usize = ${data.all_shorthand_length};\n    /// The number of animatable properties.\n    pub const ANIMATABLE: usize = ${sum(1 for prop in data.longhands if prop.animatable)};\n}\n\n% if engine == \"gecko\":\n#[allow(dead_code)]\nunsafe fn static_assert_noncustomcsspropertyid() {\n    % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):\n    std::mem::transmute::<[u8; ${i}], [u8; ${property.noncustomcsspropertyid()} as usize]>([0; ${i}]); // ${property.name}\n    % endfor\n}\n% endif\n\nimpl NonCustomPropertyId {\n    /// Get the property name.\n    #[inline]\n    pub fn name(self) -> &'static str {\n        static MAP: [&'static str; property_counts::NON_CUSTOM] = [\n            % for property in data.longhands + data.shorthands + data.all_aliases():\n            \"${property.name}\",\n            % endfor\n        ];\n        MAP[self.0 as usize]\n    }\n\n    /// Returns whether this property is animatable.\n    #[inline]\n    pub fn is_animatable(self) -> bool {\n        ${static_non_custom_property_id_set(\"ANIMATABLE\", lambda p: p.animatable)}\n        ANIMATABLE.contains(self)\n    }\n\n    /// Whether this property is enabled for all content right now.\n    #[inline]\n    pub(super) fn enabled_for_all_content(self) -> bool {\n        ${static_non_custom_property_id_set(\n            \"EXPERIMENTAL\",\n            lambda p: p.experimental(engine)\n        )}\n\n        ${static_non_custom_property_id_set(\n            \"ALWAYS_ENABLED\",\n            lambda p: (not p.experimental(engine)) and p.enabled_in_content()\n        )}\n\n        let passes_pref_check = || {\n            % if engine == \"gecko\":\n                unsafe { structs::nsCSSProps_gPropertyEnabled[self.0 as usize] }\n            % else:\n                static PREF_NAME: [Option<&str>; ${\n                    len(data.longhands) + len(data.shorthands) + len(data.all_aliases())\n                }] = [\n                    % for property in data.longhands + data.shorthands + data.all_aliases():\n                        <%\n                            pref = getattr(property, \"servo_pref\")\n                        %>\n                        % if pref:\n                            {\n                                const_assert!(!static_prefs::default_value!(\"${pref}\"));\n                                Some(\"${pref}\")\n                            },\n                        % else:\n                            None,\n                        % endif\n                    % endfor\n                ];\n                let pref = match PREF_NAME[self.0 as usize] {\n                    None => return true,\n                    Some(pref) => pref,\n                };\n\n                // The assertions above guarantee that the pref defaults to false.\n                static_prefs::Preference::get(pref, false)\n            % endif\n        };\n\n        if ALWAYS_ENABLED.contains(self) {\n            return true\n        }\n\n        if EXPERIMENTAL.contains(self) && passes_pref_check() {\n            return true\n        }\n\n        false\n    }\n\n    /// Returns whether a given rule allows a given property.\n    #[inline]\n    pub fn allowed_in_rule(self, rule_types: CssRuleTypes) -> bool {\n        debug_assert!(\n            rule_types.contains(CssRuleType::Keyframe) ||\n            rule_types.contains(CssRuleType::Page) ||\n            rule_types.contains(CssRuleType::Style) ||\n            rule_types.contains(CssRuleType::Scope) ||\n            rule_types.contains(CssRuleType::PositionTry),\n            \"Given rule type does not allow declarations.\"\n        );\n\n        static MAP: [u32; property_counts::NON_CUSTOM] = [\n            % for property in data.longhands + data.shorthands + data.all_aliases():\n            % for name in RULE_VALUES:\n            % if property.rule_types_allowed & RULE_VALUES[name] != 0:\n            CssRuleType::${to_camel_case(name)}.bit() |\n            % endif\n            % endfor\n            0,\n            % endfor\n        ];\n        MAP[self.0 as usize] & rule_types.bits() != 0\n    }\n\n    pub(super) fn allowed_in(self, context: &ParserContext) -> bool {\n        if !self.allowed_in_rule(context.rule_types()) {\n            return false;\n        }\n\n        self.allowed_in_ignoring_rule_type(context)\n    }\n\n\n    pub(super) fn allowed_in_ignoring_rule_type(self, context: &ParserContext) -> bool {\n        // The semantics of these are kinda hard to reason about, what follows\n        // is a description of the different combinations that can happen with\n        // these three sets.\n        //\n        // Experimental properties are generally controlled by prefs, but an\n        // experimental property explicitly enabled in certain context (UA or\n        // chrome sheets) is always usable in the context regardless of the\n        // pref value.\n        //\n        // Non-experimental properties are either normal properties which are\n        // usable everywhere, or internal-only properties which are only usable\n        // in certain context they are explicitly enabled in.\n        if self.enabled_for_all_content() {\n            return true;\n        }\n\n        ${static_non_custom_property_id_set(\n            \"ENABLED_IN_UA_SHEETS\",\n            lambda p: p.explicitly_enabled_in_ua_sheets()\n        )}\n        ${static_non_custom_property_id_set(\n            \"ENABLED_IN_CHROME\",\n            lambda p: p.explicitly_enabled_in_chrome()\n        )}\n\n        if context.stylesheet_origin == Origin::UserAgent &&\n            ENABLED_IN_UA_SHEETS.contains(self)\n        {\n            return true\n        }\n\n        if context.chrome_rules_enabled() && ENABLED_IN_CHROME.contains(self) {\n            return true\n        }\n\n        false\n    }\n\n    /// The supported types of this property. The return value should be\n    /// style_traits::CssType when it can become a bitflags type.\n    pub(super) fn supported_types(&self) -> u8 {\n        const SUPPORTED_TYPES: [u8; ${len(data.longhands) + len(data.shorthands)}] = [\n            % for prop in data.longhands:\n                <${prop.specified_type()} as SpecifiedValueInfo>::SUPPORTED_TYPES,\n            % endfor\n            % for prop in data.shorthands:\n            % if prop.name == \"all\":\n                0, // 'all' accepts no value other than CSS-wide keywords\n            % else:\n                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::SUPPORTED_TYPES,\n            % endif\n            % endfor\n        ];\n        SUPPORTED_TYPES[self.0 as usize]\n    }\n\n    /// See PropertyId::collect_property_completion_keywords.\n    pub(super) fn collect_property_completion_keywords(&self, f: KeywordsCollectFn) {\n        fn do_nothing(_: KeywordsCollectFn) {}\n        const COLLECT_FUNCTIONS: [fn(KeywordsCollectFn);\n                                  ${len(data.longhands) + len(data.shorthands)}] = [\n            % for prop in data.longhands:\n                <${prop.specified_type()} as SpecifiedValueInfo>::collect_completion_keywords,\n            % endfor\n            % for prop in data.shorthands:\n            % if prop.name == \"all\":\n                do_nothing, // 'all' accepts no value other than CSS-wide keywords\n            % else:\n                <shorthands::${prop.ident}::Longhands as SpecifiedValueInfo>::\n                    collect_completion_keywords,\n            % endif\n            % endfor\n        ];\n        COLLECT_FUNCTIONS[self.0 as usize](f);\n    }\n}\n\n<%def name=\"static_non_custom_property_id_set(name, is_member)\">\nstatic ${name}: NonCustomPropertyIdSet = NonCustomPropertyIdSet {\n    <%\n        storage = [0] * int((len(data.longhands) + len(data.shorthands) + len(data.all_aliases()) - 1 + 32) / 32)\n        for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):\n            if is_member(property):\n                storage[int(i / 32)] |= 1 << (i % 32)\n    %>\n    storage: [${\", \".join(\"0x%x\" % word for word in storage)}]\n};\n</%def>\n\n<%def name=\"static_longhand_id_set(name, is_member)\">\nstatic ${name}: LonghandIdSet = LonghandIdSet {\n    <%\n        storage = [0] * int((len(data.longhands) - 1 + 32) / 32)\n        for i, property in enumerate(data.longhands):\n            if is_member(property):\n                storage[int(i / 32)] |= 1 << (i % 32)\n    %>\n    storage: [${\", \".join(\"0x%x\" % word for word in storage)}]\n};\n</%def>\n\n<%\n    FIRST_LINE_RESTRICTIONS = PropertyRestrictions.first_line(data)\n    FIRST_LETTER_RESTRICTIONS = PropertyRestrictions.first_letter(data)\n    MARKER_RESTRICTIONS = PropertyRestrictions.marker(data)\n    PLACEHOLDER_RESTRICTIONS = PropertyRestrictions.placeholder(data)\n    CUE_RESTRICTIONS = PropertyRestrictions.cue(data)\n\n    def restriction_flags(property):\n        name = property.name\n        flags = []\n        if name in FIRST_LINE_RESTRICTIONS:\n            flags.append(\"APPLIES_TO_FIRST_LINE\")\n        if name in FIRST_LETTER_RESTRICTIONS:\n            flags.append(\"APPLIES_TO_FIRST_LETTER\")\n        if name in PLACEHOLDER_RESTRICTIONS:\n            flags.append(\"APPLIES_TO_PLACEHOLDER\")\n        if name in MARKER_RESTRICTIONS:\n            flags.append(\"APPLIES_TO_MARKER\")\n        if name in CUE_RESTRICTIONS:\n            flags.append(\"APPLIES_TO_CUE\")\n        return flags\n\n%>\n\n/// A group for properties which may override each other via logical resolution.\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\n#[repr(u8)]\npub enum LogicalGroupId {\n    % for i, group in enumerate(data.logical_groups.keys()):\n    /// ${group}\n    ${to_camel_case(group)} = ${i},\n    % endfor\n}\n\nimpl LogicalGroupId {\n    /// Return the list of physical mapped properties for a given logical group.\n    fn physical_properties(self) -> &'static [LonghandId] {\n        static PROPS: [[LonghandId; 4]; ${len(data.logical_groups)}] = [\n        % for group, props in data.logical_groups.items():\n        [\n            <% physical_props = [p for p in props if p.logical][0].all_physical_mapped_properties(data) %>\n            % for phys in physical_props:\n            LonghandId::${phys.camel_case},\n            % endfor\n            % for i in range(len(physical_props), 4):\n            LonghandId::${physical_props[0].camel_case},\n            % endfor\n        ],\n        % endfor\n        ];\n        &PROPS[self as usize]\n    }\n}\n\n/// A set of logical groups.\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]\npub struct LogicalGroupSet {\n    storage: [u32; (${len(data.logical_groups)} - 1 + 32) / 32]\n}\n\nimpl LogicalGroupSet {\n    /// Creates an empty `NonCustomPropertyIdSet`.\n    pub fn new() -> Self {\n        Self {\n            storage: Default::default(),\n        }\n    }\n\n    /// Return whether the given group is in the set\n    #[inline]\n    pub fn contains(&self, g: LogicalGroupId) -> bool {\n        let bit = g as usize;\n        (self.storage[bit / 32] & (1 << (bit % 32))) != 0\n    }\n\n    /// Insert a group the set.\n    #[inline]\n    pub fn insert(&mut self, g: LogicalGroupId) {\n        let bit = g as usize;\n        self.storage[bit / 32] |= 1 << (bit % 32);\n    }\n}\n\n\n#[repr(u8)]\n#[derive(Copy, Clone, Debug)]\npub(crate) enum PrioritaryPropertyId {\n    % for p in data.longhands:\n    % if p.is_prioritary():\n    ${p.camel_case},\n    % endif\n    % endfor\n}\n\nimpl PrioritaryPropertyId {\n    #[inline]\n    pub fn to_longhand(self) -> LonghandId {\n        static PRIORITARY_TO_LONGHAND: [LonghandId; property_counts::PRIORITARY] = [\n        % for p in data.longhands:\n        % if p.is_prioritary():\n            LonghandId::${p.camel_case},\n        % endif\n        % endfor\n        ];\n        PRIORITARY_TO_LONGHAND[self as usize]\n    }\n    #[inline]\n    pub fn from_longhand(l: LonghandId) -> Option<Self> {\n        static LONGHAND_TO_PRIORITARY: [Option<PrioritaryPropertyId>; ${len(data.longhands)}] = [\n        % for p in data.longhands:\n        % if p.is_prioritary():\n            Some(PrioritaryPropertyId::${p.camel_case}),\n        % else:\n            None,\n        % endif\n        % endfor\n        ];\n        LONGHAND_TO_PRIORITARY[l as usize]\n    }\n}\n\nimpl LonghandIdSet {\n    /// The set of non-inherited longhands.\n    #[inline]\n    pub(super) fn reset() -> &'static Self {\n        ${static_longhand_id_set(\"RESET\", lambda p: not p.style_struct.inherited)}\n        &RESET\n    }\n\n    #[inline]\n    pub(super) fn discrete_animatable() -> &'static Self {\n        ${static_longhand_id_set(\"DISCRETE_ANIMATABLE\", lambda p: p.animation_type == \"discrete\")}\n        &DISCRETE_ANIMATABLE\n    }\n\n    #[inline]\n    pub(super) fn logical() -> &'static Self {\n        ${static_longhand_id_set(\"LOGICAL\", lambda p: p.logical)}\n        &LOGICAL\n    }\n\n    /// Returns the set of longhands that are ignored when document colors are\n    /// disabled.\n    #[inline]\n    pub(super) fn ignored_when_colors_disabled() -> &'static Self {\n        ${static_longhand_id_set(\n            \"IGNORED_WHEN_COLORS_DISABLED\",\n            lambda p: p.ignored_when_colors_disabled\n        )}\n        &IGNORED_WHEN_COLORS_DISABLED\n    }\n\n    /// Only a few properties are allowed to depend on the visited state of\n    /// links. When cascading visited styles, we can save time by only\n    /// processing these properties.\n    pub(super) fn visited_dependent() -> &'static Self {\n        ${static_longhand_id_set(\"VISITED_DEPENDENT\", lambda p: p.is_visited_dependent())}\n        debug_assert!(Self::late_group().contains_all(&VISITED_DEPENDENT));\n        &VISITED_DEPENDENT\n    }\n\n    #[inline]\n    pub(super) fn prioritary_properties() -> &'static Self {\n        ${static_longhand_id_set(\"PRIORITARY_PROPERTIES\", lambda p: p.is_prioritary())}\n        &PRIORITARY_PROPERTIES\n    }\n\n    #[inline]\n    pub(super) fn late_group_only_inherited() -> &'static Self {\n        ${static_longhand_id_set(\"LATE_GROUP_ONLY_INHERITED\", lambda p: p.style_struct.inherited and not p.is_prioritary())}\n        &LATE_GROUP_ONLY_INHERITED\n    }\n\n    #[inline]\n    pub(super) fn late_group() -> &'static Self {\n        ${static_longhand_id_set(\"LATE_GROUP\", lambda p: not p.is_prioritary())}\n        &LATE_GROUP\n    }\n\n    /// Returns the set of properties that are declared as having no effect on\n    /// Gecko <scrollbar> elements or their descendant scrollbar parts.\n    #[cfg(debug_assertions)]\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn has_no_effect_on_gecko_scrollbars() -> &'static Self {\n        // data.py asserts that has_no_effect_on_gecko_scrollbars is True or\n        // False for properties that are inherited and Gecko pref controlled,\n        // and is None for all other properties.\n        ${static_longhand_id_set(\n            \"HAS_NO_EFFECT_ON_SCROLLBARS\",\n            lambda p: p.has_effect_on_gecko_scrollbars is False\n        )}\n        &HAS_NO_EFFECT_ON_SCROLLBARS\n    }\n\n    /// Returns the set of margin properties, for the purposes of <h1> use counters / warnings.\n    #[inline]\n    pub fn margin_properties() -> &'static Self {\n        ${static_longhand_id_set(\n            \"MARGIN_PROPERTIES\",\n            lambda p: p.logical_group == \"margin\"\n        )}\n        &MARGIN_PROPERTIES\n    }\n\n    /// Returns the set of border properties for the purpose of disabling native\n    /// appearance.\n    #[inline]\n    pub fn border_background_properties() -> &'static Self {\n        ${static_longhand_id_set(\n            \"BORDER_BACKGROUND_PROPERTIES\",\n            lambda p: (p.logical_group and p.logical_group.startswith(\"border\")) or \\\n                        p in data.shorthands_by_name[\"border\"].sub_properties or \\\n                        p in data.shorthands_by_name[\"background\"].sub_properties and \\\n                        p.name not in [\"background-blend-mode\", \"background-repeat\"]\n        )}\n        &BORDER_BACKGROUND_PROPERTIES\n    }\n\n    /// Returns properties that are zoom dependent (basically, that contain lengths).\n    #[inline]\n    pub fn zoom_dependent() -> &'static Self {\n        ${static_longhand_id_set(\"ZOOM_DEPENDENT\", lambda p: p.is_zoom_dependent())}\n        &ZOOM_DEPENDENT\n    }\n\n    /// Note that it's different from zoom_dependent(), as this only includes inherited, physical\n    /// properties.\n    #[inline]\n    pub fn zoom_dependent_inherited_properties() -> &'static Self {\n        ${static_longhand_id_set(\"ZOOM_DEPENDENT_INHERITED\", lambda p: p.is_inherited_zoom_dependent_property())}\n        &ZOOM_DEPENDENT_INHERITED\n    }\n}\n\n/// An identifier for a given longhand property.\n#[derive(Clone, Copy, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\n#[repr(u16)]\npub enum LonghandId {\n    % for i, property in enumerate(data.longhands):\n        /// ${property.name}\n        ${property.camel_case} = ${i},\n    % endfor\n}\n\nenum LogicalMappingKind {\n    Side(LogicalSide),\n    Corner(LogicalCorner),\n    Axis(LogicalAxis),\n}\n\nstruct LogicalMappingData {\n    group: LogicalGroupId,\n    kind: LogicalMappingKind,\n}\n\nimpl LogicalMappingData {\n    fn to_physical(&self, wm: WritingMode) -> LonghandId {\n        let index = match self.kind {\n            LogicalMappingKind::Side(s) => s.to_physical(wm) as usize,\n            LogicalMappingKind::Corner(c) => c.to_physical(wm) as usize,\n            LogicalMappingKind::Axis(a) => a.to_physical(wm) as usize,\n        };\n        self.group.physical_properties()[index]\n    }\n}\n\nimpl LonghandId {\n    /// Returns an iterator over all the shorthands that include this longhand.\n    pub fn shorthands(self) -> NonCustomPropertyIterator<ShorthandId> {\n        // first generate longhand to shorthands lookup map\n        //\n        // NOTE(emilio): This currently doesn't exclude the \"all\" shorthand. It\n        // could potentially do so, which would speed up serialization\n        // algorithms and what not, I guess.\n        <%\n            from functools import cmp_to_key\n            longhand_to_shorthand_map = {}\n            num_sub_properties = {}\n            for shorthand in data.shorthands:\n                num_sub_properties[shorthand.camel_case] = len(shorthand.sub_properties)\n                for sub_property in shorthand.sub_properties:\n                    if sub_property.ident not in longhand_to_shorthand_map:\n                        longhand_to_shorthand_map[sub_property.ident] = []\n\n                    longhand_to_shorthand_map[sub_property.ident].append(shorthand.camel_case)\n\n            def cmp(a, b):\n                return (a > b) - (a < b)\n\n            def preferred_order(x, y):\n                # Since we want properties in order from most subproperties to least,\n                # reverse the arguments to cmp from the expected order.\n                result = cmp(num_sub_properties.get(y, 0), num_sub_properties.get(x, 0))\n                if result:\n                    return result\n                # Fall back to lexicographic comparison.\n                return cmp(x, y)\n\n            # Sort the lists of shorthand properties according to preferred order:\n            # https://drafts.csswg.org/cssom/#concept-shorthands-preferred-order\n            for shorthand_list in longhand_to_shorthand_map.values():\n                shorthand_list.sort(key=cmp_to_key(preferred_order))\n        %>\n\n        // based on lookup results for each longhand, create result arrays\n        static MAP: [&'static [ShorthandId]; property_counts::LONGHANDS] = [\n        % for property in data.longhands:\n            &[\n                % for shorthand in longhand_to_shorthand_map.get(property.ident, []):\n                    ShorthandId::${shorthand},\n                % endfor\n            ],\n        % endfor\n        ];\n\n        NonCustomPropertyIterator {\n            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),\n            iter: MAP[self as usize].iter(),\n        }\n    }\n\n    pub(super) fn parse_value<'i, 't>(\n        self,\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<PropertyDeclaration, ParseError<'i>> {\n        type ParsePropertyFn = for<'i, 't> fn(\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n        ) -> Result<PropertyDeclaration, ParseError<'i>>;\n        static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [\n        % for property in data.longhands:\n            longhands::${property.ident}::parse_declared,\n        % endfor\n        ];\n        (PARSE_PROPERTY[self as usize])(context, input)\n    }\n\n    /// Return the relevant data to map a particular logical property into physical.\n    fn logical_mapping_data(self) -> Option<&'static LogicalMappingData> {\n        const LOGICAL_MAPPING_DATA: [Option<LogicalMappingData>; ${len(data.longhands)}] = [\n            % for prop in data.longhands:\n            % if prop.logical:\n            Some(LogicalMappingData {\n                group: LogicalGroupId::${to_camel_case(prop.logical_group)},\n                kind: ${prop.logical_mapping_kind(data)}\n            }),\n            % else:\n            None,\n            % endif\n            % endfor\n        ];\n        LOGICAL_MAPPING_DATA[self as usize].as_ref()\n    }\n\n    /// If this is a logical property, return the corresponding physical one in the given\n    /// writing mode. Otherwise, return unchanged.\n    #[inline]\n    pub fn to_physical(self, wm: WritingMode) -> Self {\n        let Some(data) = self.logical_mapping_data() else { return self };\n        data.to_physical(wm)\n    }\n\n    /// Return the logical group of this longhand property.\n    pub fn logical_group(self) -> Option<LogicalGroupId> {\n        const LOGICAL_GROUP_IDS: [Option<LogicalGroupId>; ${len(data.longhands)}] = [\n            % for prop in data.longhands:\n            % if prop.logical_group:\n            Some(LogicalGroupId::${to_camel_case(prop.logical_group)}),\n            % else:\n            None,\n            % endif\n            % endfor\n        ];\n        LOGICAL_GROUP_IDS[self as usize]\n    }\n\n    /// Returns PropertyFlags for given longhand property.\n    #[inline(always)]\n    pub fn flags(self) -> PropertyFlags {\n        const FLAGS: [PropertyFlags; ${len(data.longhands)}] = [\n            % for property in data.longhands:\n                PropertyFlags::empty()\n                % for flag in property.flags + restriction_flags(property):\n                    .union(PropertyFlags::${flag})\n                % endfor\n                ,\n            % endfor\n        ];\n        FLAGS[self as usize]\n    }\n}\n\n/// An identifier for a given shorthand property.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\n#[repr(u16)]\npub enum ShorthandId {\n    % for i, property in enumerate(data.shorthands):\n        /// ${property.name}\n        ${property.camel_case} = ${i},\n    % endfor\n}\n\nimpl ShorthandId {\n    /// Get the longhand ids that form this shorthand.\n    pub fn longhands(self) -> NonCustomPropertyIterator<LonghandId> {\n        static MAP: [&'static [LonghandId]; property_counts::SHORTHANDS] = [\n        % for property in data.shorthands:\n            &[\n                % for sub in property.sub_properties:\n                    LonghandId::${sub.camel_case},\n                % endfor\n            ],\n        % endfor\n        ];\n        NonCustomPropertyIterator {\n            filter: NonCustomPropertyId::from(self).enabled_for_all_content(),\n            iter: MAP[self as usize].iter(),\n        }\n    }\n\n    /// Try to serialize the given declarations as this shorthand.\n    ///\n    /// Returns an error if writing to the stream fails, or if the declarations\n    /// do not map to a shorthand.\n    pub fn longhands_to_css(\n        self,\n        declarations: &[&PropertyDeclaration],\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result;\n        fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result {\n            // No need to try to serialize the declarations as the 'all'\n            // shorthand, since it only accepts CSS-wide keywords (and variable\n            // references), which will be handled in\n            // get_shorthand_appendable_value.\n            Ok(())\n        }\n\n        static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [\n            % for shorthand in data.shorthands:\n            % if shorthand.ident == \"all\":\n                all_to_css,\n            % else:\n                shorthands::${shorthand.ident}::to_css,\n            % endif\n            % endfor\n        ];\n\n        LONGHANDS_TO_CSS[self as usize](declarations, dest)\n    }\n\n    /// Returns PropertyFlags for the given shorthand property.\n    #[inline]\n    pub fn flags(self) -> PropertyFlags {\n        const FLAGS: [u16; ${len(data.shorthands)}] = [\n            % for property in data.shorthands:\n                % for flag in property.flags:\n                    PropertyFlags::${flag}.bits() |\n                % endfor\n                0,\n            % endfor\n        ];\n        PropertyFlags::from_bits_retain(FLAGS[self as usize])\n    }\n\n    /// Returns the order in which this property appears relative to other\n    /// shorthands in idl-name-sorting order.\n    #[inline]\n    pub fn idl_name_sort_order(self) -> u32 {\n        <%\n            from data import to_idl_name\n            ordered = {}\n            sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))\n            for order, shorthand in enumerate(sorted_shorthands):\n                ordered[shorthand.ident] = order\n        %>\n        static IDL_NAME_SORT_ORDER: [u32; ${len(data.shorthands)}] = [\n            % for property in data.shorthands:\n            ${ordered[property.ident]},\n            % endfor\n        ];\n        IDL_NAME_SORT_ORDER[self as usize]\n    }\n\n    pub(super) fn parse_into<'i, 't>(\n        self,\n        declarations: &mut SourcePropertyDeclaration,\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(), ParseError<'i>> {\n        type ParseIntoFn = for<'i, 't> fn(\n            declarations: &mut SourcePropertyDeclaration,\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n        ) -> Result<(), ParseError<'i>>;\n\n        fn parse_all<'i, 't>(\n            _: &mut SourcePropertyDeclaration,\n            _: &ParserContext,\n            input: &mut Parser<'i, 't>\n        ) -> Result<(), ParseError<'i>> {\n            // 'all' accepts no value other than CSS-wide keywords\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        }\n\n        static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [\n            % for shorthand in data.shorthands:\n            % if shorthand.ident == \"all\":\n            parse_all,\n            % else:\n            shorthands::${shorthand.ident}::parse_into,\n            % endif\n            % endfor\n        ];\n\n        (PARSE_INTO[self as usize])(declarations, context, input)\n    }\n}\n\n/// The counted unknown property list which is used for css use counters.\n///\n/// FIXME: This should be just #[repr(u8)], but can't be because of ABI issues,\n/// see https://bugs.llvm.org/show_bug.cgi?id=44228.\n#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, PartialEq)]\n#[repr(u32)]\npub enum CountedUnknownProperty {\n    % for prop in data.counted_unknown_properties:\n    /// ${prop.name}\n    ${prop.camel_case},\n    % endfor\n}\n\nimpl CountedUnknownProperty {\n    /// Parse the counted unknown property, for testing purposes only.\n    pub fn parse_for_testing(property_name: &str) -> Option<Self> {\n        ::cssparser::ascii_case_insensitive_phf_map! {\n            unknown_ids -> CountedUnknownProperty = {\n                % for property in data.counted_unknown_properties:\n                \"${property.name}\" => CountedUnknownProperty::${property.camel_case},\n                % endfor\n            }\n        }\n        unknown_ids::get(property_name).cloned()\n    }\n\n    /// Returns the underlying index, used for use counter.\n    #[inline]\n    pub fn bit(self) -> usize {\n        self as usize\n    }\n}\n\nimpl PropertyId {\n    /// Returns a given property from the given name, _regardless of whether it\n    /// is enabled or not_, or Err(()) for unknown properties.\n    pub fn parse_unchecked(\n        property_name: &str,\n        use_counters: Option<&UseCounters>,\n    ) -> Result<Self, ()> {\n        // A special id for css use counters. ShorthandAlias is not used in the Servo build.\n        // That's why we need to allow dead_code.\n        pub enum StaticId {\n            NonCustom(NonCustomPropertyId),\n            CountedUnknown(CountedUnknownProperty),\n        }\n        ::cssparser::ascii_case_insensitive_phf_map! {\n            static_ids -> StaticId = {\n                % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()):\n                \"${property.name}\" => StaticId::NonCustom(NonCustomPropertyId(${i})),\n                % endfor\n                % for property in data.counted_unknown_properties:\n                \"${property.name}\" => {\n                    StaticId::CountedUnknown(CountedUnknownProperty::${property.camel_case})\n                },\n                % endfor\n            }\n        }\n\n        if let Some(id) = static_ids::get(property_name) {\n            return Ok(match *id {\n                StaticId::NonCustom(id) => PropertyId::NonCustom(id),\n                StaticId::CountedUnknown(unknown_prop) => {\n                    if let Some(counters) = use_counters {\n                        counters.counted_unknown_properties.record(unknown_prop);\n                    }\n                    // Always return Err(()) because these aren't valid custom property names.\n                    return Err(());\n                }\n            });\n        }\n\n        let name = crate::custom_properties::parse_name(property_name)?;\n        Ok(PropertyId::Custom(crate::custom_properties::Name::from(name)))\n    }\n}\n\nimpl PropertyDeclaration {\n    /// Given a property declaration, return the property declaration id.\n    #[inline]\n    pub fn id(&self) -> PropertyDeclarationId<'_> {\n        match *self {\n            PropertyDeclaration::Custom(ref declaration) => {\n                return PropertyDeclarationId::Custom(&declaration.name)\n            }\n            PropertyDeclaration::CSSWideKeyword(ref declaration) => {\n                return PropertyDeclarationId::Longhand(declaration.id);\n            }\n            PropertyDeclaration::WithVariables(ref declaration) => {\n                return PropertyDeclarationId::Longhand(declaration.id);\n            }\n            _ => {}\n        }\n        // This is just fine because PropertyDeclaration and LonghandId\n        // have corresponding discriminants.\n        let id = unsafe { *(self as *const _ as *const LonghandId) };\n        debug_assert_eq!(id, match *self {\n            % for property in data.longhands:\n            PropertyDeclaration::${property.camel_case}(..) => LonghandId::${property.camel_case},\n            % endfor\n            _ => id,\n        });\n        PropertyDeclarationId::Longhand(id)\n    }\n\n    /// Given a declaration, convert it into a declaration for a corresponding\n    /// physical property.\n    #[inline]\n    pub fn to_physical(&self, wm: WritingMode) -> Self {\n        match *self {\n            PropertyDeclaration::WithVariables(VariableDeclaration {\n                id,\n                ref value,\n            }) => {\n                return PropertyDeclaration::WithVariables(VariableDeclaration {\n                    id: id.to_physical(wm),\n                    value: value.clone(),\n                })\n            }\n            PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {\n                id,\n                keyword,\n            }) => {\n                return PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {\n                    id: id.to_physical(wm),\n                    keyword,\n                })\n            }\n            PropertyDeclaration::Custom(..) => return self.clone(),\n            % for prop in data.longhands:\n            PropertyDeclaration::${prop.camel_case}(..) => {},\n            % endfor\n        }\n\n        let mut ret = self.clone();\n\n        % for prop in data.longhands:\n        % for physical_property in prop.all_physical_mapped_properties(data):\n        % if physical_property.specified_type() != prop.specified_type():\n            <% raise \"Logical property %s should share specified value with physical property %s\" % \\\n                     (prop.name, physical_property.name) %>\n        % endif\n        % endfor\n        % endfor\n\n        unsafe {\n            let longhand_id = *(&mut ret as *mut _ as *mut LonghandId);\n\n            debug_assert_eq!(\n                PropertyDeclarationId::Longhand(longhand_id),\n                ret.id()\n            );\n\n            // This is just fine because PropertyDeclaration and LonghandId\n            // have corresponding discriminants.\n            *(&mut ret as *mut _ as *mut LonghandId) = longhand_id.to_physical(wm);\n\n            debug_assert_eq!(\n                PropertyDeclarationId::Longhand(longhand_id.to_physical(wm)),\n                ret.id()\n            );\n        }\n\n        ret\n    }\n\n    /// Returns whether or not the property is set by a system font\n    pub fn get_system(&self) -> Option<SystemFont> {\n        match *self {\n            % if engine == \"gecko\":\n            % for prop in SYSTEM_FONT_LONGHANDS:\n                PropertyDeclaration::${to_camel_case(prop)}(ref prop) => {\n                    prop.get_system()\n                }\n            % endfor\n            % endif\n            _ => None,\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub use super::gecko::style_structs;\n\n/// The module where all the style structs are defined.\n#[cfg(feature = \"servo\")]\npub mod style_structs {\n    use rustc_hash::FxHasher;\n    use super::longhands;\n    use std::hash::{Hash, Hasher};\n    use crate::logical_geometry::PhysicalSide;\n    use crate::values::specified::color::ColorSchemeFlags;\n    use crate::derives::*;\n\n    <%def name=\"impl_physical_sides(ident, props)\">\n        /// Gets the value of the longhand of `${ident}` on the `s` side`.\n        pub fn get_${ident}(&self, s: PhysicalSide) -> &longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T {\n            match s {\n                PhysicalSide::Top => &self.${data.longhands_by_name[props[0]].ident},\n                PhysicalSide::Right => &self.${data.longhands_by_name[props[1]].ident},\n                PhysicalSide::Bottom => &self.${data.longhands_by_name[props[2]].ident},\n                PhysicalSide::Left => &self.${data.longhands_by_name[props[3]].ident},\n            }\n        }\n        /// Sets the value of the longhand of `${ident}` on the `s` side to `v`.\n        pub fn set_${ident}(&mut self, s: PhysicalSide, v: longhands::${data.longhands_by_name[props[0]].ident}::computed_value::T) {\n            match s {\n                PhysicalSide::Top => self.set_${data.longhands_by_name[props[0]].ident}(v),\n                PhysicalSide::Right => self.set_${data.longhands_by_name[props[1]].ident}(v),\n                PhysicalSide::Bottom => self.set_${data.longhands_by_name[props[2]].ident}(v),\n                PhysicalSide::Left => self.set_${data.longhands_by_name[props[3]].ident}(v),\n            }\n        }\n    </%def>\n\n    % for style_struct in data.active_style_structs():\n        % if style_struct.name == \"Font\":\n        #[derive(Clone, Debug, MallocSizeOf)]\n        #[cfg_attr(feature = \"servo\", derive(Serialize, Deserialize))]\n        % else:\n        #[derive(Clone, Debug, MallocSizeOf, PartialEq)]\n        % endif\n        /// The ${style_struct.name} style struct.\n        pub struct ${style_struct.name} {\n            % for longhand in style_struct.longhands:\n                % if not longhand.logical:\n                    /// The ${longhand.name} computed value.\n                    pub ${longhand.ident}: longhands::${longhand.ident}::computed_value::T,\n                % endif\n            % endfor\n            % if style_struct.name == \"Font\":\n                /// The font hash, used for font caching.\n                pub hash: u64,\n            % endif\n            % if style_struct.name == \"Box\":\n                /// The display value specified by the CSS stylesheets (without any style adjustments),\n                /// which is needed for hypothetical layout boxes.\n                pub original_display: longhands::display::computed_value::T,\n            % endif\n        }\n        % if style_struct.name == \"Font\":\n        impl PartialEq for Font {\n            fn eq(&self, other: &Font) -> bool {\n                self.hash == other.hash\n                % for longhand in style_struct.longhands:\n                    && self.${longhand.ident} == other.${longhand.ident}\n                % endfor\n            }\n        }\n        % endif\n\n        impl ${style_struct.name} {\n            % for longhand in style_struct.longhands:\n                % if not longhand.logical:\n                    % if longhand.ident == \"display\":\n                        /// Set `display`.\n                        ///\n                        /// We need to keep track of the original display for hypothetical boxes,\n                        /// so we need to special-case this.\n                        #[allow(non_snake_case)]\n                        #[inline]\n                        pub fn set_display(&mut self, v: longhands::display::computed_value::T) {\n                            self.display = v;\n                            self.original_display = v;\n                        }\n                    % else:\n                        /// Set ${longhand.name}.\n                        #[allow(non_snake_case)]\n                        #[inline]\n                        pub fn set_${longhand.ident}(&mut self, v: longhands::${longhand.ident}::computed_value::T) {\n                            self.${longhand.ident} = v;\n                        }\n                    % endif\n                    % if longhand.ident == \"display\":\n                        /// Set `display` from other struct.\n                        ///\n                        /// Same as `set_display` above.\n                        /// Thus, we need to special-case this.\n                        #[allow(non_snake_case)]\n                        #[inline]\n                        pub fn copy_display_from(&mut self, other: &Self) {\n                            self.display = other.display.clone();\n                            self.original_display = other.display.clone();\n                        }\n                    % else:\n                        /// Set ${longhand.name} from other struct.\n                        #[allow(non_snake_case)]\n                        #[inline]\n                        pub fn copy_${longhand.ident}_from(&mut self, other: &Self) {\n                            self.${longhand.ident} = other.${longhand.ident}.clone();\n                        }\n                    % endif\n                    /// Reset ${longhand.name} from the initial struct.\n                    #[allow(non_snake_case)]\n                    #[inline]\n                    pub fn reset_${longhand.ident}(&mut self, other: &Self) {\n                        self.copy_${longhand.ident}_from(other)\n                    }\n\n                    /// Get the computed value for ${longhand.name}.\n                    #[allow(non_snake_case)]\n                    #[inline]\n                    pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T {\n                        self.${longhand.ident}.clone()\n                    }\n\n                    /// Whether `self` and `other` have the same computed value for ${longhand.name}.\n                    #[allow(non_snake_case)]\n                    #[inline]\n                    pub fn ${longhand.ident}_equals(&self, other: &Self) -> bool {\n                        self.${longhand.ident} == other.${longhand.ident}\n                    }\n                % endif\n                % if longhand.vector and longhand.vector.need_index:\n                    /// If this longhand is indexed, get the number of elements.\n                    #[allow(non_snake_case)]\n                    pub fn ${longhand.ident}_count(&self) -> usize {\n                        self.${longhand.ident}.0.len()\n                    }\n\n                    /// If this longhand is indexed, get the element at given\n                    /// index.\n                    #[allow(non_snake_case)]\n                    pub fn ${longhand.ident}_at(&self, index: usize)\n                        -> longhands::${longhand.ident}::computed_value::SingleComputedValue {\n                        self.${longhand.ident}.0[index].clone()\n                    }\n                % endif\n            % endfor\n            % if style_struct.name == \"Font\":\n                /// Computes a font hash in order to be able to cache fonts\n                /// effectively in GFX and layout.\n                pub fn compute_font_hash(&mut self) {\n                    let mut hasher: FxHasher = Default::default();\n                    self.font_weight.hash(&mut hasher);\n                    self.font_stretch.hash(&mut hasher);\n                    self.font_style.hash(&mut hasher);\n                    self.font_family.hash(&mut hasher);\n                    self.hash = hasher.finish()\n                }\n                /// Create a new Font with the initial values of all members.\n                pub fn initial_values() -> Self {\n                    Self {\n                        % for longhand in style_struct.longhands:\n                            % if not longhand.logical:\n                                ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),\n                            % endif\n                        % endfor\n                        hash: 0,\n                    }\n                 }\n            % elif style_struct.name == \"InheritedUI\":\n                /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`.\n                #[inline]\n                pub fn color_scheme_bits(&self) -> ColorSchemeFlags {\n                    self.color_scheme.bits\n                }\n            % elif style_struct.name == \"Box\":\n                /// Sets the display property, but without touching original_display,\n                /// except when the adjustment comes from root or item display fixups.\n                pub fn set_adjusted_display(\n                    &mut self,\n                    dpy: longhands::display::computed_value::T,\n                    is_item_or_root: bool\n                ) {\n                    self.display = dpy;\n                    if is_item_or_root {\n                        self.original_display = dpy;\n                    }\n                }\n            % elif style_struct.name == \"Margin\":\n                ${impl_physical_sides(\"margin\", [\"margin-top\", \"margin-right\", \"margin-bottom\", \"margin-left\"])}\n            % elif style_struct.name == \"Position\":\n                ${impl_physical_sides(\"inset\", [\"top\", \"right\", \"bottom\", \"left\"])}\n            % endif\n        }\n\n    % endfor\n}\n\n% for style_struct in data.active_style_structs():\n    impl style_structs::${style_struct.name} {\n        % for longhand in style_struct.longhands:\n            % if longhand.vector and longhand.vector.need_index:\n                /// Iterate over the values of ${longhand.name}.\n                #[allow(non_snake_case)]\n                #[inline]\n                pub fn ${longhand.ident}_iter(&self) -> ${longhand.camel_case}Iter<'_> {\n                    ${longhand.camel_case}Iter {\n                        style_struct: self,\n                        current: 0,\n                        max: self.${longhand.ident}_count(),\n                    }\n                }\n\n                /// Get a value mod `index` for the property ${longhand.name}.\n                #[allow(non_snake_case)]\n                #[inline]\n                pub fn ${longhand.ident}_mod(&self, index: usize)\n                    -> longhands::${longhand.ident}::computed_value::SingleComputedValue {\n                    self.${longhand.ident}_at(index % self.${longhand.ident}_count())\n                }\n\n                /// Clone the computed value for the property.\n                #[allow(non_snake_case)]\n                #[inline]\n                #[cfg(feature = \"gecko\")]\n                pub fn clone_${longhand.ident}(\n                    &self,\n                ) -> longhands::${longhand.ident}::computed_value::T {\n                    longhands::${longhand.ident}::computed_value::List(\n                        self.${longhand.ident}_iter().collect()\n                    )\n                }\n            % endif\n        % endfor\n\n        % if style_struct.name == \"UI\":\n            /// Returns whether there is any animation specified with\n            /// animation-name other than `none`.\n            pub fn specifies_animations(&self) -> bool {\n                self.animation_name_iter().any(|name| !name.is_none())\n            }\n\n            /// Returns whether there are any transitions specified.\n            #[cfg(feature = \"servo\")]\n            pub fn specifies_transitions(&self) -> bool {\n                (0..self.transition_property_count()).any(|index| {\n                    let combined_duration =\n                        self.transition_duration_mod(index).seconds().max(0.) +\n                        self.transition_delay_mod(index).seconds();\n                    combined_duration > 0.\n                })\n            }\n\n            /// Returns whether animation-timeline is initial value. We need this information to\n            /// resolve animation-duration.\n            #[cfg(feature = \"servo\")]\n            pub fn has_initial_animation_timeline(&self) -> bool {\n                self.animation_timeline_count() == 1 && self.animation_timeline_at(0).is_auto()\n            }\n\n            /// Returns whether there is any named progress timeline specified with\n            /// scroll-timeline-name other than `none`.\n            #[cfg(feature = \"gecko\")]\n            pub fn specifies_scroll_timelines(&self) -> bool {\n                self.scroll_timeline_name_iter().any(|name| !name.value.is_none())\n            }\n\n            /// Returns whether there is any timeline scope specified.\n            #[cfg(feature = \"gecko\")]\n            pub fn specifies_timeline_scope(&self) -> bool {\n                !self.mTimelineScope.is_none()\n            }\n\n            /// Returns whether there is any named progress timeline specified with\n            /// view-timeline-name other than `none`.\n            #[cfg(feature = \"gecko\")]\n            pub fn specifies_view_timelines(&self) -> bool {\n                self.view_timeline_name_iter().any(|name| !name.value.is_none())\n            }\n\n            /// Returns true if animation properties are equal between styles, but without\n            /// considering keyframe data and animation-timeline.\n            #[cfg(feature = \"servo\")]\n            pub fn animations_equals(&self, other: &Self) -> bool {\n                self.animation_name_iter().eq(other.animation_name_iter()) &&\n                self.animation_delay_iter().eq(other.animation_delay_iter()) &&\n                self.animation_direction_iter().eq(other.animation_direction_iter()) &&\n                self.animation_duration_iter().eq(other.animation_duration_iter()) &&\n                self.animation_fill_mode_iter().eq(other.animation_fill_mode_iter()) &&\n                self.animation_iteration_count_iter().eq(other.animation_iteration_count_iter()) &&\n                self.animation_play_state_iter().eq(other.animation_play_state_iter()) &&\n                self.animation_timing_function_iter().eq(other.animation_timing_function_iter())\n            }\n\n        % elif style_struct.name == \"Column\":\n            /// Whether this is a multicol style.\n            #[cfg(feature = \"servo\")]\n            pub fn is_multicol(&self) -> bool {\n                !self.column_width.is_auto() || !self.column_count.is_auto()\n            }\n        % endif\n    }\n\n    % for longhand in style_struct.longhands:\n        % if longhand.vector and longhand.vector.need_index:\n            /// An iterator over the values of the ${longhand.name} properties.\n            pub struct ${longhand.camel_case}Iter<'a> {\n                style_struct: &'a style_structs::${style_struct.name},\n                current: usize,\n                max: usize,\n            }\n\n            impl<'a> Iterator for ${longhand.camel_case}Iter<'a> {\n                type Item = longhands::${longhand.ident}::computed_value::SingleComputedValue;\n\n                fn next(&mut self) -> Option<Self::Item> {\n                    self.current += 1;\n                    if self.current <= self.max {\n                        Some(self.style_struct.${longhand.ident}_at(self.current - 1))\n                    } else {\n                        None\n                    }\n                }\n            }\n        % endif\n    % endfor\n% endfor\n\n\n#[cfg(feature = \"gecko\")]\npub use super::gecko::{ComputedValues, ComputedValuesInner};\n\n#[cfg(feature = \"servo\")]\n#[cfg_attr(feature = \"servo\", derive(Clone, Debug))]\n/// Actual data of ComputedValues, to match up with Gecko\npub struct ComputedValuesInner {\n    % for style_struct in data.active_style_structs():\n        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,\n    % endfor\n    custom_properties: crate::custom_properties::ComputedCustomProperties,\n\n    /// The set of attributes used as values in `attr()`\n    pub attribute_references: AttributeReferences,\n\n    /// The effective zoom value.\n    pub effective_zoom: computed::Zoom,\n\n    /// A set of flags we use to store misc information regarding this style.\n    pub flags: ComputedValueFlags,\n\n    /// The writing mode of this computed values struct.\n    pub writing_mode: WritingMode,\n\n    /// The rule node representing the ordered list of rules matched for this\n    /// node.  Can be None for default values and text nodes.  This is\n    /// essentially an optimization to avoid referencing the root rule node.\n    pub rules: Option<StrongRuleNode>,\n\n    /// The element's computed values if visited, only computed if there's a\n    /// relevant link for this element. A element's \"relevant link\" is the\n    /// element being matched if it is a link or the nearest ancestor link.\n    visited_style: Option<Arc<ComputedValues>>,\n}\n\n/// The struct that Servo uses to represent computed values.\n///\n/// This struct contains an immutable atomically-reference-counted pointer to\n/// every kind of style struct.\n///\n/// When needed, the structs may be copied in order to get mutated.\n#[cfg(feature = \"servo\")]\n#[cfg_attr(feature = \"servo\", derive(Clone, Debug))]\npub struct ComputedValues {\n    /// The actual computed values\n    ///\n    /// In Gecko the outer ComputedValues is actually a ComputedStyle, whereas\n    /// ComputedValuesInner is the core set of computed values.\n    ///\n    /// We maintain this distinction in servo to reduce the amount of special\n    /// casing.\n    inner: ComputedValuesInner,\n\n    /// The pseudo-element that we're using.\n    pseudo: Option<PseudoElement>,\n}\n\nimpl ComputedValues {\n    /// Returns the pseudo-element that this style represents.\n    #[cfg(feature = \"servo\")]\n    pub fn pseudo(&self) -> Option<PseudoElement> {\n        self.pseudo\n    }\n\n    /// Returns true if this is the style for a pseudo-element.\n    #[cfg(feature = \"servo\")]\n    pub fn is_pseudo_style(&self) -> bool {\n        self.pseudo().is_some()\n    }\n\n    /// Returns whether this style's display value is equal to contents.\n    pub fn is_display_contents(&self) -> bool {\n        self.clone_display().is_contents()\n    }\n\n    /// Gets a reference to the rule node. Panic if no rule node exists.\n    pub fn rules(&self) -> &StrongRuleNode {\n        self.rules.as_ref().unwrap()\n    }\n\n    /// Returns the visited rules, if applicable.\n    pub fn visited_rules(&self) -> Option<&StrongRuleNode> {\n        self.visited_style().and_then(|s| s.rules.as_ref())\n    }\n\n    /// Gets a reference to the custom properties map (if one exists).\n    pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {\n        &self.custom_properties\n    }\n\n% for prop in data.longhands:\n% if not prop.logical:\n    /// Gets the computed value of a given property.\n    #[inline(always)]\n    #[allow(non_snake_case)]\n    pub fn clone_${prop.ident}(\n        &self,\n    ) -> longhands::${prop.ident}::computed_value::T {\n        self.get_${prop.style_struct.name_lower}().clone_${prop.ident}()\n    }\n\n    /// Gets the computed value of a given property.\n    #[inline(always)]\n    #[allow(non_snake_case)]\n    pub fn ${prop.ident}_equals(&self, other: &Self) -> bool {\n        self.get_${prop.style_struct.name_lower}().${prop.ident}_equals(other.get_${prop.style_struct.name_lower}())\n    }\n% endif\n% endfor\n\n    /// Writes the (resolved or computed) value of the given longhand as a string in `dest`.\n    ///\n    /// TODO(emilio): We should move all the special resolution from\n    /// nsComputedDOMStyle to ToResolvedValue instead.\n    pub fn computed_or_resolved_value(\n        &self,\n        property_id: LonghandId,\n        context: Option<&mut resolved::Context>,\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        let mut dest = CssWriter::new(dest);\n        let property_id = property_id.to_physical(self.writing_mode);\n        match property_id {\n            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):\n            <% props = list(props) %>\n            ${\" |\\n\".join(\"LonghandId::{}\".format(p.camel_case) for p in props)} => {\n                let value = match property_id {\n                    % for prop in props:\n                    % if not prop.logical:\n                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),\n                    % endif\n                    % endfor\n                    _ => unsafe { debug_unreachable!() },\n                };\n                if let Some(c) = context {\n                    c.current_longhand = Some(property_id);\n                    value.to_resolved_value(c).to_css(&mut dest)\n                } else {\n                    value.to_css(&mut dest)\n                }\n            }\n            % endfor\n        }\n    }\n\n    /// Returns the computed value of the given longhand as a\n    /// [`TypedValueList`], if supported.\n    pub fn property_value_to_typed_value_list(\n        &self,\n        property_id: LonghandId,\n    ) -> Option<TypedValueList> {\n        let property_id = property_id.to_physical(self.writing_mode);\n        match property_id {\n            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):\n            <% props = list(props) %>\n            ${\" |\\n\".join(\"LonghandId::{}\".format(p.camel_case) for p in props)} => {\n                let value = match property_id {\n                    % for prop in props:\n                    % if not prop.logical:\n                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),\n                    % endif\n                    % endfor\n                    _ => unsafe { debug_unreachable!() },\n                };\n                value.to_typed_value_list()\n            }\n            % endfor\n        }\n    }\n\n    /// Returns the given longhand's resolved value as a property declaration.\n    pub fn computed_or_resolved_declaration(\n        &self,\n        property_id: LonghandId,\n        context: Option<&mut resolved::Context>,\n    ) -> PropertyDeclaration {\n        let physical_property_id = property_id.to_physical(self.writing_mode);\n        match physical_property_id {\n            % for specified_type, props in groupby(data.longhands, key=lambda x: x.specified_type()):\n            <% props = list(props) %>\n            ${\" |\\n\".join(\"LonghandId::{}\".format(p.camel_case) for p in props)} => {\n                let mut computed_value = match physical_property_id {\n                    % for prop in props:\n                    % if not prop.logical:\n                    LonghandId::${prop.camel_case} => self.clone_${prop.ident}(),\n                    % endif\n                    % endfor\n                    _ => unsafe { debug_unreachable!() },\n                };\n                if let Some(c) = context {\n                    c.current_longhand = Some(physical_property_id);\n                    let resolved = computed_value.to_resolved_value(c);\n                    computed_value = ToResolvedValue::from_resolved_value(resolved);\n                }\n                let specified = ToComputedValue::from_computed_value(&computed_value);\n                % if props[0].boxed:\n                let specified = Box::new(specified);\n                % endif\n                % if len(props) == 1:\n                PropertyDeclaration::${props[0].camel_case}(specified)\n                % else:\n                unsafe {\n                    let mut out = mem::MaybeUninit::uninit();\n                    ptr::write(\n                        out.as_mut_ptr() as *mut PropertyDeclarationVariantRepr<${specified_type}>,\n                        PropertyDeclarationVariantRepr {\n                            tag: property_id as u16,\n                            value: specified,\n                        },\n                    );\n                    out.assume_init()\n                }\n                % endif\n            }\n            % endfor\n        }\n    }\n\n    /// Resolves the currentColor keyword.\n    ///\n    /// Any color value from computed values (except for the 'color' property\n    /// itself) should go through this method.\n    ///\n    /// Usage example:\n    /// let top_color =\n    ///   style.resolve_color(&style.get_border().clone_border_top_color());\n    #[inline]\n    pub fn resolve_color(&self, color: &computed::Color) -> crate::color::AbsoluteColor {\n        let current_color = self.get_inherited_text().clone_color();\n        color.resolve_to_absolute(&current_color)\n    }\n\n    /// Returns which longhand properties have different values in the two\n    /// ComputedValues.\n    #[cfg(feature = \"gecko_debug\")]\n    pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {\n        let mut set = LonghandIdSet::new();\n        % for prop in data.longhands:\n        % if not prop.logical:\n        if self.clone_${prop.ident}() != other.clone_${prop.ident}() {\n            set.insert(LonghandId::${prop.camel_case});\n        }\n        % endif\n        % endfor\n        set\n    }\n\n    /// Create a `TransitionPropertyIterator` for this styles transition properties.\n    pub fn transition_properties<'a>(&'a self) -> TransitionPropertyIterator<'a> {\n        TransitionPropertyIterator::from_style(self)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl ComputedValues {\n    /// Create a new refcounted `ComputedValues`\n    pub fn new(\n        pseudo: Option<&PseudoElement>,\n        custom_properties: crate::custom_properties::ComputedCustomProperties,\n        attribute_references: crate::dom::AttributeReferences,\n        writing_mode: WritingMode,\n        effective_zoom: computed::Zoom,\n        flags: ComputedValueFlags,\n        rules: Option<StrongRuleNode>,\n        visited_style: Option<Arc<ComputedValues>>,\n        % for style_struct in data.active_style_structs():\n        ${style_struct.ident}: Arc<style_structs::${style_struct.name}>,\n        % endfor\n    ) -> Arc<Self> {\n        Arc::new(Self {\n            inner: ComputedValuesInner {\n                custom_properties,\n                attribute_references,\n                writing_mode,\n                rules,\n                visited_style,\n                effective_zoom,\n                flags,\n            % for style_struct in data.active_style_structs():\n                ${style_struct.ident},\n            % endfor\n            },\n            pseudo: pseudo.cloned(),\n        })\n    }\n\n    /// Get the initial computed values.\n    pub fn initial_values_with_font_override(default_font: super::style_structs::Font) -> Arc<Self> {\n        use crate::computed_value_flags::ComputedValueFlags;\n        use servo_arc::Arc;\n        use super::{ComputedValues, ComputedValuesInner, longhands, style_structs};\n\n        Arc::new(ComputedValues {\n            inner: ComputedValuesInner {\n                % for style_struct in data.active_style_structs():\n                    % if style_struct.name == \"Font\":\n                        font: Arc::new(default_font),\n                    <% continue %>\n                    % endif %\n\n                    ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} {\n                        % for longhand in style_struct.longhands:\n                            % if not longhand.logical:\n                                ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(),\n                            % endif\n                        % endfor\n                        % if style_struct.name == \"Box\":\n                            original_display: longhands::display::get_initial_value(),\n                        % endif\n                    }),\n                % endfor\n                custom_properties: crate::custom_properties::ComputedCustomProperties::default(),\n                attribute_references: AttributeReferences::default(),\n                writing_mode: WritingMode::empty(),\n                rules: None,\n                visited_style: None,\n                effective_zoom: crate::values::computed::Zoom::ONE,\n                flags: ComputedValueFlags::empty(),\n            },\n            pseudo: None,\n        })\n    }\n\n    /// Converts the computed values to an Arc<> from a reference.\n    pub fn to_arc(&self) -> Arc<Self> {\n        // SAFETY: We're guaranteed to be allocated as an Arc<> since the\n        // functions above are the only ones that create ComputedValues\n        // instances in Servo (and that must be the case since ComputedValues'\n        // member is private).\n        unsafe { Arc::from_raw_addrefed(self) }\n    }\n\n    /// Serializes the computed value of this property as a string.\n    pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String {\n        match property {\n            PropertyDeclarationId::Longhand(id) => {\n                let mut context = resolved::Context {\n                    style: self,\n                    for_property: PropertyId::NonCustom(id.into()),\n                    current_longhand: Some(id),\n                };\n                let mut s = String::new();\n                self.computed_or_resolved_value(\n                    id,\n                    Some(&mut context),\n                    &mut s\n                ).unwrap();\n                s\n            }\n            PropertyDeclarationId::Custom(name) => {\n                // FIXME(bug 1869476): This should use a stylist to determine\n                // whether the name corresponds to an inherited custom property\n                // and then choose the inherited/non_inherited map accordingly.\n                let p = &self.custom_properties;\n                let Some(value) = p.inherited.get(name).or_else(|| p.non_inherited.get(name))\n                else {\n                    return String::new();\n                };\n                let mut context = resolved::Context {\n                    style: self,\n                    for_property: PropertyId::Custom(name.clone()),\n                    current_longhand: None,\n                };\n                value\n                    .clone()\n                    .to_resolved_value(&mut context)\n                    .to_css_string()\n            }\n        }\n    }\n\n    /// Calls the given function for each cached lazy pseudo-element style.\n    pub fn each_cached_lazy_pseudo<F>(&self, mut _f: F)\n    where\n        F: FnMut(&Self),\n    {\n        // Servo doesn't currently cache lazy pseudo-element styles.\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl ops::Deref for ComputedValues {\n    type Target = ComputedValuesInner;\n    fn deref(&self) -> &ComputedValuesInner {\n        &self.inner\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl ops::DerefMut for ComputedValues {\n    fn deref_mut(&mut self) -> &mut ComputedValuesInner {\n        &mut self.inner\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl ComputedValuesInner {\n    /// Share ComputedValues but with different flags.\n    pub fn clone_with_flags(&self, flags: ComputedValueFlags, pseudo: Option<&PseudoElement>) -> Arc<ComputedValues> {\n        Arc::new(ComputedValues {\n            inner: Self {\n                custom_properties: self.custom_properties.clone(),\n                attribute_references: self.attribute_references.clone(),\n                writing_mode: self.writing_mode.clone(),\n                rules: self.rules.clone(),\n                visited_style: self.visited_style.clone(),\n                flags,\n                effective_zoom: self.effective_zoom.clone(),\n                % for style_struct in data.active_style_structs():\n                ${style_struct.ident}: self.${style_struct.ident}.clone(),\n                % endfor\n            },\n            pseudo: pseudo.cloned(),\n        })\n    }\n\n    /// Returns the visited style, if any.\n    pub fn visited_style(&self) -> Option<&ComputedValues> {\n        self.visited_style.as_deref()\n    }\n\n    % for style_struct in data.active_style_structs():\n        /// Clone the ${style_struct.name} struct.\n        #[inline]\n        pub fn clone_${style_struct.name_lower}(&self) -> Arc<style_structs::${style_struct.name}> {\n            self.${style_struct.ident}.clone()\n        }\n\n        /// Get a immutable reference to the ${style_struct.name} struct.\n        #[inline]\n        pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {\n            &self.${style_struct.ident}\n        }\n\n        /// Get a mutable reference to the ${style_struct.name} struct.\n        #[inline]\n        pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {\n            Arc::make_mut(&mut self.${style_struct.ident})\n        }\n    % endfor\n\n    /// Gets a reference to the rule node. Panic if no rule node exists.\n    pub fn rules(&self) -> &StrongRuleNode {\n        self.rules.as_ref().unwrap()\n    }\n\n    #[inline]\n    /// Returns whether the \"content\" property for the given style is completely\n    /// ineffective, and would yield an empty `::before` or `::after`\n    /// pseudo-element.\n    pub fn ineffective_content_property(&self) -> bool {\n        use crate::values::generics::counters::Content;\n        match self.get_counters().content {\n            Content::Normal | Content::None => true,\n            Content::Items(ref items) => items.items.is_empty()\n        }\n    }\n\n    /// Whether the current style is multicolumn.\n    #[inline]\n    pub fn is_multicol(&self) -> bool {\n        self.get_column().is_multicol()\n    }\n\n    /// Get the logical computed inline size.\n    #[inline]\n    pub fn content_inline_size(&self) -> &computed::Size {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() {\n            &position_style.height\n        } else {\n            &position_style.width\n        }\n    }\n\n    /// Get the logical computed block size.\n    #[inline]\n    pub fn content_block_size(&self) -> &computed::Size {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() { &position_style.width } else { &position_style.height }\n    }\n\n    /// Get the logical computed min inline size.\n    #[inline]\n    pub fn min_inline_size(&self) -> &computed::Size {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() { &position_style.min_height } else { &position_style.min_width }\n    }\n\n    /// Get the logical computed min block size.\n    #[inline]\n    pub fn min_block_size(&self) -> &computed::Size {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() { &position_style.min_width } else { &position_style.min_height }\n    }\n\n    /// Get the logical computed max inline size.\n    #[inline]\n    pub fn max_inline_size(&self) -> &computed::MaxSize {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() { &position_style.max_height } else { &position_style.max_width }\n    }\n\n    /// Get the logical computed max block size.\n    #[inline]\n    pub fn max_block_size(&self) -> &computed::MaxSize {\n        let position_style = self.get_position();\n        if self.writing_mode.is_vertical() { &position_style.max_width } else { &position_style.max_height }\n    }\n\n    /// Get the logical computed padding for this writing mode.\n    #[inline]\n    pub fn logical_padding(&self) -> LogicalMargin<&computed::LengthPercentage> {\n        let padding_style = self.get_padding();\n        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(\n            &padding_style.padding_top.0,\n            &padding_style.padding_right.0,\n            &padding_style.padding_bottom.0,\n            &padding_style.padding_left.0,\n        ))\n    }\n\n    /// Gets the logical computed margin from this style.\n    #[inline]\n    pub fn logical_margin(&self) -> LogicalMargin<&computed::Margin> {\n        let margin_style = self.get_margin();\n        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(\n            &margin_style.margin_top,\n            &margin_style.margin_right,\n            &margin_style.margin_bottom,\n            &margin_style.margin_left,\n        ))\n    }\n\n    /// Gets the logical position from this style.\n    #[inline]\n    pub fn logical_position(&self) -> LogicalMargin<&computed::Inset> {\n        // FIXME(SimonSapin): should be the writing mode of the containing block, maybe?\n        let position_style = self.get_position();\n        LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new(\n            &position_style.top,\n            &position_style.right,\n            &position_style.bottom,\n            &position_style.left,\n        ))\n    }\n\n    /// Return true if the effects force the transform style to be Flat\n    pub fn overrides_transform_style(&self) -> bool {\n        use crate::computed_values::mix_blend_mode::T as MixBlendMode;\n\n        let effects = self.get_effects();\n        // TODO(gw): Add clip-path, isolation, mask-image, mask-border-source when supported.\n        effects.opacity < 1.0 ||\n           !effects.filter.0.is_empty() ||\n           !effects.clip.is_auto() ||\n           effects.mix_blend_mode != MixBlendMode::Normal\n    }\n\n    /// <https://drafts.csswg.org/css-transforms/#grouping-property-values>\n    pub fn get_used_transform_style(&self) -> computed_values::transform_style::T {\n        use crate::computed_values::transform_style::T as TransformStyle;\n\n        let box_ = self.get_box();\n\n        if self.overrides_transform_style() {\n            TransformStyle::Flat\n        } else {\n            // Return the computed value if not overridden by the above exceptions\n            box_.transform_style\n        }\n    }\n}\n\n/// A reference to a style struct of the parent, or our own style struct.\npub enum StyleStructRef<'a, T: 'static> {\n    /// A borrowed struct from the parent, for example, for inheriting style.\n    Borrowed(&'a T),\n    /// An owned struct, that we've already mutated.\n    Owned(UniqueArc<T>),\n    /// Temporarily vacated, will panic if accessed\n    Vacated,\n}\n\nimpl<'a, T: 'a> StyleStructRef<'a, T>\nwhere\n    T: Clone,\n{\n    /// Ensure a mutable reference of this value exists, either cloning the\n    /// borrowed value, or returning the owned one.\n    pub fn mutate(&mut self) -> &mut T {\n        if let StyleStructRef::Borrowed(v) = *self {\n            *self = StyleStructRef::Owned(UniqueArc::new(v.clone()));\n        }\n\n        match *self {\n            StyleStructRef::Owned(ref mut v) => v,\n            StyleStructRef::Borrowed(..) => unreachable!(),\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\")\n        }\n    }\n\n    /// Whether this is pointer-equal to the struct we're going to copy the\n    /// value from.\n    ///\n    /// This is used to avoid allocations when people write stuff like `font:\n    /// inherit` or such `all: initial`.\n    #[inline]\n    pub fn ptr_eq(&self, struct_to_copy_from: &T) -> bool {\n        match *self {\n            StyleStructRef::Owned(..) => false,\n            StyleStructRef::Borrowed(s) => {\n                s as *const T == struct_to_copy_from as *const T\n            }\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\")\n        }\n    }\n\n    /// Extract a unique Arc from this struct, vacating it.\n    ///\n    /// The vacated state is a transient one, please put the Arc back\n    /// when done via `put()`. This function is to be used to separate\n    /// the struct being mutated from the computed context\n    pub fn take(&mut self) -> UniqueArc<T> {\n        use std::mem::replace;\n        let inner = replace(self, StyleStructRef::Vacated);\n\n        match inner {\n            StyleStructRef::Owned(arc) => arc,\n            StyleStructRef::Borrowed(s) => UniqueArc::new(s.clone()),\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\"),\n        }\n    }\n\n    /// Replace vacated ref with an arc\n    pub fn put(&mut self, arc: UniqueArc<T>) {\n        debug_assert!(matches!(*self, StyleStructRef::Vacated));\n        *self = StyleStructRef::Owned(arc);\n    }\n\n    /// Get a mutable reference to the owned struct, or `None` if the struct\n    /// hasn't been mutated.\n    pub fn get_if_mutated(&mut self) -> Option<&mut T> {\n        match *self {\n            StyleStructRef::Owned(ref mut v) => Some(v),\n            StyleStructRef::Borrowed(..) => None,\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\")\n        }\n    }\n\n    /// Returns an `Arc` to the internal struct, constructing one if\n    /// appropriate.\n    pub fn build(self) -> Arc<T> {\n        match self {\n            StyleStructRef::Owned(v) => v.shareable(),\n            // SAFETY: We know all style structs are arc-allocated.\n            StyleStructRef::Borrowed(v) => unsafe { Arc::from_raw_addrefed(v) },\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\")\n        }\n    }\n}\n\nimpl<'a, T: 'a> ops::Deref for StyleStructRef<'a, T> {\n    type Target = T;\n\n    fn deref(&self) -> &T {\n        match *self {\n            StyleStructRef::Owned(ref v) => &**v,\n            StyleStructRef::Borrowed(v) => v,\n            StyleStructRef::Vacated => panic!(\"Accessed vacated style struct\")\n        }\n    }\n}\n\n/// A type used to compute a struct with minimal overhead.\n///\n/// This allows holding references to the parent/default computed values without\n/// actually cloning them, until we either build the style, or mutate the\n/// inherited value.\npub struct StyleBuilder<'a> {\n    /// The device we're using to compute style.\n    ///\n    /// This provides access to viewport unit ratios, etc.\n    pub device: &'a Device,\n\n    /// The stylist we're using to compute style except for media queries.\n    /// device is used in media queries instead.\n    pub stylist: Option<&'a Stylist>,\n\n    /// The style we're inheriting from.\n    ///\n    /// This is effectively\n    /// `parent_style.unwrap_or(device.default_computed_values())`.\n    inherited_style: &'a ComputedValues,\n\n    /// The style we're getting reset structs from.\n    reset_style: &'a ComputedValues,\n\n    /// The rule node representing the ordered list of rules matched for this\n    /// node.\n    pub rules: Option<StrongRuleNode>,\n\n    /// The computed custom properties and attributes.\n    pub substitution_functions: crate::custom_properties::ComputedSubstitutionFunctions,\n\n    /// The set of attributes used as values in `attr()`\n    pub attribute_references: crate::dom::AttributeReferences,\n\n    /// Non-custom properties that are considered invalid at compute time\n    /// due to cyclic dependencies with custom properties.\n    /// e.g. `--foo: 1em; font-size: var(--foo)` where `--foo` is registered.\n    pub invalid_non_custom_properties: LonghandIdSet,\n\n    /// The pseudo-element this style will represent.\n    pub pseudo: Option<&'a PseudoElement>,\n\n    /// Whether we have mutated any reset structs since the the last time\n    /// `clear_modified_reset` was called.  This is used to tell whether the\n    /// `StyleAdjuster` did any work.\n    modified_reset: bool,\n\n    /// Whether this is the style for the root element.\n    pub is_root_element: bool,\n\n    /// The writing mode flags.\n    ///\n    /// TODO(emilio): Make private.\n    pub writing_mode: WritingMode,\n\n    /// The color-scheme bits. Needed because they may otherwise be different between visited and\n    /// unvisited colors.\n    pub color_scheme: ColorSchemeFlags,\n\n    /// The effective zoom.\n    pub effective_zoom: computed::Zoom,\n\n    /// The effective zoom for inheritance (the \"specified\" zoom on this element).\n    pub effective_zoom_for_inheritance: computed::Zoom,\n\n    /// Flags for the computed value.\n    pub flags: Cell<ComputedValueFlags>,\n\n    /// The element's style if visited, only computed if there's a relevant link\n    /// for this element.  A element's \"relevant link\" is the element being\n    /// matched if it is a link or the nearest ancestor link.\n    pub visited_style: Option<Arc<ComputedValues>>,\n    % for style_struct in data.active_style_structs():\n        ${style_struct.ident}: StyleStructRef<'a, style_structs::${style_struct.name}>,\n    % endfor\n}\n\nimpl<'a> StyleBuilder<'a> {\n    /// Trivially construct a `StyleBuilder`.\n    pub fn new(\n        device: &'a Device,\n        stylist: Option<&'a Stylist>,\n        parent_style: Option<&'a ComputedValues>,\n        pseudo: Option<&'a PseudoElement>,\n        rules: Option<StrongRuleNode>,\n        is_root_element: bool,\n    ) -> Self {\n        let reset_style = device.default_computed_values();\n        let inherited_style = parent_style.unwrap_or(reset_style);\n\n        let flags = inherited_style.flags.inherited();\n        Self {\n            device,\n            stylist,\n            inherited_style,\n            reset_style,\n            pseudo,\n            rules,\n            modified_reset: false,\n            is_root_element,\n            substitution_functions: crate::custom_properties::ComputedSubstitutionFunctions::default(),\n            attribute_references: crate::dom::AttributeReferences::default(),\n            invalid_non_custom_properties: LonghandIdSet::default(),\n            writing_mode: inherited_style.writing_mode,\n            effective_zoom: inherited_style.effective_zoom,\n            effective_zoom_for_inheritance: computed::Zoom::ONE,\n            color_scheme: inherited_style.get_inherited_ui().color_scheme_bits(),\n            flags: Cell::new(flags),\n            visited_style: None,\n            % for style_struct in data.active_style_structs():\n            % if style_struct.inherited:\n            ${style_struct.ident}: StyleStructRef::Borrowed(inherited_style.get_${style_struct.name_lower}()),\n            % else:\n            ${style_struct.ident}: StyleStructRef::Borrowed(reset_style.get_${style_struct.name_lower}()),\n            % endif\n            % endfor\n        }\n    }\n\n    /// NOTE(emilio): This is done so we can compute relative units with respect\n    /// to the parent style, but all the early properties / writing-mode / etc\n    /// are already set to the right ones on the kid.\n    ///\n    /// Do _not_ actually call this to construct a style, this should mostly be\n    /// used for animations.\n    pub fn for_derived_style(\n        device: &'a Device,\n        stylist: Option<&'a Stylist>,\n        style_to_derive_from: &'a ComputedValues,\n        parent_style: Option<&'a ComputedValues>,\n    ) -> Self {\n        let reset_style = device.default_computed_values();\n        let inherited_style = parent_style.unwrap_or(reset_style);\n        let map = crate::custom_properties::ComputedSubstitutionFunctions::new(\n            Some(style_to_derive_from.custom_properties().clone()),\n            None,\n        );\n        Self {\n            device,\n            stylist,\n            inherited_style,\n            reset_style,\n            pseudo: None,\n            modified_reset: false,\n            is_root_element: false,\n            rules: None,\n            attribute_references: crate::dom::AttributeReferences::default(),\n            substitution_functions: map,\n            invalid_non_custom_properties: LonghandIdSet::default(),\n            writing_mode: style_to_derive_from.writing_mode,\n            effective_zoom: style_to_derive_from.effective_zoom,\n            effective_zoom_for_inheritance: Self::zoom_for_inheritance(style_to_derive_from.get_box().clone_zoom(), inherited_style),\n            color_scheme: style_to_derive_from.get_inherited_ui().color_scheme_bits(),\n            flags: Cell::new(style_to_derive_from.flags),\n            visited_style: None,\n            % for style_struct in data.active_style_structs():\n            ${style_struct.ident}: StyleStructRef::Borrowed(\n                style_to_derive_from.get_${style_struct.name_lower}()\n            ),\n            % endfor\n        }\n    }\n\n    /// Copy the reset properties from `style`.\n    pub fn copy_reset_from(&mut self, style: &'a ComputedValues) {\n        % for style_struct in data.active_style_structs():\n        % if not style_struct.inherited:\n        self.${style_struct.ident} =\n            StyleStructRef::Borrowed(style.get_${style_struct.name_lower}());\n        % endif\n        % endfor\n    }\n\n    % for property in data.longhands:\n    % if not property.logical:\n    % if not property.style_struct.inherited:\n    /// Inherit `${property.ident}` from our parent style.\n    #[allow(non_snake_case)]\n    pub fn inherit_${property.ident}(&mut self) {\n        let inherited_struct =\n            self.inherited_style.get_${property.style_struct.name_lower}();\n\n        self.modified_reset = true;\n        self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);\n\n        % if property.ident == \"content\" or property.ident == \"display\":\n        self.add_flags(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE);\n        % endif\n\n        if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {\n            return;\n        }\n\n        self.${property.style_struct.ident}.mutate()\n            .copy_${property.ident}_from(inherited_struct);\n    }\n    % else:\n    /// Reset `${property.ident}` to the initial value.\n    #[allow(non_snake_case)]\n    pub fn reset_${property.ident}(&mut self) {\n        let reset_struct =\n            self.reset_style.get_${property.style_struct.name_lower}();\n\n        if self.${property.style_struct.ident}.ptr_eq(reset_struct) {\n            return;\n        }\n\n        self.${property.style_struct.ident}.mutate()\n            .reset_${property.ident}(reset_struct);\n    }\n    % endif\n\n    % if not property.vector or property.vector.simple_bindings or engine == \"servo\":\n    /// Set the `${property.ident}` to the computed value `value`.\n    #[allow(non_snake_case)]\n    pub fn set_${property.ident}(\n        &mut self,\n        value: longhands::${property.ident}::computed_value::T\n    ) {\n        % if not property.style_struct.inherited:\n        self.modified_reset = true;\n        % endif\n\n        self.${property.style_struct.ident}.mutate()\n            .set_${property.ident}(\n                value,\n                % if property.logical:\n                self.writing_mode,\n                % endif\n            );\n    }\n    % endif\n    % endif\n    % endfor\n    <% del property %>\n\n    /// Inherits style from the parent element, accounting for the default\n    /// computed values that need to be provided as well.\n    pub fn for_inheritance(\n        device: &'a Device,\n        stylist: Option<&'a Stylist>,\n        parent: Option<&'a ComputedValues>,\n        pseudo: Option<&'a PseudoElement>,\n    ) -> Self {\n        // Rebuild the visited style from the parent, ensuring that it will also\n        // not have rules.  This matches the unvisited style that will be\n        // produced by this builder.  This assumes that the caller doesn't need\n        // to adjust or process visited style, so we can just build visited\n        // style here for simplicity.\n        let visited_style = parent.and_then(|parent| {\n            parent.visited_style().map(|style| {\n                Self::for_inheritance(\n                    device,\n                    stylist,\n                    Some(style),\n                    pseudo,\n                ).build()\n            })\n        });\n        let custom_properties = if let Some(p) = parent { p.custom_properties().clone() } else { crate::custom_properties::ComputedCustomProperties::default() };\n        let mut ret = Self::new(\n            device,\n            stylist,\n            parent,\n            pseudo,\n            /* rules = */ None,\n            /* is_root_element = */ false,\n        );\n        ret.substitution_functions.custom_properties = custom_properties;\n        ret.visited_style = visited_style;\n        ret\n    }\n\n    /// Returns whether we have a visited style.\n    pub fn has_visited_style(&self) -> bool {\n        self.visited_style.is_some()\n    }\n\n    /// Returns whether we're a pseudo-elements style.\n    pub fn is_pseudo_element(&self) -> bool {\n        self.pseudo.map_or(false, |p| !p.is_anon_box())\n    }\n\n    /// Returns the style we're getting reset properties from.\n    pub fn default_style(&self) -> &'a ComputedValues {\n        self.reset_style\n    }\n\n    % for style_struct in data.active_style_structs():\n        /// Gets an immutable view of the current `${style_struct.name}` style.\n        pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {\n            &self.${style_struct.ident}\n        }\n\n        /// Gets a mutable view of the current `${style_struct.name}` style.\n        pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} {\n            % if not style_struct.inherited:\n            self.modified_reset = true;\n            % endif\n            self.${style_struct.ident}.mutate()\n        }\n\n        /// Gets a mutable view of the current `${style_struct.name}` style.\n        pub fn take_${style_struct.name_lower}(&mut self) -> UniqueArc<style_structs::${style_struct.name}> {\n            % if not style_struct.inherited:\n            self.modified_reset = true;\n            % endif\n            self.${style_struct.ident}.take()\n        }\n\n        /// Gets a mutable view of the current `${style_struct.name}` style.\n        pub fn put_${style_struct.name_lower}(&mut self, s: UniqueArc<style_structs::${style_struct.name}>) {\n            self.${style_struct.ident}.put(s)\n        }\n\n        /// Gets a mutable view of the current `${style_struct.name}` style,\n        /// only if it's been mutated before.\n        pub fn get_${style_struct.name_lower}_if_mutated(&mut self)\n                                                         -> Option<&mut style_structs::${style_struct.name}> {\n            self.${style_struct.ident}.get_if_mutated()\n        }\n\n        /// Reset the current `${style_struct.name}` style to its default value.\n        pub fn reset_${style_struct.name_lower}_struct(&mut self) {\n            self.${style_struct.ident} =\n                StyleStructRef::Borrowed(self.reset_style.get_${style_struct.name_lower}());\n        }\n    % endfor\n    <% del style_struct %>\n\n    /// Returns whether this computed style represents a floated object.\n    pub fn is_floating(&self) -> bool {\n        self.get_box().clone_float().is_floating()\n    }\n\n    /// Returns whether this computed style represents an absolutely-positioned\n    /// object.\n    pub fn is_absolutely_positioned(&self) -> bool {\n        self.get_box().clone_position().is_absolutely_positioned()\n    }\n\n    /// Whether this style has a top-layer style.\n    #[cfg(feature = \"servo\")]\n    pub fn in_top_layer(&self) -> bool {\n        matches!(self.get_box().clone__servo_top_layer(),\n                 longhands::_servo_top_layer::computed_value::T::Top)\n    }\n\n    /// Whether this style has a top-layer style.\n    #[cfg(feature = \"gecko\")]\n    pub fn in_top_layer(&self) -> bool {\n        matches!(self.get_box().clone__moz_top_layer(),\n                 longhands::_moz_top_layer::computed_value::T::Auto)\n    }\n\n    /// Clears the \"have any reset structs been modified\" flag.\n    pub fn clear_modified_reset(&mut self) {\n        self.modified_reset = false;\n    }\n\n    /// Returns whether we have mutated any reset structs since the the last\n    /// time `clear_modified_reset` was called.\n    pub fn modified_reset(&self) -> bool {\n        self.modified_reset\n    }\n\n    /// Return the current flags.\n    #[inline]\n    pub fn flags(&self) -> ComputedValueFlags {\n        self.flags.get()\n    }\n\n    /// Add a flag to the current builder.\n    #[inline]\n    pub fn add_flags(&self, flag: ComputedValueFlags) {\n        let flags = self.flags() | flag;\n        self.flags.set(flags);\n    }\n\n    /// Removes a flag to the current builder.\n    #[inline]\n    pub fn remove_flags(&self, flag: ComputedValueFlags) {\n        let flags = self.flags() & !flag;\n        self.flags.set(flags);\n    }\n\n    /// Turns this `StyleBuilder` into a proper `ComputedValues` instance.\n    pub fn build(self) -> Arc<ComputedValues> {\n        ComputedValues::new(\n            self.pseudo,\n            self.substitution_functions.custom_properties,\n            self.attribute_references,\n            self.writing_mode,\n            self.effective_zoom,\n            self.flags.get(),\n            self.rules,\n            self.visited_style,\n            % for style_struct in data.active_style_structs():\n            self.${style_struct.ident}.build(),\n            % endfor\n        )\n    }\n\n    /// Get the substitution function maps if necessary.\n    pub fn substitution_functions(&self) -> &crate::custom_properties::ComputedSubstitutionFunctions {\n        &self.substitution_functions\n    }\n\n    /// Get the custom properties map if necessary.\n    pub fn custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {\n        &self.substitution_functions.custom_properties\n    }\n\n    /// Get the inherited custom properties map.\n    pub fn inherited_custom_properties(&self) -> &crate::custom_properties::ComputedCustomProperties {\n        &self.inherited_style.custom_properties\n    }\n\n    /// Access to various information about our inherited styles.  We don't\n    /// expose an inherited ComputedValues directly, because in the\n    /// ::first-line case some of the inherited information needs to come from\n    /// one ComputedValues instance and some from a different one.\n\n    /// Inherited writing-mode.\n    pub fn inherited_writing_mode(&self) -> &WritingMode {\n        &self.inherited_style.writing_mode\n    }\n\n    /// The effective zoom value that we should multiply absolute lengths by.\n    pub fn effective_zoom(&self) -> computed::Zoom {\n        self.effective_zoom\n    }\n\n    /// The zoom specified on this element.\n    pub fn specified_zoom(&self) -> computed::Zoom {\n        self.get_box().clone_zoom()\n    }\n\n    /// Computes effective_zoom and effective_zoom_for_inheritance based on the current style\n    /// information.\n    pub fn recompute_effective_zooms(&mut self) {\n        let specified = self.specified_zoom();\n        self.effective_zoom = self.inherited_style.effective_zoom.compute_effective(specified);\n        self.effective_zoom_for_inheritance = Self::zoom_for_inheritance(specified, self.inherited_style);\n    }\n\n    fn zoom_for_inheritance(specified: computed::Zoom, inherited_style: &ComputedValues) -> computed::Zoom {\n        if specified.is_document() {\n            // If our inherited effective zoom has derived to zero, there's not much we can do.\n            // This value is not exposed to content anyways (it's used for scrollbars and to avoid\n            // zoom affecting canvas).\n            inherited_style.effective_zoom.inverted().unwrap_or(computed::Zoom::ONE)\n        } else {\n            specified\n        }\n    }\n\n    /// The computed value flags of our parent.\n    #[inline]\n    pub fn get_parent_flags(&self) -> ComputedValueFlags {\n        self.inherited_style.flags\n    }\n\n    /// Calculate the line height, given the currently resolved line-height and font.\n    pub fn calc_line_height(\n        &self,\n        device: &Device,\n        line_height_base: LineHeightBase,\n        writing_mode: WritingMode,\n    ) -> computed::NonNegativeLength {\n        use crate::computed_value_flags::ComputedValueFlags;\n        let (font, flag) = match line_height_base {\n            LineHeightBase::CurrentStyle => (\n                self.get_font(),\n                ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,\n            ),\n            LineHeightBase::InheritedStyle => (\n                self.get_parent_font(),\n                ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,\n            ),\n        };\n        let line_height = font.clone_line_height();\n        if matches!(line_height, computed::LineHeight::Normal) {\n            self.add_flags(flag);\n        }\n        let lh = device.calc_line_height(&font, writing_mode, None);\n        if line_height_base == LineHeightBase::InheritedStyle {\n            // Apply our own zoom if our style source is the parent style.\n            computed::NonNegativeLength::new(self.effective_zoom_for_inheritance.zoom(lh.px()))\n        } else {\n            lh\n        }\n    }\n\n    /// And access to inherited style structs.\n    % for style_struct in data.active_style_structs():\n        /// Gets our inherited `${style_struct.name}`.  We don't name these\n        /// accessors `inherited_${style_struct.name_lower}` because we already\n        /// have things like \"box\" vs \"inherited_box\" as struct names.  Do the\n        /// next-best thing and call them `parent_${style_struct.name_lower}`\n        /// instead.\n        pub fn get_parent_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} {\n            self.inherited_style.get_${style_struct.name_lower}()\n        }\n    % endfor\n}\n\n/// A per-longhand function that performs the CSS cascade for that longhand.\npub type CascadePropertyFn =\n    unsafe extern \"Rust\" fn(\n        declaration: &PropertyDeclaration,\n        context: &mut computed::Context,\n    );\n\n/// A per-longhand array of functions to perform the CSS cascade on each of\n/// them, effectively doing virtual dispatch.\npub static CASCADE_PROPERTY: [CascadePropertyFn; ${len(data.longhands)}] = [\n    % for property in data.longhands:\n        longhands::${property.ident}::cascade_property,\n    % endfor\n];\n\n/// An identifier for a given alias property.\n#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]\n#[repr(u16)]\npub enum AliasId {\n    % for i, property in enumerate(data.all_aliases()):\n        /// ${property.name}\n        ${property.camel_case} = ${i},\n    % endfor\n}\n\nimpl fmt::Debug for AliasId {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        let name = NonCustomPropertyId::from(*self).name();\n        formatter.write_str(name)\n    }\n}\n\nimpl AliasId {\n    /// Returns the property we're aliasing, as a longhand or a shorthand.\n    #[inline]\n    pub fn aliased_property(self) -> NonCustomPropertyId {\n        static MAP: [NonCustomPropertyId; ${len(data.all_aliases())}] = [\n        % for alias in data.all_aliases():\n            % if alias.original.type() == \"longhand\":\n            NonCustomPropertyId::from_longhand(LonghandId::${alias.original.camel_case}),\n            % else:\n            <% assert alias.original.type() == \"shorthand\" %>\n            NonCustomPropertyId::from_shorthand(ShorthandId::${alias.original.camel_case}),\n            % endif\n        % endfor\n        ];\n        MAP[self as usize]\n    }\n}\n\n/// Call the given macro with tokens like this for each longhand and shorthand properties\n/// that is enabled in content:\n///\n/// ```\n/// [CamelCaseName, SetCamelCaseName, PropertyId::Longhand(LonghandId::CamelCaseName)],\n/// ```\n///\n/// NOTE(emilio): Callers are responsible to deal with prefs.\n#[macro_export]\nmacro_rules! css_properties_accessors {\n    ($macro_name: ident) => {\n        $macro_name! {\n            % for kind, props in [(\"Longhand\", data.longhands), (\"Shorthand\", data.shorthands)]:\n                % for property in props:\n                    % if property.enabled_in_content():\n                        % for prop in [property] + property.aliases:\n                            % if '-' in prop.name:\n                                [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},\n                                 PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())],\n                            % endif\n                            [${prop.camel_case}, Set${prop.camel_case},\n                             PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())],\n                        % endfor\n                    % endif\n                % endfor\n            % endfor\n        }\n    }\n}\n\n/// Call the given macro with tokens like this for each longhand properties:\n///\n/// ```\n/// { snake_case_ident }\n/// ```\n#[macro_export]\nmacro_rules! longhand_properties_idents {\n    ($macro_name: ident) => {\n        $macro_name! {\n            % for property in data.longhands:\n                { ${property.ident} }\n            % endfor\n        }\n    }\n}\n\n// Large pages generate tens of thousands of ComputedValues.\n#[cfg(feature = \"gecko\")]\nsize_of_test!(ComputedValues, 248);\n#[cfg(feature = \"servo\")]\nsize_of_test!(ComputedValues, 224);\n\n// FFI relies on this.\nsize_of_test!(Option<Arc<ComputedValues>>, 8);\n\n// There are two reasons for this test to fail:\n//\n//   * Your changes made a specified value type for a given property go\n//     over the threshold. In that case, you should try to shrink it again\n//     or, if not possible, mark the property as boxed in the property\n//     definition.\n//\n//   * Your changes made a specified value type smaller, so that it no\n//     longer needs to be boxed. In this case you just need to remove\n//     boxed=True from the property definition. Nice job!\n#[cfg(target_pointer_width = \"64\")]\n#[allow(dead_code)] // https://github.com/rust-lang/rust/issues/96952\nconst BOX_THRESHOLD: usize = 24;\n% for longhand in data.longhands:\n#[cfg(target_pointer_width = \"64\")]\n% if longhand.boxed:\nconst_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() > BOX_THRESHOLD);\n% else:\nconst_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>() <= BOX_THRESHOLD);\n% endif\n% endfor\n\n% if engine == \"servo\":\n% for effect_name in [\"repaint\", \"recalculate_overflow\", \"rebuild_stacking_context\", \"rebuild_box\"]:\npub(crate) fn restyle_damage_${effect_name} (old: &ComputedValues, new: &ComputedValues) -> bool {\n    % for style_struct in data.active_style_structs():\n        <% longhands_affected = [effect_name in longhand.servo_restyle_damage.split() for longhand in style_struct.longhands if not longhand.logical] %>\n        % if any(longhands_affected):\n        let old_${style_struct.name_lower} = old.get_${style_struct.name_lower}();\n        let new_${style_struct.name_lower} = new.get_${style_struct.name_lower}();\n        if !std::ptr::eq(old_${style_struct.name_lower}, new_${style_struct.name_lower}) {\n            if\n            % for longhand in style_struct.longhands:\n            % if effect_name in longhand.servo_restyle_damage.split() and not longhand.logical:\n                old_${style_struct.name_lower}.${longhand.ident} != new_${style_struct.name_lower}.${longhand.ident} ||\n            % endif\n            % endfor\n            false {\n                return true;\n            }\n        }\n        % endif\n    % endfor\n    false\n}\n% endfor\n% endif\n\n/// Descriptor types for @-rules like @font-face and @counter-style.\n<%def name=\"generate_descriptors(descriptors)\">\nuse super::*;\n#[allow(unused_imports)]\nuse crate::values::specified;\n\n/// Descriptor identifier.\n#[derive(Clone, Copy, Debug, Eq, Hash, FromPrimitive, Parse, PartialEq)]\n#[repr(u8)]\npub enum DescriptorId {\n    % for descriptor in descriptors:\n    /// The \"${descriptor.name}\" descriptor.\n    ${descriptor.camel_case},\n    % endfor\n}\n\nimpl DescriptorId {\n    /// The total number of descriptors.\n    pub const COUNT: usize = ${len(descriptors)};\n\n    /// The CSS name of this descriptor.\n    pub fn name(&self) -> &'static str {\n        const NAMES: [&'static str; DescriptorId::COUNT] = [\n        % for descriptor in descriptors:\n            \"${descriptor.name}\",\n        % endfor\n        ];\n        NAMES[*self as usize]\n    }\n}\n\n/// All descriptor values.\n#[derive(Clone, Debug, Default, ToShmem, PartialEq, MallocSizeOf)]\npub struct Descriptors {\n    % for descriptor in descriptors:\n    /// The \"${descriptor.name}\" descriptor value.\n    % if descriptor.ignore_malloc_size_of:\n    #[ignore_malloc_size_of = \"${descriptor.ignore_malloc_size_of}\"]\n    % endif\n    pub ${descriptor.ident}: Option<${descriptor.type}>,\n    % endfor\n}\n\nimpl Descriptors {\n    /// Gets a descriptor in CSS syntax.\n    pub fn get(&self, id: DescriptorId, dest: &mut CssStringWriter) -> fmt::Result {\n        let mut dest = CssWriter::new(dest);\n        match id {\n        % for descriptor in descriptors:\n            DescriptorId::${descriptor.camel_case} => self.${descriptor.ident}.to_css(&mut dest),\n        % endfor\n        }\n    }\n\n    /// Parses a given descriptor. Returns whether the descriptor changed.\n    pub fn set<'i, 't>(&mut self, id: DescriptorId, context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<bool, ParseError<'i>> {\n        use crate::parser::Parse;\n        // DeclarationParser also calls parse_entirely so we’d normally not need to, but in this\n        // case we do because we set the value as a side effect rather than returning it.\n        match id {\n        % for descriptor in descriptors:\n            DescriptorId::${descriptor.camel_case} => {\n                let value = Some(input.parse_entirely(|i| Parse::parse(context, i))?);\n                let change = self.${descriptor.ident} != value;\n                self.${descriptor.ident} = value;\n                Ok(change)\n            },\n        % endfor\n        }\n    }\n\n    /// Removes a descriptor. Returns true if it used to be set.\n    pub fn remove(&mut self, id: DescriptorId) -> bool {\n        match id {\n        % for descriptor in descriptors:\n            DescriptorId::${descriptor.camel_case} => self.${descriptor.ident}.take().is_some(),\n        % endfor\n        }\n    }\n\n    /// Returns the count of set descriptors.\n    pub fn len(&self) -> usize {\n        let mut count = 0;\n        % for descriptor in descriptors:\n        if self.${descriptor.ident}.is_some() {\n            count += 1;\n        }\n        % endfor\n        count\n    }\n\n    /// Returns the descriptor at position `i`.\n    pub fn at(&self, i: usize) -> Option<DescriptorId> {\n        let mut cur = 0;\n        % for descriptor in descriptors:\n        if self.${descriptor.ident}.is_some() {\n            if cur == i {\n                return Some(DescriptorId::${descriptor.camel_case});\n            }\n            cur += 1;\n        }\n        % endfor\n        let _ = cur; // Silences warning on the last descriptor\n        None\n    }\n}\n\nimpl ToCss for Descriptors {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use std::fmt::Write;\n        % for descriptor in descriptors:\n        if let Some(ref value) = self.${descriptor.ident} {\n            dest.write_str(\"${descriptor.name}: \")?;\n            value.to_css(dest)?;\n            dest.write_str(\"; \")?;\n        }\n        % endfor\n        Ok(())\n    }\n}\n\n/// Parser for descriptor declarations in at-rules.\npub struct DescriptorParser<'a, 'b: 'a> {\n    /// The parser context.\n    pub context: &'a ParserContext<'b>,\n    /// The descriptors to parse into.\n    pub descriptors: &'a mut Descriptors,\n}\n\nimpl<'a, 'b, 'i> cssparser::AtRuleParser<'i> for DescriptorParser<'a, 'b> {\n    type Prelude = ();\n    type AtRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i> cssparser::QualifiedRuleParser<'i> for DescriptorParser<'a, 'b> {\n    type Prelude = ();\n    type QualifiedRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i> cssparser::RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>\n    for DescriptorParser<'a, 'b>\n{\n    fn parse_qualified(&self) -> bool {\n        false\n    }\n    fn parse_declarations(&self) -> bool {\n        true\n    }\n}\n\nimpl<'a, 'b, 'i> cssparser::DeclarationParser<'i> for DescriptorParser<'a, 'b> {\n    type Declaration = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_value<'t>(\n        &mut self,\n        name: cssparser::CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        _declaration_start: &cssparser::ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        let Ok(id) = DescriptorId::from_ident(name.as_ref()) else {\n            return Err(\n                input.new_custom_error(\n                    selectors::parser::SelectorParseErrorKind::UnexpectedIdent(name.clone())\n                )\n            );\n        };\n        self.descriptors.set(id, self.context, input)?;\n        Ok(())\n    }\n}\n</%def>\n\n/// Generated code for @font-face descriptors.\npub mod font_face {\n    use crate::font_face::*;\n${generate_descriptors(data.font_face_descriptors)}\n}\n\n/// Generated code for @counter-style descriptors.\npub mod counter_style {\n    use crate::counter_style::*;\n${generate_descriptors(data.counter_style_descriptors)}\n}\n\n/// Generated code for @property descriptors.\npub mod property {\n    use crate::properties_and_values::rule::*;\n${generate_descriptors(data.property_descriptors)}\n}\n\n/// Generated code for @view-transition descriptors.\npub mod view_transition {\n    use crate::stylesheets::view_transition_rule::*;\n${generate_descriptors(data.view_transition_descriptors)}\n}\n"
  },
  {
    "path": "style/properties/property_descriptors.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# The descriptors of the @property rule.\n\n# https://drafts.css-houdini.org/css-properties-values-api-1/#the-syntax-descriptor\n[syntax]\ntype = \"SyntaxDescriptor\"\n\n# https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor\n[inherits]\ntype = \"Inherits\"\n\n# https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor\n[initial-value]\ntype = \"InitialValue\"\nignore_malloc_size_of = \"Arc\"\n"
  },
  {
    "path": "style/properties/shorthands.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Manual shorthand parsing and serialization\n#![allow(missing_docs)]\n\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::specified;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{\n    values::SequenceWriter, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo,\n    StyleParseErrorKind, ToCss,\n};\n\nmacro_rules! expanded {\n    ( $( $name: ident: $value: expr ),+ ) => {\n        expanded!( $( $name: $value, )+ )\n    };\n    ( $( $name: ident: $value: expr, )+ ) => {\n        Longhands {\n            $(\n                $name: $crate::properties::MaybeBoxed::maybe_boxed($value),\n            )+\n        }\n    }\n}\npub(crate) use expanded;\n\nmacro_rules! try_parse_one {\n    ($context: expr, $input: expr, $var: ident, $parse_path: path) => {\n        if $var.is_none() {\n            if let Ok(value) = $input.try_parse(|i| $parse_path($context, i)) {\n                $var = Some(value);\n                continue;\n            }\n        }\n    };\n    ($input: expr, $var: ident, $parse_path: path) => {\n        if $var.is_none() {\n            if let Ok(value) = $input.try_parse(|i| $parse_path(i)) {\n                $var = Some(value);\n                continue;\n            }\n        }\n    };\n}\n\nmacro_rules! unwrap_or_initial {\n    ($prop: ident) => {\n        unwrap_or_initial!($prop, $prop)\n    };\n    ($prop: ident, $expr: expr) => {\n        $expr.unwrap_or_else(|| $prop::get_initial_specified_value())\n    };\n}\n\n/// Serializes a border shorthand value composed of width/style/color.\npub fn serialize_directional_border<W>(\n    dest: &mut CssWriter<W>,\n    width: &specified::BorderSideWidth,\n    style: &specified::BorderStyle,\n    color: &specified::Color,\n) -> fmt::Result\nwhere\n    W: Write,\n{\n    use specified::{BorderSideWidth, BorderStyle, Color};\n    let has_style = *style != BorderStyle::None;\n    let has_color = *color != Color::CurrentColor;\n    let has_width = *width != BorderSideWidth::medium();\n    if !has_style && !has_color && !has_width {\n        return width.to_css(dest);\n    }\n    let mut writer = SequenceWriter::new(dest, \" \");\n    if has_width {\n        writer.item(width)?;\n    }\n    if has_style {\n        writer.item(style)?;\n    }\n    if has_color {\n        writer.item(color)?;\n    }\n    Ok(())\n}\n\npub fn parse_border<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<\n    (\n        specified::BorderSideWidth,\n        specified::BorderStyle,\n        specified::Color,\n    ),\n    ParseError<'i>,\n> {\n    use crate::values::specified::{BorderSideWidth, BorderStyle, Color};\n    let mut color = None;\n    let mut style = None;\n    let mut width = None;\n    let mut parsed = 0;\n    loop {\n        parsed += 1;\n        try_parse_one!(context, input, width, BorderSideWidth::parse);\n        try_parse_one!(input, style, BorderStyle::parse);\n        try_parse_one!(context, input, color, Color::parse);\n        parsed -= 1;\n        break;\n    }\n    if parsed == 0 {\n        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n    }\n    Ok((\n        width.unwrap_or(BorderSideWidth::medium()),\n        style.unwrap_or(BorderStyle::None),\n        color.unwrap_or(Color::CurrentColor),\n    ))\n}\n\npub mod border_block {\n    use super::*;\n    pub use crate::properties::generated::shorthands::border_block::*;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let (width, style, color) = super::parse_border(context, input)?;\n        Ok(Longhands {\n            border_block_start_width: width.clone(),\n            border_block_start_style: style.clone(),\n            border_block_start_color: color.clone(),\n            border_block_end_width: width,\n            border_block_end_style: style,\n            border_block_end_color: color,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            // FIXME: Should serialize empty if start != end, right?\n            super::serialize_directional_border(\n                dest,\n                &self.border_block_start_width,\n                &self.border_block_start_style,\n                &self.border_block_start_color,\n            )\n        }\n    }\n}\n\npub mod border_inline {\n    use super::*;\n    pub use crate::properties::generated::shorthands::border_inline::*;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let (width, style, color) = super::parse_border(context, input)?;\n        Ok(Longhands {\n            border_inline_start_width: width.clone(),\n            border_inline_start_style: style.clone(),\n            border_inline_start_color: color.clone(),\n            border_inline_end_width: width,\n            border_inline_end_style: style,\n            border_inline_end_color: color,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            // FIXME: Should serialize empty if start != end, right?\n            super::serialize_directional_border(\n                dest,\n                &self.border_inline_start_width,\n                &self.border_inline_start_style,\n                &self.border_inline_start_color,\n            )\n        }\n    }\n}\n\npub mod border_radius {\n    pub use crate::properties::generated::shorthands::border_radius::*;\n\n    use super::*;\n    use crate::values::generics::border::BorderCornerRadius;\n    use crate::values::generics::rect::Rect;\n    use crate::values::specified::BorderRadius;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let radii = BorderRadius::parse(context, input)?;\n        Ok(expanded! {\n            border_top_left_radius: radii.top_left,\n            border_top_right_radius: radii.top_right,\n            border_bottom_right_radius: radii.bottom_right,\n            border_bottom_left_radius: radii.bottom_left,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let LonghandsToSerialize {\n                border_top_left_radius: &BorderCornerRadius(ref tl),\n                border_top_right_radius: &BorderCornerRadius(ref tr),\n                border_bottom_right_radius: &BorderCornerRadius(ref br),\n                border_bottom_left_radius: &BorderCornerRadius(ref bl),\n            } = *self;\n\n            let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width());\n            let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height());\n\n            BorderRadius::serialize_rects(widths, heights, dest)\n        }\n    }\n}\n\npub mod border_image {\n    pub use crate::properties::generated::shorthands::border_image::*;\n\n    use super::*;\n    use crate::properties::longhands::{\n        border_image_outset, border_image_repeat, border_image_slice, border_image_source,\n        border_image_width,\n    };\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut outset = border_image_outset::get_initial_specified_value();\n        let mut repeat = border_image_repeat::get_initial_specified_value();\n        let mut slice = border_image_slice::get_initial_specified_value();\n        let mut source = border_image_source::get_initial_specified_value();\n        let mut width = border_image_width::get_initial_specified_value();\n        let mut any = false;\n        let mut parsed_slice = false;\n        let mut parsed_source = false;\n        let mut parsed_repeat = false;\n        loop {\n            if !parsed_slice {\n                if let Ok(value) =\n                    input.try_parse(|input| border_image_slice::parse(context, input))\n                {\n                    parsed_slice = true;\n                    any = true;\n                    slice = value;\n                    // Parse border image width and outset, if applicable.\n                    let maybe_width_outset: Result<_, ParseError> = input.try_parse(|input| {\n                        input.expect_delim('/')?;\n\n                        // Parse border image width, if applicable.\n                        let w = input\n                            .try_parse(|input| border_image_width::parse(context, input))\n                            .ok();\n\n                        // Parse border image outset if applicable.\n                        let o = input\n                            .try_parse(|input| {\n                                input.expect_delim('/')?;\n                                border_image_outset::parse(context, input)\n                            })\n                            .ok();\n                        if w.is_none() && o.is_none() {\n                            return Err(\n                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                            );\n                        }\n                        Ok((w, o))\n                    });\n                    if let Ok((w, o)) = maybe_width_outset {\n                        if let Some(w) = w {\n                            width = w;\n                        }\n                        if let Some(o) = o {\n                            outset = o;\n                        }\n                    }\n                    continue;\n                }\n            }\n            if !parsed_source {\n                if let Ok(value) =\n                    input.try_parse(|input| border_image_source::parse(context, input))\n                {\n                    source = value;\n                    parsed_source = true;\n                    any = true;\n                    continue;\n                }\n            }\n            if !parsed_repeat {\n                if let Ok(value) =\n                    input.try_parse(|input| border_image_repeat::parse(context, input))\n                {\n                    repeat = value;\n                    parsed_repeat = true;\n                    any = true;\n                    continue;\n                }\n            }\n            break;\n        }\n        if !any {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n           border_image_outset: outset,\n           border_image_repeat: repeat,\n           border_image_slice: slice,\n           border_image_source: source,\n           border_image_width: width,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let mut has_any = false;\n            let has_source =\n                *self.border_image_source != border_image_source::get_initial_specified_value();\n            has_any = has_any || has_source;\n            let has_slice =\n                *self.border_image_slice != border_image_slice::get_initial_specified_value();\n            has_any = has_any || has_slice;\n            let has_outset =\n                *self.border_image_outset != border_image_outset::get_initial_specified_value();\n            has_any = has_any || has_outset;\n            let has_width =\n                *self.border_image_width != border_image_width::get_initial_specified_value();\n            has_any = has_any || has_width;\n            let has_repeat =\n                *self.border_image_repeat != border_image_repeat::get_initial_specified_value();\n            has_any = has_any || has_repeat;\n            if has_source || !has_any {\n                self.border_image_source.to_css(dest)?;\n                if !has_any {\n                    return Ok(());\n                }\n            }\n            let needs_slice = has_slice || has_width || has_outset;\n            if needs_slice {\n                if has_source {\n                    dest.write_char(' ')?;\n                }\n                self.border_image_slice.to_css(dest)?;\n                if has_width || has_outset {\n                    dest.write_str(\" /\")?;\n                    if has_width {\n                        dest.write_char(' ')?;\n                        self.border_image_width.to_css(dest)?;\n                    }\n                    if has_outset {\n                        dest.write_str(\" / \")?;\n                        self.border_image_outset.to_css(dest)?;\n                    }\n                }\n            }\n            if has_repeat {\n                if has_source || needs_slice {\n                    dest.write_char(' ')?;\n                }\n                self.border_image_repeat.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod border {\n    pub use crate::properties::generated::shorthands::border::*;\n\n    use super::*;\n    pub use crate::properties::generated::shorthands::border_left;\n    use crate::properties::longhands::{\n        border_image_outset, border_image_repeat, border_image_slice, border_image_source,\n        border_image_width,\n    };\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let (width, style, color) = super::parse_border(context, input)?;\n        Ok(expanded! {\n            border_top_width: width.clone(),\n            border_top_style: style,\n            border_top_color: color.clone(),\n            border_right_width: width.clone(),\n            border_right_style: style,\n            border_right_color: color.clone(),\n            border_bottom_width: width.clone(),\n            border_bottom_style: style,\n            border_bottom_color: color.clone(),\n            border_left_width: width.clone(),\n            border_left_style: style,\n            border_left_color: color.clone(),\n\n            // The 'border' shorthand resets 'border-image' to its initial value.\n            // See https://drafts.csswg.org/css-backgrounds-3/#the-border-shorthands\n            border_image_outset: border_image_outset::get_initial_specified_value(),\n            border_image_repeat: border_image_repeat::get_initial_specified_value(),\n            border_image_slice: border_image_slice::get_initial_specified_value(),\n            border_image_source: border_image_source::get_initial_specified_value(),\n            border_image_width: border_image_width::get_initial_specified_value(),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::properties::longhands;\n\n            // If any of the border-image longhands differ from their initial specified values we should not\n            // invoke serialize_directional_border(), so there is no point in continuing on to compute all_equal.\n            if *self.border_image_outset\n                != longhands::border_image_outset::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            if *self.border_image_repeat\n                != longhands::border_image_repeat::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            if *self.border_image_slice\n                != longhands::border_image_slice::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            if *self.border_image_source\n                != longhands::border_image_source::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            if *self.border_image_width\n                != longhands::border_image_width::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n\n            let all_equal = {\n                let border_top_width = self.border_top_width;\n                let border_top_style = self.border_top_style;\n                let border_top_color = self.border_top_color;\n                let border_right_width = self.border_right_width;\n                let border_right_style = self.border_right_style;\n                let border_right_color = self.border_right_color;\n                let border_bottom_width = self.border_bottom_width;\n                let border_bottom_style = self.border_bottom_style;\n                let border_bottom_color = self.border_bottom_color;\n                let border_left_width = self.border_left_width;\n                let border_left_style = self.border_left_style;\n                let border_left_color = self.border_left_color;\n\n                border_top_width == border_right_width\n                    && border_right_width == border_bottom_width\n                    && border_bottom_width == border_left_width\n                    && border_top_style == border_right_style\n                    && border_right_style == border_bottom_style\n                    && border_bottom_style == border_left_style\n                    && border_top_color == border_right_color\n                    && border_right_color == border_bottom_color\n                    && border_bottom_color == border_left_color\n            };\n\n            // If all longhands are all present, then all sides should be the same,\n            // so we can just one set of color/style/width\n            if !all_equal {\n                return Ok(());\n            }\n            super::serialize_directional_border(\n                dest,\n                self.border_left_width,\n                self.border_left_style,\n                self.border_left_color,\n            )\n        }\n    }\n\n    // We need to implement this by hand because deriving this would also derive border-image,\n    // which this property only resets. Just use the same as border-left for simplicity.\n    impl SpecifiedValueInfo for Longhands {\n        const SUPPORTED_TYPES: u8 = border_left::Longhands::SUPPORTED_TYPES;\n\n        fn collect_completion_keywords(f: KeywordsCollectFn) {\n            border_left::Longhands::collect_completion_keywords(f);\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod container {\n    use super::*;\n    pub use crate::properties::generated::shorthands::container::*;\n\n    use crate::values::specified::{ContainerName, ContainerType};\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        // See https://github.com/w3c/csswg-drafts/issues/7180 for why we don't match the spec.\n        let container_name = ContainerName::parse(context, input)?;\n        let container_type = if input.try_parse(|input| input.expect_delim('/')).is_ok() {\n            ContainerType::parse(context, input)?\n        } else {\n            ContainerType::NORMAL\n        };\n        Ok(expanded! {\n            container_name: container_name,\n            container_type: container_type,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.container_name.to_css(dest)?;\n            if !self.container_type.is_normal() {\n                dest.write_str(\" / \")?;\n                self.container_type.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod vertical_align {\n    use super::*;\n    pub use crate::properties::generated::shorthands::vertical_align::*;\n\n    use crate::values::specified::{AlignmentBaseline, BaselineShift, BaselineSource};\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut baseline_source = None;\n        let mut alignment_baseline = None;\n        let mut baseline_shift = None;\n        let mut parsed = 0;\n\n        loop {\n            parsed += 1;\n\n            try_parse_one!(input, baseline_source, BaselineSource::parse_non_auto);\n            try_parse_one!(input, alignment_baseline, AlignmentBaseline::parse);\n            try_parse_one!(context, input, baseline_shift, BaselineShift::parse);\n\n            parsed -= 1;\n            break;\n        }\n\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(expanded! {\n            baseline_source: baseline_source.unwrap_or(BaselineSource::Auto),\n            alignment_baseline: alignment_baseline.unwrap_or(AlignmentBaseline::Baseline),\n            baseline_shift: baseline_shift.unwrap_or(BaselineShift::zero()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if *self.baseline_source != BaselineSource::Auto {\n                writer.item(self.baseline_source)?;\n            }\n            if *self.alignment_baseline != AlignmentBaseline::Baseline {\n                writer.item(self.alignment_baseline)?;\n            }\n            if *self.baseline_shift != BaselineShift::zero() {\n                writer.item(self.baseline_shift)?;\n            }\n            if !writer.has_written() {\n                self.alignment_baseline.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod page_break_before {\n    use super::*;\n    pub use crate::properties::generated::shorthands::page_break_before::*;\n\n    use crate::values::specified::BreakBetween;\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        Ok(expanded! {\n            break_before: BreakBetween::parse_legacy(context, input)?,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.break_before.to_css_legacy(dest)\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod page_break_after {\n    pub use crate::properties::generated::shorthands::page_break_after::*;\n\n    use super::*;\n    use crate::values::specified::BreakBetween;\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        Ok(expanded! {\n            break_after: BreakBetween::parse_legacy(context, input)?,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.break_after.to_css_legacy(dest)\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod page_break_inside {\n    use super::*;\n    pub use crate::properties::generated::shorthands::page_break_inside::*;\n    use crate::values::specified::BreakWithin;\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        Ok(expanded! {\n            break_inside: BreakWithin::parse_legacy(context, input)?,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.break_inside.to_css_legacy(dest)\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod offset {\n    use super::*;\n    pub use crate::properties::generated::shorthands::offset::*;\n    use crate::values::specified::{\n        LengthPercentage, OffsetPath, OffsetPosition, OffsetRotate, PositionOrAuto,\n    };\n    use crate::Zero;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let offset_position = input.try_parse(|i| OffsetPosition::parse(context, i)).ok();\n        let offset_path = input.try_parse(|i| OffsetPath::parse(context, i)).ok();\n\n        // Must have one of [offset-position, offset-path].\n        // FIXME: The syntax is out-of-date after the update of the spec.\n        if offset_position.is_none() && offset_path.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let mut offset_distance = None;\n        let mut offset_rotate = None;\n        // offset-distance and offset-rotate are grouped with offset-path.\n        if offset_path.is_some() {\n            loop {\n                if offset_distance.is_none() {\n                    if let Ok(value) = input.try_parse(|i| LengthPercentage::parse(context, i)) {\n                        offset_distance = Some(value);\n                    }\n                }\n\n                if offset_rotate.is_none() {\n                    if let Ok(value) = input.try_parse(|i| OffsetRotate::parse(context, i)) {\n                        offset_rotate = Some(value);\n                        continue;\n                    }\n                }\n                break;\n            }\n        }\n\n        let offset_anchor = input\n            .try_parse(|i| {\n                i.expect_delim('/')?;\n                PositionOrAuto::parse(context, i)\n            })\n            .ok();\n\n        Ok(expanded! {\n            offset_position: offset_position.unwrap_or(OffsetPosition::normal()),\n            offset_path: offset_path.unwrap_or(OffsetPath::none()),\n            offset_distance: offset_distance.unwrap_or(LengthPercentage::zero()),\n            offset_rotate: offset_rotate.unwrap_or(OffsetRotate::auto()),\n            offset_anchor: offset_anchor.unwrap_or(PositionOrAuto::auto()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            // The basic concept is: we must serialize offset-position or offset-path group.\n            // offset-path group means \"offset-path offset-distance offset-rotate\".\n            let must_serialize_path = *self.offset_path != OffsetPath::None\n                || (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());\n            let position_is_default = matches!(self.offset_position, OffsetPosition::Normal);\n            if !position_is_default || !must_serialize_path {\n                self.offset_position.to_css(dest)?;\n            }\n\n            if must_serialize_path {\n                if !position_is_default {\n                    dest.write_char(' ')?;\n                }\n                self.offset_path.to_css(dest)?;\n            }\n\n            if !self.offset_distance.is_zero() {\n                dest.write_char(' ')?;\n                self.offset_distance.to_css(dest)?;\n            }\n\n            if !self.offset_rotate.is_auto() {\n                dest.write_char(' ')?;\n                self.offset_rotate.to_css(dest)?;\n            }\n\n            if *self.offset_anchor != PositionOrAuto::auto() {\n                dest.write_str(\" / \")?;\n                self.offset_anchor.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod _webkit_perspective {\n    pub use crate::properties::generated::shorthands::_webkit_perspective::*;\n\n    use super::*;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        use crate::properties::longhands::perspective;\n        use crate::values::generics::NonNegative;\n        use crate::values::specified::{AllowQuirks, Length, Perspective};\n\n        if let Ok(l) = input.try_parse(|input| {\n            Length::parse_non_negative_quirky(context, input, AllowQuirks::Always)\n        }) {\n            Ok(expanded! {\n                perspective: Perspective::Length(NonNegative(l)),\n            })\n        } else {\n            Ok(expanded! {\n                perspective: perspective::parse(context, input)?\n            })\n        }\n    }\n}\n\npub mod _webkit_transform {\n    pub use crate::properties::generated::shorthands::_webkit_transform::*;\n\n    use super::*;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        use crate::values::specified::Transform;\n        Ok(expanded! {\n            transform: Transform::parse_legacy(context, input)?,\n        })\n    }\n}\n\npub mod columns {\n    pub use crate::properties::generated::shorthands::columns::*;\n\n    use super::*;\n    use crate::properties::longhands::{column_count, column_width};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut column_count = None;\n        let mut column_width = None;\n        let mut autos = 0;\n\n        loop {\n            if input\n                .try_parse(|input| input.expect_ident_matching(\"auto\"))\n                .is_ok()\n            {\n                // Leave the options to None, 'auto' is the initial value.\n                autos += 1;\n                continue;\n            }\n\n            if column_count.is_none() {\n                if let Ok(value) = input.try_parse(|input| column_count::parse(context, input)) {\n                    column_count = Some(value);\n                    continue;\n                }\n            }\n\n            if column_width.is_none() {\n                if let Ok(value) = input.try_parse(|input| column_width::parse(context, input)) {\n                    column_width = Some(value);\n                    continue;\n                }\n            }\n\n            break;\n        }\n\n        let values = autos + column_count.iter().len() + column_width.iter().len();\n        if values == 0 || values > 2 {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        } else {\n            Ok(expanded! {\n                column_count: unwrap_or_initial!(column_count),\n                column_width: unwrap_or_initial!(column_width),\n            })\n        }\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if self.column_width.is_auto() {\n                return self.column_count.to_css(dest);\n            }\n            self.column_width.to_css(dest)?;\n            if !self.column_count.is_auto() {\n                dest.write_char(' ')?;\n                self.column_count.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod column_rule {\n    pub use crate::properties::generated::shorthands::column_rule::*;\n\n    use super::*;\n    use crate::properties::longhands::column_rule_color;\n    use crate::properties::longhands::{column_rule_style, column_rule_width};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut column_rule_width = None;\n        let mut column_rule_style = None;\n        let mut column_rule_color = None;\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, column_rule_width, column_rule_width::parse);\n            try_parse_one!(context, input, column_rule_style, column_rule_style::parse);\n            try_parse_one!(context, input, column_rule_color, column_rule_color::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            column_rule_width: unwrap_or_initial!(column_rule_width),\n            column_rule_style: unwrap_or_initial!(column_rule_style),\n            column_rule_color: unwrap_or_initial!(column_rule_color),\n        })\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod text_wrap {\n    pub use crate::properties::generated::shorthands::text_wrap::*;\n\n    use super::*;\n    use crate::properties::longhands::{text_wrap_mode, text_wrap_style};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut mode = None;\n        let mut style = None;\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, mode, text_wrap_mode::parse);\n            try_parse_one!(context, input, style, text_wrap_style::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            text_wrap_mode: unwrap_or_initial!(text_wrap_mode, mode),\n            text_wrap_style: unwrap_or_initial!(text_wrap_style, style),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use text_wrap_mode::computed_value::T as Mode;\n            use text_wrap_style::computed_value::T as Style;\n\n            if matches!(self.text_wrap_style, &Style::Auto) {\n                return self.text_wrap_mode.to_css(dest);\n            }\n\n            if *self.text_wrap_mode != Mode::Wrap {\n                self.text_wrap_mode.to_css(dest)?;\n                dest.write_char(' ')?;\n            }\n\n            self.text_wrap_style.to_css(dest)\n        }\n    }\n}\n\npub mod white_space {\n    pub use crate::properties::generated::shorthands::white_space::*;\n\n    use super::*;\n    use crate::properties::longhands::{text_wrap_mode, white_space_collapse};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        use text_wrap_mode::computed_value::T as Wrap;\n        use white_space_collapse::computed_value::T as Collapse;\n\n        fn parse_special_shorthands<'i, 't>(\n            input: &mut Parser<'i, 't>,\n        ) -> Result<Longhands, ParseError<'i>> {\n            let (mode, collapse) = try_match_ident_ignore_ascii_case! { input,\n                \"normal\" => (Wrap::Wrap, Collapse::Collapse),\n                \"pre\" => (Wrap::Nowrap, Collapse::Preserve),\n                \"pre-wrap\" => (Wrap::Wrap, Collapse::Preserve),\n                \"pre-line\" => (Wrap::Wrap, Collapse::PreserveBreaks),\n            };\n            Ok(expanded! {\n                text_wrap_mode: mode,\n                white_space_collapse: collapse,\n            })\n        }\n\n        if let Ok(result) = input.try_parse(parse_special_shorthands) {\n            return Ok(result);\n        }\n\n        let mut wrap = None;\n        let mut collapse = None;\n        let mut parsed = 0;\n\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, wrap, text_wrap_mode::parse);\n            try_parse_one!(context, input, collapse, white_space_collapse::parse);\n            parsed -= 1;\n            break;\n        }\n\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(expanded! {\n            text_wrap_mode: unwrap_or_initial!(text_wrap_mode, wrap),\n            white_space_collapse: unwrap_or_initial!(white_space_collapse, collapse),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use text_wrap_mode::computed_value::T as Wrap;\n            use white_space_collapse::computed_value::T as Collapse;\n\n            match *self.text_wrap_mode {\n                Wrap::Wrap => match *self.white_space_collapse {\n                    Collapse::Collapse => return dest.write_str(\"normal\"),\n                    Collapse::Preserve => return dest.write_str(\"pre-wrap\"),\n                    Collapse::PreserveBreaks => return dest.write_str(\"pre-line\"),\n                    _ => (),\n                },\n                Wrap::Nowrap => {\n                    if let Collapse::Preserve = *self.white_space_collapse {\n                        return dest.write_str(\"pre\");\n                    }\n                },\n            }\n\n            let mut has_value = false;\n            if *self.white_space_collapse != Collapse::Collapse {\n                self.white_space_collapse.to_css(dest)?;\n                has_value = true;\n            }\n\n            if *self.text_wrap_mode != Wrap::Wrap {\n                if has_value {\n                    dest.write_char(' ')?;\n                }\n                self.text_wrap_mode.to_css(dest)?;\n            }\n\n            Ok(())\n        }\n    }\n\n    impl SpecifiedValueInfo for Longhands {\n        fn collect_completion_keywords(f: KeywordsCollectFn) {\n            // Collect keywords from our longhands.\n            text_wrap_mode::SpecifiedValue::collect_completion_keywords(f);\n            white_space_collapse::SpecifiedValue::collect_completion_keywords(f);\n\n            // Add the special values supported only by the shorthand\n            // (see parse_special_shorthands() above).\n            f(&[\"normal\", \"pre\", \"pre-wrap\", \"pre-line\"])\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod _webkit_text_stroke {\n    pub use crate::properties::generated::shorthands::_webkit_text_stroke::*;\n\n    use super::*;\n    use crate::properties::longhands::{_webkit_text_stroke_color, _webkit_text_stroke_width};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut color = None;\n        let mut width = None;\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, color, _webkit_text_stroke_color::parse);\n            try_parse_one!(context, input, width, _webkit_text_stroke_width::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            _webkit_text_stroke_color: unwrap_or_initial!(_webkit_text_stroke_color, color),\n            _webkit_text_stroke_width: unwrap_or_initial!(_webkit_text_stroke_width, width),\n        })\n    }\n}\n\npub mod list_style {\n    pub use crate::properties::generated::shorthands::list_style::*;\n\n    use super::*;\n    use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};\n    use crate::values::specified::Image;\n    use selectors::parser::SelectorParseErrorKind;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        // `none` is ambiguous until we've finished parsing the shorthands, so we count the number\n        // of times we see it.\n        let mut nones = 0u8;\n        let (mut image, mut position, mut list_style_type) = (None, None, None);\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n\n            if input\n                .try_parse(|input| input.expect_ident_matching(\"none\"))\n                .is_ok()\n            {\n                nones += 1;\n                if nones > 2 {\n                    return Err(input\n                        .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(\"none\".into())));\n                }\n                continue;\n            }\n\n            try_parse_one!(context, input, image, list_style_image::parse);\n            try_parse_one!(context, input, position, list_style_position::parse);\n            // list-style-type must be checked the last, because it accepts\n            // arbitrary identifier for custom counter style, and thus may\n            // affect values of list-style-position.\n            try_parse_one!(context, input, list_style_type, list_style_type::parse);\n\n            parsed -= 1;\n            break;\n        }\n\n        let position = unwrap_or_initial!(list_style_position, position);\n\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // If there are two `none`s, then we can't have a type or image; if there is one `none`,\n        // then we can't have both a type *and* an image; if there is no `none` then we're fine as\n        // long as we parsed something.\n        use self::list_style_type::SpecifiedValue as ListStyleType;\n        match (nones, list_style_type, image) {\n            (2, None, None) => Ok(expanded! {\n                list_style_position: position,\n                list_style_image: Image::None,\n                list_style_type: ListStyleType::none(),\n            }),\n            (1, None, Some(image)) => Ok(expanded! {\n                list_style_position: position,\n                list_style_image: image,\n                list_style_type: ListStyleType::none(),\n            }),\n            (1, Some(list_style_type), None) => Ok(expanded! {\n                list_style_position: position,\n                list_style_image: Image::None,\n                list_style_type: list_style_type,\n            }),\n            (1, None, None) => Ok(expanded! {\n                list_style_position: position,\n                list_style_image: Image::None,\n                list_style_type: ListStyleType::none(),\n            }),\n            (0, list_style_type, image) => Ok(expanded! {\n                list_style_position: position,\n                list_style_image: unwrap_or_initial!(list_style_image, image),\n                list_style_type: unwrap_or_initial!(list_style_type),\n            }),\n            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use list_style_image::SpecifiedValue as ListStyleImage;\n            use list_style_position::SpecifiedValue as ListStylePosition;\n            use list_style_type::SpecifiedValue as ListStyleType;\n\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if *self.list_style_position != ListStylePosition::Outside {\n                writer.item(self.list_style_position)?;\n            }\n            if *self.list_style_image != ListStyleImage::None {\n                writer.item(self.list_style_image)?;\n            }\n            if *self.list_style_type != ListStyleType::disc() {\n                writer.item(self.list_style_type)?;\n            }\n            if !writer.has_written() {\n                self.list_style_position.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod gap {\n    pub use crate::properties::generated::shorthands::gap::*;\n\n    use super::*;\n    use crate::properties::longhands::{column_gap, row_gap};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let r_gap = row_gap::parse(context, input)?;\n        let c_gap = input\n            .try_parse(|input| column_gap::parse(context, input))\n            .unwrap_or(r_gap.clone());\n\n        Ok(expanded! {\n            row_gap: r_gap,\n            column_gap: c_gap,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.row_gap.to_css(dest)?;\n            if self.row_gap != self.column_gap {\n                dest.write_char(' ')?;\n                self.column_gap.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod marker {\n    pub use crate::properties::generated::shorthands::marker::*;\n\n    use super::*;\n    use crate::values::specified::url::UrlOrNone;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let url = UrlOrNone::parse(context, input)?;\n\n        Ok(expanded! {\n            marker_start: url.clone(),\n            marker_mid: url.clone(),\n            marker_end: url,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if self.marker_start == self.marker_mid && self.marker_mid == self.marker_end {\n                self.marker_start.to_css(dest)\n            } else {\n                Ok(())\n            }\n        }\n    }\n}\n\npub mod flex_flow {\n    pub use crate::properties::generated::shorthands::flex_flow::*;\n\n    use super::*;\n    use crate::properties::longhands::{flex_direction, flex_wrap};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut parsed = 0;\n        let mut direction = None;\n        let mut wrap = None;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, direction, flex_direction::parse);\n            try_parse_one!(context, input, wrap, flex_wrap::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            flex_direction: unwrap_or_initial!(flex_direction, direction),\n            flex_wrap: unwrap_or_initial!(flex_wrap, wrap),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if *self.flex_direction == flex_direction::get_initial_specified_value()\n                && *self.flex_wrap != flex_wrap::get_initial_specified_value()\n            {\n                return self.flex_wrap.to_css(dest);\n            }\n            self.flex_direction.to_css(dest)?;\n            if *self.flex_wrap != flex_wrap::get_initial_specified_value() {\n                dest.write_char(' ')?;\n                self.flex_wrap.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod flex {\n    pub use crate::properties::generated::shorthands::flex::*;\n\n    use super::*;\n    use crate::properties::longhands::flex_basis::SpecifiedValue as FlexBasis;\n    use crate::values::specified::NonNegativeNumber;\n\n    fn parse_flexibility<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(NonNegativeNumber, Option<NonNegativeNumber>), ParseError<'i>> {\n        let grow = NonNegativeNumber::parse(context, input)?;\n        let shrink = input\n            .try_parse(|i| NonNegativeNumber::parse(context, i))\n            .ok();\n        Ok((grow, shrink))\n    }\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut grow = None;\n        let mut shrink = None;\n        let mut basis = None;\n\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(expanded! {\n                flex_grow: NonNegativeNumber::new(0.0),\n                flex_shrink: NonNegativeNumber::new(0.0),\n                flex_basis: FlexBasis::auto(),\n            });\n        }\n        loop {\n            if grow.is_none() {\n                if let Ok((flex_grow, flex_shrink)) =\n                    input.try_parse(|i| parse_flexibility(context, i))\n                {\n                    grow = Some(flex_grow);\n                    shrink = flex_shrink;\n                    continue;\n                }\n            }\n            if basis.is_none() {\n                if let Ok(value) = input.try_parse(|input| FlexBasis::parse(context, input)) {\n                    basis = Some(value);\n                    continue;\n                }\n            }\n            break;\n        }\n\n        if grow.is_none() && basis.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            flex_grow: grow.unwrap_or(NonNegativeNumber::new(1.0)),\n            flex_shrink: shrink.unwrap_or(NonNegativeNumber::new(1.0)),\n            flex_basis: basis.unwrap_or(FlexBasis::zero_percent()),\n        })\n    }\n}\n\npub mod place_content {\n    pub use crate::properties::generated::shorthands::place_content::*;\n\n    use super::*;\n    use crate::values::specified::align::ContentDistribution;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let align_content = ContentDistribution::parse_block(context, input)?;\n        let justify_content =\n            input.try_parse(|input| ContentDistribution::parse_inline(context, input));\n\n        let justify_content = match justify_content {\n            Ok(v) => v,\n            Err(..) => {\n                if !align_content.is_baseline_position() {\n                    align_content\n                } else {\n                    ContentDistribution::start()\n                }\n            },\n        };\n\n        Ok(expanded! {\n            align_content: align_content,\n            justify_content: justify_content,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.align_content.to_css(dest)?;\n            if self.align_content != self.justify_content {\n                dest.write_char(' ')?;\n                self.justify_content.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod place_self {\n    pub use crate::properties::generated::shorthands::place_self::*;\n\n    use super::*;\n    use crate::values::specified::align::SelfAlignment;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let align = SelfAlignment::parse_block(context, input)?;\n        let justify = input.try_parse(|input| SelfAlignment::parse_inline(context, input));\n\n        let justify = match justify {\n            Ok(v) => v,\n            Err(..) => {\n                debug_assert!(align.is_valid_on_both_axes());\n                align\n            },\n        };\n\n        Ok(expanded! {\n            align_self: align,\n            justify_self: justify,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.align_self.to_css(dest)?;\n            if self.align_self != self.justify_self {\n                dest.write_char(' ')?;\n                self.justify_self.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod place_items {\n    pub use crate::properties::generated::shorthands::place_items::*;\n\n    use super::*;\n    use crate::values::specified::align::{ItemPlacement, JustifyItems};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let align = ItemPlacement::parse_block(context, input)?;\n        let justify = input\n            .try_parse(|input| ItemPlacement::parse_inline(context, input))\n            .unwrap_or_else(|_| align.clone());\n\n        Ok(expanded! {\n            align_items: align,\n            justify_items: JustifyItems(justify),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.align_items.to_css(dest)?;\n            if self.align_items != &self.justify_items.0 {\n                dest.write_char(' ')?;\n                self.justify_items.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod grid_row {\n    pub use crate::properties::generated::shorthands::grid_row::*;\n\n    use super::*;\n    use crate::values::specified::GridLine;\n    use crate::Zero;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let start = input.try_parse(|i| GridLine::parse(context, i))?;\n        let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n            GridLine::parse(context, input)?\n        } else {\n            let mut line = GridLine::auto();\n            if start.line_num.is_zero() && !start.is_span {\n                line.ident = start.ident.clone();\n            }\n\n            line\n        };\n\n        Ok(expanded! {\n            grid_row_start: start,\n            grid_row_end: end,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.grid_row_start.to_css(dest)?;\n            if self.grid_row_start.can_omit(self.grid_row_end) {\n                return Ok(());\n            }\n            dest.write_str(\" / \")?;\n            self.grid_row_end.to_css(dest)\n        }\n    }\n}\n\npub mod grid_column {\n    pub use crate::properties::generated::shorthands::grid_column::*;\n\n    use super::*;\n    use crate::values::specified::GridLine;\n    use crate::Zero;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let start = input.try_parse(|i| GridLine::parse(context, i))?;\n        let end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n            GridLine::parse(context, input)?\n        } else {\n            let mut line = GridLine::auto();\n            if start.line_num.is_zero() && !start.is_span {\n                line.ident = start.ident.clone();\n            }\n\n            line\n        };\n\n        Ok(expanded! {\n            grid_column_start: start,\n            grid_column_end: end,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.grid_column_start.to_css(dest)?;\n            if self.grid_column_start.can_omit(self.grid_column_end) {\n                return Ok(());\n            }\n            dest.write_str(\" / \")?;\n            self.grid_column_end.to_css(dest)\n        }\n    }\n}\n\npub mod grid_area {\n    pub use crate::properties::generated::shorthands::grid_area::*;\n\n    use super::*;\n    use crate::values::specified::GridLine;\n    use crate::Zero;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        fn line_with_ident_from(other: &GridLine) -> GridLine {\n            let mut this = GridLine::auto();\n            if other.line_num.is_zero() && !other.is_span {\n                this.ident = other.ident.clone();\n            }\n\n            this\n        }\n\n        let row_start = input.try_parse(|i| GridLine::parse(context, i))?;\n        let (column_start, row_end, column_end) =\n            if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n                let column_start = GridLine::parse(context, input)?;\n                let (row_end, column_end) = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n                    let row_end = GridLine::parse(context, input)?;\n                    let column_end = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n                        GridLine::parse(context, input)?\n                    } else {\n                        line_with_ident_from(&column_start)\n                    };\n\n                    (row_end, column_end)\n                } else {\n                    let row_end = line_with_ident_from(&row_start);\n                    let column_end = line_with_ident_from(&column_start);\n                    (row_end, column_end)\n                };\n\n                (column_start, row_end, column_end)\n            } else {\n                let line = line_with_ident_from(&row_start);\n                (line.clone(), line.clone(), line)\n            };\n\n        Ok(expanded! {\n            grid_row_start: row_start,\n            grid_row_end: row_end,\n            grid_column_start: column_start,\n            grid_column_end: column_end,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            self.grid_row_start.to_css(dest)?;\n            let mut trailing_values = 3;\n            if self.grid_column_start.can_omit(self.grid_column_end) {\n                trailing_values -= 1;\n                if self.grid_row_start.can_omit(self.grid_row_end) {\n                    trailing_values -= 1;\n                    if self.grid_row_start.can_omit(self.grid_column_start) {\n                        trailing_values -= 1;\n                    }\n                }\n            }\n            let values = [\n                &self.grid_column_start,\n                &self.grid_row_end,\n                &self.grid_column_end,\n            ];\n            for value in values.iter().take(trailing_values) {\n                dest.write_str(\" / \")?;\n                value.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod position_try {\n    pub use crate::properties::generated::shorthands::position_try::*;\n\n    use super::*;\n    use crate::values::specified::position::{PositionTryFallbacks, PositionTryOrder};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let order =\n            if static_prefs::pref!(\"layout.css.anchor-positioning.position-try-order.enabled\") {\n                input.try_parse(PositionTryOrder::parse).ok()\n            } else {\n                None\n            };\n        let fallbacks = PositionTryFallbacks::parse(context, input)?;\n        Ok(expanded! {\n            position_try_order: order.unwrap_or(PositionTryOrder::normal()),\n            position_try_fallbacks: fallbacks,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if let Some(o) = self.position_try_order {\n                if *o != PositionTryOrder::Normal {\n                    o.to_css(dest)?;\n                    dest.write_char(' ')?;\n                }\n            }\n            self.position_try_fallbacks.to_css(dest)\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nfn timeline_to_css<W>(\n    name: &[specified::TimelineName],\n    axes: &[specified::ScrollAxis],\n    dest: &mut CssWriter<W>,\n) -> fmt::Result\nwhere\n    W: fmt::Write,\n{\n    if name.len() != axes.len() {\n        return Ok(());\n    }\n    for (i, (name, axis)) in std::iter::zip(name.iter(), axes.iter()).enumerate() {\n        if i != 0 {\n            dest.write_str(\", \")?;\n        }\n        name.to_css(dest)?;\n        if !axis.is_default() {\n            dest.write_char(' ')?;\n            axis.to_css(dest)?;\n        }\n    }\n    Ok(())\n}\n\n#[cfg(feature = \"gecko\")]\npub mod scroll_timeline {\n    pub use crate::properties::generated::shorthands::scroll_timeline::*;\n\n    use super::*;\n    use crate::properties::longhands::{scroll_timeline_axis, scroll_timeline_name};\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut names = Vec::with_capacity(1);\n        let mut axes = Vec::with_capacity(1);\n        input.parse_comma_separated(|input| {\n            let name = scroll_timeline_name::single_value::parse(context, input)?;\n            let axis = input.try_parse(|i| scroll_timeline_axis::single_value::parse(context, i));\n\n            names.push(name);\n            axes.push(axis.unwrap_or_default());\n\n            Ok(())\n        })?;\n\n        Ok(expanded! {\n            scroll_timeline_name: scroll_timeline_name::SpecifiedValue(names.into()),\n            scroll_timeline_axis: scroll_timeline_axis::SpecifiedValue(axes.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            super::timeline_to_css(\n                &self.scroll_timeline_name.0,\n                &self.scroll_timeline_axis.0,\n                dest,\n            )\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod view_timeline {\n    pub use crate::properties::generated::shorthands::view_timeline::*;\n\n    use super::*;\n    use crate::properties::longhands::{view_timeline_axis, view_timeline_name};\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut names = Vec::with_capacity(1);\n        let mut axes = Vec::with_capacity(1);\n        input.parse_comma_separated(|input| {\n            let name = view_timeline_name::single_value::parse(context, input)?;\n            let axis = input.try_parse(|i| view_timeline_axis::single_value::parse(context, i));\n\n            names.push(name);\n            axes.push(axis.unwrap_or_default());\n\n            Ok(())\n        })?;\n\n        Ok(expanded! {\n            view_timeline_name: view_timeline_name::SpecifiedValue(names.into()),\n            view_timeline_axis: view_timeline_axis::SpecifiedValue(axes.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            super::timeline_to_css(&self.view_timeline_name.0, &self.view_timeline_axis.0, dest)\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod animation_range {\n    pub use crate::properties::generated::shorthands::animation_range::*;\n\n    use super::*;\n    use crate::properties::longhands::{animation_range_end, animation_range_start};\n    use crate::values::specified::LengthPercentage;\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut starts = Vec::with_capacity(1);\n        let mut ends = Vec::with_capacity(1);\n        input.parse_comma_separated(|input| {\n            let start = animation_range_start::single_value::parse(context, input)?;\n            let end = input\n                .try_parse(|i| animation_range_end::single_value::parse(context, i))\n                .unwrap_or_else(|_| {\n                    use crate::values::generics::animation::AnimationRangeEnd;\n                    use crate::values::specified::animation::{\n                        AnimationRangeValue, TimelineRangeName,\n                    };\n\n                    // If `<animation-range-start>` includes a timeline range name,\n                    // `animation-range-end` is set to that same timeline range name and 100%.\n                    // Otherwise, any omitted longhand is set to its initial value.\n                    let name = if start.0.name.is_none() {\n                        TimelineRangeName::Normal\n                    } else {\n                        start.0.name\n                    };\n                    AnimationRangeEnd(AnimationRangeValue::new(\n                        name,\n                        LengthPercentage::hundred_percent(),\n                    ))\n                });\n\n            starts.push(start);\n            ends.push(end);\n            Ok(())\n        })?;\n\n        Ok(expanded! {\n            animation_range_start: animation_range_start::SpecifiedValue(starts.into()),\n            animation_range_end: animation_range_end::SpecifiedValue(ends.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::values::specified::length::EqualsPercentage;\n            let starts = &self.animation_range_start.0;\n            let ends = &self.animation_range_end.0;\n            if starts.len() != ends.len() {\n                return Ok(());\n            }\n\n            for (i, (start, end)) in std::iter::zip(starts.iter(), ends.iter()).enumerate() {\n                if i != 0 {\n                    dest.write_str(\", \")?;\n                }\n                start.to_css(dest)?;\n                let can_omit_end = (start.0.name == end.0.name && end.0.lp.equals_percentage(1.0))\n                    || (start.0.name.is_none() && end.0.name.is_normal());\n                if !can_omit_end {\n                    dest.write_char(' ')?;\n                    end.to_css(dest)?;\n                }\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod transition {\n    pub use crate::properties::generated::shorthands::transition::*;\n\n    use super::*;\n    use crate::properties::longhands::{\n        transition_behavior, transition_delay, transition_duration, transition_property,\n        transition_timing_function,\n    };\n    use crate::values::specified::TransitionProperty;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        struct SingleTransition {\n            transition_property: transition_property::SingleSpecifiedValue,\n            transition_duration: transition_duration::SingleSpecifiedValue,\n            transition_timing_function: transition_timing_function::SingleSpecifiedValue,\n            transition_delay: transition_delay::SingleSpecifiedValue,\n            transition_behavior: transition_behavior::SingleSpecifiedValue,\n        }\n\n        fn parse_one_transition<'i, 't>(\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n            first: bool,\n        ) -> Result<SingleTransition, ParseError<'i>> {\n            let mut property = None;\n            let mut duration = None;\n            let mut timing_function = None;\n            let mut delay = None;\n            let mut behavior = None;\n\n            let mut parsed = 0;\n            loop {\n                parsed += 1;\n\n                try_parse_one!(\n                    context,\n                    input,\n                    duration,\n                    transition_duration::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    timing_function,\n                    transition_timing_function::single_value::parse\n                );\n                try_parse_one!(context, input, delay, transition_delay::single_value::parse);\n                try_parse_one!(\n                    context,\n                    input,\n                    behavior,\n                    transition_behavior::single_value::parse\n                );\n                if property.is_none() {\n                    if let Ok(value) = input.try_parse(|i| TransitionProperty::parse(context, i)) {\n                        property = Some(value);\n                        continue;\n                    }\n\n                    if first && input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n                        property = Some(TransitionProperty::none());\n                        continue;\n                    }\n                }\n\n                parsed -= 1;\n                break;\n            }\n\n            if parsed != 0 {\n                Ok(SingleTransition {\n                    transition_property: property.unwrap_or_else(\n                        transition_property::single_value::get_initial_specified_value,\n                    ),\n                    transition_duration: duration.unwrap_or_else(\n                        transition_duration::single_value::get_initial_specified_value,\n                    ),\n                    transition_timing_function: timing_function.unwrap_or_else(\n                        transition_timing_function::single_value::get_initial_specified_value,\n                    ),\n                    transition_delay: delay.unwrap_or_else(\n                        transition_delay::single_value::get_initial_specified_value,\n                    ),\n                    transition_behavior: behavior.unwrap_or_else(\n                        transition_behavior::single_value::get_initial_specified_value,\n                    ),\n                })\n            } else {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            }\n        }\n\n        let mut first = true;\n        let mut has_transition_property_none = false;\n        let results = input.parse_comma_separated(|i| {\n            if has_transition_property_none {\n                return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n            let transition = parse_one_transition(context, i, first)?;\n            first = false;\n            has_transition_property_none = transition.transition_property.is_none();\n            Ok(transition)\n        })?;\n\n        let len = results.len();\n        let mut property = Vec::with_capacity(len);\n        let mut duration = Vec::with_capacity(len);\n        let mut timing_function = Vec::with_capacity(len);\n        let mut delay = Vec::with_capacity(len);\n        let mut behavior = Vec::with_capacity(len);\n        for result in results {\n            property.push(result.transition_property);\n            duration.push(result.transition_duration);\n            timing_function.push(result.transition_timing_function);\n            delay.push(result.transition_delay);\n            behavior.push(result.transition_behavior);\n        }\n\n        Ok(Longhands {\n            transition_property: transition_property::SpecifiedValue(property.into()),\n            transition_duration: transition_duration::SpecifiedValue(duration.into()),\n            transition_timing_function: transition_timing_function::SpecifiedValue(\n                timing_function.into(),\n            ),\n            transition_delay: transition_delay::SpecifiedValue(delay.into()),\n            transition_behavior: transition_behavior::SpecifiedValue(behavior.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::Zero;\n            use style_traits::values::SequenceWriter;\n\n            let len = self.transition_property.0.len();\n            debug_assert_ne!(\n                len, 0,\n                \"We should always have at least one transition-property, even if none\"\n            );\n            if self.transition_duration.0.len() != len {\n                return Ok(());\n            }\n            if self.transition_delay.0.len() != len {\n                return Ok(());\n            }\n            if self.transition_timing_function.0.len() != len {\n                return Ok(());\n            }\n            if self.transition_behavior.0.len() != len {\n                return Ok(());\n            }\n            for i in 0..len {\n                if i != 0 {\n                    dest.write_str(\", \")?;\n                }\n\n                let has_duration = !self.transition_duration.0[i].is_zero();\n                let has_timing = !self.transition_timing_function.0[i].is_ease();\n                let has_delay = !self.transition_delay.0[i].is_zero();\n                let has_behavior = !self.transition_behavior.0[i].is_normal();\n                let has_any = has_duration || has_timing || has_delay || has_behavior;\n\n                let mut writer = SequenceWriter::new(dest, \" \");\n                if !self.transition_property.0[i].is_all() || !has_any {\n                    writer.item(&self.transition_property.0[i])?;\n                }\n                if has_duration || has_delay {\n                    writer.item(&self.transition_duration.0[i])?;\n                }\n                if has_timing {\n                    writer.item(&self.transition_timing_function.0[i])?;\n                }\n                if has_delay {\n                    writer.item(&self.transition_delay.0[i])?;\n                }\n                if has_behavior {\n                    writer.item(&self.transition_behavior.0[i])?;\n                }\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod outline {\n    pub use crate::properties::generated::shorthands::outline::*;\n\n    use super::*;\n    use crate::properties::longhands::{outline_color, outline_style, outline_width};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let _unused = context;\n        let mut color = None;\n        let mut style = None;\n        let mut width = None;\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, color, specified::Color::parse);\n            try_parse_one!(context, input, style, outline_style::parse);\n            try_parse_one!(context, input, width, outline_width::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            outline_color: unwrap_or_initial!(outline_color, color),\n            outline_style: unwrap_or_initial!(outline_style, style),\n            outline_width: unwrap_or_initial!(outline_width, width),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if *self.outline_color != outline_color::get_initial_specified_value() {\n                writer.item(self.outline_color)?;\n            }\n            if *self.outline_style != outline_style::get_initial_specified_value() {\n                writer.item(self.outline_style)?;\n            }\n            if *self.outline_width != outline_width::get_initial_specified_value() {\n                writer.item(self.outline_width)?;\n            }\n            if !writer.has_written() {\n                self.outline_style.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod background_position {\n    pub use crate::properties::generated::shorthands::background_position::*;\n\n    use super::*;\n    use crate::properties::longhands::{background_position_x, background_position_y};\n    use crate::values::specified::position::Position;\n    use crate::values::specified::AllowQuirks;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut position_x = Vec::with_capacity(1);\n        let mut position_y = Vec::with_capacity(1);\n        let mut any = false;\n\n        input.parse_comma_separated(|input| {\n            let value = Position::parse_three_value_quirky(context, input, AllowQuirks::Yes)?;\n            position_x.push(value.horizontal);\n            position_y.push(value.vertical);\n            any = true;\n            Ok(())\n        })?;\n        if !any {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(expanded! {\n            background_position_x: background_position_x::SpecifiedValue(position_x.into()),\n            background_position_y: background_position_y::SpecifiedValue(position_y.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let len = self.background_position_x.0.len();\n            if len == 0 || len != self.background_position_y.0.len() {\n                return Ok(());\n            }\n            for i in 0..len {\n                Position {\n                    horizontal: self.background_position_x.0[i].clone(),\n                    vertical: self.background_position_y.0[i].clone(),\n                }\n                .to_css(dest)?;\n\n                if i < len - 1 {\n                    dest.write_str(\", \")?;\n                }\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod background {\n    pub use crate::properties::generated::shorthands::background::*;\n\n    use super::*;\n    use crate::properties::longhands::background_clip;\n    use crate::properties::longhands::background_clip::single_value::computed_value::T as Clip;\n    use crate::properties::longhands::background_origin::single_value::computed_value::T as Origin;\n    use crate::properties::longhands::{\n        background_attachment, background_color, background_image, background_origin,\n        background_size,\n    };\n    use crate::properties::longhands::{\n        background_position_x, background_position_y, background_repeat,\n    };\n    use crate::values::specified::{AllowQuirks, Color, Position, PositionComponent};\n\n    impl From<background_origin::single_value::SpecifiedValue>\n        for background_clip::single_value::SpecifiedValue\n    {\n        fn from(\n            origin: background_origin::single_value::SpecifiedValue,\n        ) -> background_clip::single_value::SpecifiedValue {\n            match origin {\n                background_origin::single_value::SpecifiedValue::ContentBox => {\n                    background_clip::single_value::SpecifiedValue::ContentBox\n                },\n                background_origin::single_value::SpecifiedValue::PaddingBox => {\n                    background_clip::single_value::SpecifiedValue::PaddingBox\n                },\n                background_origin::single_value::SpecifiedValue::BorderBox => {\n                    background_clip::single_value::SpecifiedValue::BorderBox\n                },\n            }\n        }\n    }\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut background_color = None;\n\n        let mut background_image = Vec::with_capacity(1);\n        let mut background_position_x = Vec::with_capacity(1);\n        let mut background_position_y = Vec::with_capacity(1);\n        let mut background_repeat = Vec::with_capacity(1);\n        let mut background_size = Vec::with_capacity(1);\n        let mut background_attachment = Vec::with_capacity(1);\n        let mut background_origin = Vec::with_capacity(1);\n        let mut background_clip = Vec::with_capacity(1);\n        input.parse_comma_separated(|input| {\n            if background_color.is_some() {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            let mut image = None;\n            let mut position = None;\n            let mut repeat = None;\n            let mut size = None;\n            let mut attachment = None;\n            let mut origin = None;\n            let mut clip = None;\n            let mut parsed = 0;\n            loop {\n                parsed += 1;\n                try_parse_one!(context, input, background_color, Color::parse);\n                if position.is_none() {\n                    if let Ok(value) = input.try_parse(|input| {\n                        Position::parse_three_value_quirky(context, input, AllowQuirks::No)\n                    }) {\n                        position = Some(value);\n\n                        size = input\n                            .try_parse(|input| {\n                                input.expect_delim('/')?;\n                                background_size::single_value::parse(context, input)\n                            })\n                            .ok();\n\n                        continue;\n                    }\n                }\n                try_parse_one!(context, input, image, background_image::single_value::parse);\n                try_parse_one!(\n                    context,\n                    input,\n                    repeat,\n                    background_repeat::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    attachment,\n                    background_attachment::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    origin,\n                    background_origin::single_value::parse\n                );\n                try_parse_one!(context, input, clip, background_clip::single_value::parse);\n                parsed -= 1;\n                break;\n            }\n            if parsed == 0 {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n            if clip.is_none() {\n                if let Some(origin) = origin {\n                    clip = Some(background_clip::single_value::SpecifiedValue::from(origin));\n                }\n            }\n            if let Some(position) = position {\n                background_position_x.push(position.horizontal);\n                background_position_y.push(position.vertical);\n            } else {\n                background_position_x.push(PositionComponent::zero());\n                background_position_y.push(PositionComponent::zero());\n            }\n            if let Some(bg_image) = image {\n                background_image.push(bg_image);\n            } else {\n                background_image\n                    .push(background_image::single_value::get_initial_specified_value());\n            }\n            if let Some(bg_repeat) = repeat {\n                background_repeat.push(bg_repeat);\n            } else {\n                background_repeat\n                    .push(background_repeat::single_value::get_initial_specified_value());\n            }\n            if let Some(bg_size) = size {\n                background_size.push(bg_size);\n            } else {\n                background_size.push(background_size::single_value::get_initial_specified_value());\n            }\n            if let Some(bg_attachment) = attachment {\n                background_attachment.push(bg_attachment);\n            } else {\n                background_attachment\n                    .push(background_attachment::single_value::get_initial_specified_value());\n            }\n            if let Some(bg_origin) = origin {\n                background_origin.push(bg_origin);\n            } else {\n                background_origin\n                    .push(background_origin::single_value::get_initial_specified_value());\n            }\n            if let Some(bg_clip) = clip {\n                background_clip.push(bg_clip);\n            } else {\n                background_clip.push(background_clip::single_value::get_initial_specified_value());\n            }\n            Ok(())\n        })?;\n\n        Ok(expanded! {\n            background_color: background_color.unwrap_or(Color::transparent()),\n            background_image: background_image::SpecifiedValue(background_image.into()),\n            background_position_x: background_position_x::SpecifiedValue(background_position_x.into()),\n            background_position_y: background_position_y::SpecifiedValue(background_position_y.into()),\n            background_repeat: background_repeat::SpecifiedValue(background_repeat.into()),\n            background_size: background_size::SpecifiedValue(background_size.into()),\n            background_attachment: background_attachment::SpecifiedValue(background_attachment.into()),\n            background_origin: background_origin::SpecifiedValue(background_origin.into()),\n            background_clip: background_clip::SpecifiedValue(background_clip.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let len = self.background_image.0.len();\n            if len == 0 {\n                return Ok(());\n            }\n            if len != self.background_image.0.len() {\n                return Ok(());\n            }\n            if len != self.background_position_x.0.len() {\n                return Ok(());\n            }\n            if len != self.background_position_y.0.len() {\n                return Ok(());\n            }\n            if len != self.background_size.0.len() {\n                return Ok(());\n            }\n            if len != self.background_repeat.0.len() {\n                return Ok(());\n            }\n            if len != self.background_origin.0.len() {\n                return Ok(());\n            }\n            if len != self.background_clip.0.len() {\n                return Ok(());\n            }\n            if len != self.background_attachment.0.len() {\n                return Ok(());\n            }\n\n            for i in 0..len {\n                let image = &self.background_image.0[i];\n                let position_x = &self.background_position_x.0[i];\n                let position_y = &self.background_position_y.0[i];\n                let repeat = &self.background_repeat.0[i];\n                let size = &self.background_size.0[i];\n                let attachment = &self.background_attachment.0[i];\n                let origin = &self.background_origin.0[i];\n                let clip = &self.background_clip.0[i];\n\n                if i != 0 {\n                    dest.write_str(\", \")?;\n                }\n\n                let mut writer = SequenceWriter::new(dest, \" \");\n                if *image != background_image::single_value::get_initial_specified_value() {\n                    writer.item(image)?;\n                }\n\n                if *position_x != PositionComponent::zero()\n                    || *position_y != PositionComponent::zero()\n                    || *size != background_size::single_value::get_initial_specified_value()\n                {\n                    writer.write_item(|dest| {\n                        Position {\n                            horizontal: position_x.clone(),\n                            vertical: position_y.clone(),\n                        }\n                        .to_css(dest)?;\n                        if *size != background_size::single_value::get_initial_specified_value() {\n                            dest.write_str(\" / \")?;\n                            size.to_css(dest)?;\n                        }\n                        Ok(())\n                    })?;\n                }\n                if *repeat != background_repeat::single_value::get_initial_specified_value() {\n                    writer.item(repeat)?;\n                }\n                if *attachment != background_attachment::single_value::get_initial_specified_value()\n                {\n                    writer.item(attachment)?;\n                }\n\n                if *origin != Origin::PaddingBox || *clip != Clip::BorderBox {\n                    writer.item(origin)?;\n                    if *clip != From::from(*origin) {\n                        writer.item(clip)?;\n                    }\n                }\n\n                if i == len - 1 {\n                    if *self.background_color != background_color::get_initial_specified_value() {\n                        writer.item(self.background_color)?;\n                    }\n                }\n\n                if !writer.has_written() {\n                    image.to_css(dest)?;\n                }\n            }\n\n            Ok(())\n        }\n    }\n}\n\npub mod font {\n    pub use crate::properties::generated::shorthands::font::*;\n\n    use super::*;\n    #[cfg(feature = \"gecko\")]\n    use crate::properties::longhands::{\n        font_family, font_feature_settings, font_language_override, font_size,\n        font_size_adjust, font_variant_alternates, font_variant_east_asian, font_variant_emoji,\n        font_variant_ligatures, font_variant_numeric, font_variant_position,\n    };\n    use crate::properties::longhands::{\n        font_kerning, font_optical_sizing, font_stretch, font_style, font_variant_caps, font_variation_settings,\n        font_weight,\n    };\n    #[cfg(feature = \"gecko\")]\n    use crate::values::specified::font::SystemFont;\n    use crate::values::specified::font::{\n        FontFamily, FontSize, FontStretch, FontStretchKeyword, FontStyle, FontWeight, LineHeight,\n    };\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut nb_normals = 0;\n        let mut style = None;\n        let mut variant_caps = None;\n        let mut weight = None;\n        let mut stretch = None;\n        #[cfg(feature = \"gecko\")]\n        if let Ok(sys) = input.try_parse(|i| SystemFont::parse(context, i)) {\n            return Ok(Longhands {\n                font_family: font_family::SpecifiedValue::system_font(sys),\n                font_size: font_size::SpecifiedValue::system_font(sys),\n                font_style: font_style::SpecifiedValue::system_font(sys),\n                font_stretch: font_stretch::SpecifiedValue::system_font(sys),\n                font_weight: font_weight::SpecifiedValue::system_font(sys),\n                line_height: LineHeight::normal(),\n                font_kerning: font_kerning::get_initial_specified_value(),\n                font_language_override: font_language_override::get_initial_specified_value(),\n                font_size_adjust: font_size_adjust::get_initial_specified_value(),\n                font_variant_alternates: font_variant_alternates::get_initial_specified_value(),\n                font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),\n                font_variant_emoji: font_variant_emoji::get_initial_specified_value(),\n                font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),\n                font_variant_numeric: font_variant_numeric::get_initial_specified_value(),\n                font_variant_position: font_variant_position::get_initial_specified_value(),\n                font_feature_settings: font_feature_settings::get_initial_specified_value(),\n                font_optical_sizing: font_optical_sizing::get_initial_specified_value(),\n                font_variant_caps: font_variant_caps::get_initial_specified_value(),\n                font_variation_settings: font_variation_settings::get_initial_specified_value(),\n            });\n        }\n\n        let size;\n        loop {\n            if input\n                .try_parse(|input| input.expect_ident_matching(\"normal\"))\n                .is_ok()\n            {\n                nb_normals += 1;\n                continue;\n            }\n            try_parse_one!(context, input, style, font_style::parse);\n            try_parse_one!(context, input, weight, font_weight::parse);\n            if variant_caps.is_none() {\n                if input\n                    .try_parse(|input| input.expect_ident_matching(\"small-caps\"))\n                    .is_ok()\n                {\n                    variant_caps = Some(font_variant_caps::SpecifiedValue::SmallCaps);\n                    continue;\n                }\n            }\n            try_parse_one!(input, stretch, FontStretchKeyword::parse);\n            size = FontSize::parse(context, input)?;\n            break;\n        }\n\n        let line_height = if input.try_parse(|input| input.expect_delim('/')).is_ok() {\n            Some(LineHeight::parse(context, input)?)\n        } else {\n            None\n        };\n\n        #[inline]\n        fn count<T>(opt: &Option<T>) -> u8 {\n            if opt.is_some() {\n                1\n            } else {\n                0\n            }\n        }\n\n        if (count(&style) + count(&weight) + count(&variant_caps) + count(&stretch) + nb_normals)\n            > 4\n        {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let family = FontFamily::parse(context, input)?;\n        let stretch = stretch.map(FontStretch::Keyword);\n        Ok(Longhands {\n            font_style: unwrap_or_initial!(font_style, style),\n            font_weight: unwrap_or_initial!(font_weight, weight),\n            font_stretch: unwrap_or_initial!(font_stretch, stretch),\n            font_variant_caps: unwrap_or_initial!(font_variant_caps, variant_caps),\n            font_size: size,\n            line_height: line_height.unwrap_or(LineHeight::normal()),\n            font_family: family,\n            font_optical_sizing: font_optical_sizing::get_initial_specified_value(),\n            font_variation_settings: font_variation_settings::get_initial_specified_value(),\n            font_kerning: font_kerning::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_language_override: font_language_override::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_size_adjust: font_size_adjust::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_alternates: font_variant_alternates::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_emoji: font_variant_emoji::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_numeric: font_variant_numeric::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_variant_position: font_variant_position::get_initial_specified_value(),\n            #[cfg(feature = \"gecko\")]\n            font_feature_settings: font_feature_settings::get_initial_specified_value(),\n        })\n    }\n\n    #[cfg(feature = \"gecko\")]\n    enum CheckSystemResult {\n        AllSystem(SystemFont),\n        SomeSystem,\n        None,\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            #[cfg(feature = \"gecko\")]\n            match self.check_system() {\n                CheckSystemResult::AllSystem(sys) => return sys.to_css(dest),\n                CheckSystemResult::SomeSystem => return Ok(()),\n                CheckSystemResult::None => {},\n            }\n\n            if let Some(v) = self.font_optical_sizing {\n                if v != &font_optical_sizing::get_initial_specified_value() {\n                    return Ok(());\n                }\n            }\n            if let Some(v) = self.font_variation_settings {\n                if v != &font_variation_settings::get_initial_specified_value() {\n                    return Ok(());\n                }\n            }\n            #[cfg(feature = \"gecko\")]\n            if let Some(v) = self.font_variant_emoji {\n                if v != &font_variant_emoji::get_initial_specified_value() {\n                    return Ok(());\n                }\n            }\n\n            if self.font_kerning != &font_kerning::get_initial_specified_value() {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_language_override != &font_language_override::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_variant_alternates\n                != &font_variant_alternates::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_variant_east_asian\n                != &font_variant_east_asian::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value()\n            {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_variant_position != &font_variant_position::get_initial_specified_value() {\n                return Ok(());\n            }\n            #[cfg(feature = \"gecko\")]\n            if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() {\n                return Ok(());\n            }\n\n            let font_stretch = match *self.font_stretch {\n                FontStretch::Keyword(kw) => kw,\n                FontStretch::Stretch(percentage) => {\n                    match FontStretchKeyword::from_percentage(percentage.0.get()) {\n                        Some(kw) => kw,\n                        None => return Ok(()),\n                    }\n                },\n                FontStretch::System(..) => return Ok(()),\n            };\n\n            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value()\n                && *self.font_variant_caps != font_variant_caps::SpecifiedValue::SmallCaps\n            {\n                return Ok(());\n            }\n\n            if self.font_style != &font_style::get_initial_specified_value() {\n                self.font_style.to_css(dest)?;\n                dest.write_char(' ')?;\n            }\n            if self.font_variant_caps != &font_variant_caps::get_initial_specified_value() {\n                self.font_variant_caps.to_css(dest)?;\n                dest.write_char(' ')?;\n            }\n\n            if self.font_weight != &FontWeight::normal()\n                && self.font_weight != &FontWeight::from_gecko_keyword(400)\n            {\n                self.font_weight.to_css(dest)?;\n                dest.write_char(' ')?;\n            }\n\n            if font_stretch != FontStretchKeyword::Normal {\n                font_stretch.to_css(dest)?;\n                dest.write_char(' ')?;\n            }\n\n            self.font_size.to_css(dest)?;\n\n            if *self.line_height != LineHeight::normal() {\n                dest.write_str(\" / \")?;\n                self.line_height.to_css(dest)?;\n            }\n\n            dest.write_char(' ')?;\n            self.font_family.to_css(dest)?;\n\n            Ok(())\n        }\n    }\n\n    impl<'a> LonghandsToSerialize<'a> {\n        #[cfg(feature = \"gecko\")]\n        fn check_system(&self) -> CheckSystemResult {\n            let mut sys = None;\n            let mut all = true;\n\n            macro_rules! check {\n                ($v:expr) => {\n                    match $v.get_system() {\n                        Some(s) => {\n                            debug_assert!(sys.is_none() || s == sys.unwrap());\n                            sys = Some(s);\n                        },\n                        None => {\n                            all = false;\n                        },\n                    }\n                };\n                ($e:expr, $($es:expr),+) => { check!($e); check!($($es),*); };\n            }\n\n            check!(\n                self.font_family,\n                self.font_size,\n                self.font_style,\n                self.font_stretch,\n                self.font_weight\n            );\n\n            if self.line_height != &LineHeight::normal() {\n                all = false\n            }\n            if all {\n                CheckSystemResult::AllSystem(sys.unwrap())\n            } else if sys.is_some() {\n                CheckSystemResult::SomeSystem\n            } else {\n                CheckSystemResult::None\n            }\n        }\n    }\n\n    impl SpecifiedValueInfo for Longhands {\n        const SUPPORTED_TYPES: u8 = FontStyle::SUPPORTED_TYPES\n            | FontWeight::SUPPORTED_TYPES\n            | FontStretch::SUPPORTED_TYPES\n            | font_variant_caps::SpecifiedValue::SUPPORTED_TYPES\n            | FontSize::SUPPORTED_TYPES\n            | FontFamily::SUPPORTED_TYPES;\n\n        fn collect_completion_keywords(f: KeywordsCollectFn) {\n            FontStyle::collect_completion_keywords(f);\n            FontWeight::collect_completion_keywords(f);\n            FontStretch::collect_completion_keywords(f);\n            font_variant_caps::SpecifiedValue::collect_completion_keywords(f);\n            FontSize::collect_completion_keywords(f);\n            FontFamily::collect_completion_keywords(f);\n\n            #[cfg(feature = \"gecko\")]\n            SystemFont::collect_completion_keywords(f);\n        }\n    }\n}\n\npub mod font_variant {\n    pub use crate::properties::generated::shorthands::font_variant::*;\n\n    use super::*;\n    use crate::properties::longhands::font_variant_caps;\n    #[cfg(feature = \"gecko\")]\n    use crate::properties::longhands::{\n        font_variant_alternates, font_variant_east_asian, font_variant_emoji,\n        font_variant_ligatures, font_variant_numeric, font_variant_position,\n    };\n    #[allow(unused_imports)]\n    use crate::values::specified::FontVariantLigatures;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        #[cfg(feature = \"gecko\")]\n        let mut ligatures = None;\n        let mut caps = None;\n        #[cfg(feature = \"gecko\")]\n        let mut alternates = None;\n        #[cfg(feature = \"gecko\")]\n        let mut numeric = None;\n        #[cfg(feature = \"gecko\")]\n        let mut east_asian = None;\n        #[cfg(feature = \"gecko\")]\n        let mut position = None;\n        #[cfg(feature = \"gecko\")]\n        let mut emoji = None;\n\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n        } else if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            #[cfg(feature = \"gecko\")]\n            {\n                ligatures = Some(FontVariantLigatures::NONE);\n            }\n        } else {\n            let mut parsed = 0;\n            loop {\n                parsed += 1;\n                if input\n                    .try_parse(|input| input.expect_ident_matching(\"normal\"))\n                    .is_ok()\n                    || input\n                        .try_parse(|input| input.expect_ident_matching(\"none\"))\n                        .is_ok()\n                {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, ligatures, font_variant_ligatures::parse);\n                try_parse_one!(context, input, caps, font_variant_caps::parse);\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, alternates, font_variant_alternates::parse);\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, numeric, font_variant_numeric::parse);\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, east_asian, font_variant_east_asian::parse);\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, position, font_variant_position::parse);\n                #[cfg(feature = \"gecko\")]\n                try_parse_one!(context, input, emoji, font_variant_emoji::parse);\n                parsed -= 1;\n                break;\n            }\n\n            if parsed == 0 {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n        }\n\n        #[cfg(feature = \"gecko\")]\n        return Ok(expanded! {\n            font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures),\n            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),\n            font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates),\n            font_variant_numeric: unwrap_or_initial!(font_variant_numeric, numeric),\n            font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian),\n            font_variant_position: unwrap_or_initial!(font_variant_position, position),\n            font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji),\n        });\n        #[cfg(feature = \"servo\")]\n        return Ok(expanded! {\n            font_variant_caps: unwrap_or_initial!(font_variant_caps, caps),\n        });\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        #[allow(unused_assignments)]\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            #[cfg(feature = \"gecko\")]\n            let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE;\n            #[cfg(feature = \"servo\")]\n            let has_none_ligatures = false;\n\n            #[cfg(feature = \"gecko\")]\n            const TOTAL_SUBPROPS: usize = 7;\n            #[cfg(feature = \"servo\")]\n            const TOTAL_SUBPROPS: usize = 1;\n            let mut nb_normals = 0;\n            macro_rules! count_normal {\n                ($e: expr, $p: ident) => {\n                    if *$e == $p::get_initial_specified_value() {\n                        nb_normals += 1;\n                    }\n                };\n                ($v: ident) => {\n                    count_normal!(self.$v, $v);\n                };\n            }\n            #[cfg(feature = \"gecko\")]\n            count_normal!(font_variant_ligatures);\n            count_normal!(font_variant_caps);\n            #[cfg(feature = \"gecko\")]\n            count_normal!(font_variant_alternates);\n            #[cfg(feature = \"gecko\")]\n            count_normal!(font_variant_numeric);\n            #[cfg(feature = \"gecko\")]\n            count_normal!(font_variant_east_asian);\n            #[cfg(feature = \"gecko\")]\n            count_normal!(font_variant_position);\n            #[cfg(feature = \"gecko\")]\n            if let Some(value) = self.font_variant_emoji {\n                if value == &font_variant_emoji::get_initial_specified_value() {\n                    nb_normals += 1;\n                }\n            } else {\n                nb_normals += 1;\n            }\n\n            if nb_normals == TOTAL_SUBPROPS {\n                return dest.write_str(\"normal\");\n            }\n            if has_none_ligatures {\n                if nb_normals == TOTAL_SUBPROPS - 1 {\n                    dest.write_str(\"none\")?;\n                }\n                return Ok(());\n            }\n\n            let mut writer = SequenceWriter::new(dest, \" \");\n            macro_rules! write {\n                ($e: expr, $p: ident) => {\n                    if *$e != $p::get_initial_specified_value() {\n                        writer.item($e)?;\n                    }\n                };\n                ($v: ident) => {\n                    write!(self.$v, $v);\n                };\n            }\n\n            #[cfg(feature = \"gecko\")]\n            write!(font_variant_ligatures);\n            write!(font_variant_caps);\n            #[cfg(feature = \"gecko\")]\n            write!(font_variant_alternates);\n            #[cfg(feature = \"gecko\")]\n            write!(font_variant_numeric);\n            #[cfg(feature = \"gecko\")]\n            write!(font_variant_east_asian);\n            #[cfg(feature = \"gecko\")]\n            write!(font_variant_position);\n            #[cfg(feature = \"gecko\")]\n            if let Some(v) = self.font_variant_emoji {\n                write!(v, font_variant_emoji);\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod font_synthesis {\n    pub use crate::properties::generated::shorthands::font_synthesis::*;\n\n    use super::*;\n    use crate::values::specified::{FontSynthesis, FontSynthesisStyle};\n\n    pub fn parse_value<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut weight = FontSynthesis::None;\n        let mut style = FontSynthesisStyle::None;\n        let mut small_caps = FontSynthesis::None;\n        let mut position = FontSynthesis::None;\n\n        if !input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            let mut has_custom_value = false;\n            while !input.is_exhausted() {\n                try_match_ident_ignore_ascii_case! { input,\n                    \"weight\" if weight == FontSynthesis::None => {\n                        has_custom_value = true;\n                        weight = FontSynthesis::Auto;\n                        continue;\n                    },\n                    \"style\" if style == FontSynthesisStyle::None => {\n                        has_custom_value = true;\n                        style = FontSynthesisStyle::Auto;\n                        continue;\n                    },\n                    \"small-caps\" if small_caps == FontSynthesis::None => {\n                        has_custom_value = true;\n                        small_caps = FontSynthesis::Auto;\n                        continue;\n                    },\n                    \"position\" if position == FontSynthesis::None => {\n                        has_custom_value = true;\n                        position = FontSynthesis::Auto;\n                        continue;\n                    },\n                    \"oblique-only\" if style == FontSynthesisStyle::None => {\n                        has_custom_value = true;\n                        style = FontSynthesisStyle::ObliqueOnly;\n                        continue;\n                    },\n                }\n            }\n            if !has_custom_value {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n        }\n\n        Ok(expanded! {\n            font_synthesis_weight: weight,\n            font_synthesis_style: style,\n            font_synthesis_small_caps: small_caps,\n            font_synthesis_position: position,\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if self.font_synthesis_weight == &FontSynthesis::Auto {\n                writer.raw_item(\"weight\")?;\n            }\n            if self.font_synthesis_style != &FontSynthesisStyle::None {\n                if self.font_synthesis_style == &FontSynthesisStyle::Auto {\n                    writer.raw_item(\"style\")?;\n                } else {\n                    writer.raw_item(\"oblique-only\")?;\n                }\n            }\n            if self.font_synthesis_small_caps == &FontSynthesis::Auto {\n                writer.raw_item(\"small-caps\")?;\n            }\n            if self.font_synthesis_position == &FontSynthesis::Auto {\n                writer.raw_item(\"position\")?;\n            }\n            if !writer.has_written() {\n                writer.raw_item(\"none\")?;\n            }\n            Ok(())\n        }\n    }\n\n    // The shorthand takes the sub-property names of the longhands, and not the\n    // 'auto' keyword like they do, so we can't automatically derive this.\n    impl SpecifiedValueInfo for Longhands {\n        fn collect_completion_keywords(f: KeywordsCollectFn) {\n            f(&[\n                \"none\",\n                \"oblique-only\",\n                \"small-caps\",\n                \"position\",\n                \"style\",\n                \"weight\",\n            ]);\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod text_box {\n    pub use crate::properties::generated::shorthands::text_box::*;\n\n    use super::*;\n    use crate::values::specified::{TextBoxEdge, TextBoxTrim};\n\n    pub fn parse_value<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut trim = None;\n        let mut edge = None;\n\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(Longhands {\n                text_box_trim: TextBoxTrim::NONE,\n                text_box_edge: TextBoxEdge::Auto,\n            });\n        }\n\n        loop {\n            try_parse_one!(context, input, trim, TextBoxTrim::parse);\n            try_parse_one!(context, input, edge, TextBoxEdge::parse);\n            break;\n        }\n\n        if trim.is_none() && edge.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // From https://drafts.csswg.org/css-inline-3/#text-box-shorthand:\n        // > Omitting the 'text-box-trim' value sets it to 'trim-both'\n        // > (not the initial value), while omitting the 'text-box-edge'\n        // > value sets it to auto (the initial value).\n        Ok(Longhands {\n            text_box_trim: trim.unwrap_or(TextBoxTrim::TRIM_BOTH),\n            text_box_edge: edge.unwrap_or(TextBoxEdge::Auto),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if *self.text_box_trim == TextBoxTrim::NONE && *self.text_box_edge == TextBoxEdge::Auto\n            {\n                return dest.write_str(\"normal\");\n            }\n\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if *self.text_box_trim != specified::TextBoxTrim::TRIM_BOTH {\n                writer.item(self.text_box_trim)?;\n            }\n            if *self.text_box_edge != specified::TextBoxEdge::Auto {\n                writer.item(self.text_box_edge)?;\n            }\n            if !writer.has_written() {\n                self.text_box_trim.to_css(dest)?;\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod text_emphasis {\n    pub use crate::properties::generated::shorthands::text_emphasis::*;\n\n    use super::*;\n    use crate::properties::longhands::{text_emphasis_color, text_emphasis_style};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut color = None;\n        let mut style = None;\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, color, text_emphasis_color::parse);\n            try_parse_one!(context, input, style, text_emphasis_style::parse);\n            parsed -= 1;\n            break;\n        }\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(expanded! {\n            text_emphasis_color: unwrap_or_initial!(text_emphasis_color, color),\n            text_emphasis_style: unwrap_or_initial!(text_emphasis_style, style),\n        })\n    }\n}\n\npub mod text_decoration {\n    pub use crate::properties::generated::shorthands::text_decoration::*;\n\n    use super::*;\n    #[cfg(feature = \"gecko\")]\n    use crate::properties::longhands::text_decoration_thickness;\n    use crate::properties::longhands::{\n        text_decoration_color, text_decoration_line, text_decoration_style,\n    };\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut line = None;\n        let mut style = None;\n        let mut color = None;\n        #[cfg(feature = \"gecko\")]\n        let mut thickness = None;\n\n        let mut parsed = 0;\n        loop {\n            parsed += 1;\n            try_parse_one!(context, input, line, text_decoration_line::parse);\n            try_parse_one!(context, input, style, text_decoration_style::parse);\n            try_parse_one!(context, input, color, text_decoration_color::parse);\n            #[cfg(feature = \"gecko\")]\n            try_parse_one!(context, input, thickness, text_decoration_thickness::parse);\n            parsed -= 1;\n            break;\n        }\n\n        if parsed == 0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        #[cfg(feature = \"gecko\")]\n        return Ok(expanded! {\n            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),\n            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),\n            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),\n            text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness),\n        });\n        #[cfg(feature = \"servo\")]\n        return Ok(expanded! {\n            text_decoration_line: unwrap_or_initial!(text_decoration_line, line),\n            text_decoration_style: unwrap_or_initial!(text_decoration_style, style),\n            text_decoration_color: unwrap_or_initial!(text_decoration_color, color),\n        });\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        #[allow(unused)]\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::values::specified::Color;\n            use crate::values::specified::TextDecorationLine;\n\n            let is_solid_style =\n                *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid;\n            let is_current_color = *self.text_decoration_color == Color::CurrentColor;\n            #[cfg(feature = \"gecko\")]\n            let is_auto_thickness = self.text_decoration_thickness.is_auto();\n            #[cfg(feature = \"servo\")]\n            let is_auto_thickness = true;\n            let is_none = *self.text_decoration_line == TextDecorationLine::none();\n\n            let mut writer = SequenceWriter::new(dest, \" \");\n            if (is_solid_style && is_current_color && is_auto_thickness) || !is_none {\n                writer.item(self.text_decoration_line)?;\n            }\n            #[cfg(feature = \"gecko\")]\n            if !is_auto_thickness {\n                writer.item(self.text_decoration_thickness)?;\n            }\n            if !is_solid_style {\n                writer.item(self.text_decoration_style)?;\n            }\n            if !is_current_color {\n                writer.item(self.text_decoration_color)?;\n            }\n            Ok(())\n        }\n    }\n}\n\npub mod animation {\n    pub use crate::properties::generated::shorthands::animation::*;\n\n    use super::*;\n    use crate::properties::longhands::{\n        animation_delay, animation_direction, animation_duration, animation_fill_mode,\n        animation_iteration_count, animation_name, animation_play_state, animation_range_end,\n        animation_range_start, animation_timeline, animation_timing_function,\n    };\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        struct SingleAnimation {\n            animation_name: animation_name::SingleSpecifiedValue,\n            animation_duration: animation_duration::SingleSpecifiedValue,\n            animation_timing_function: animation_timing_function::SingleSpecifiedValue,\n            animation_delay: animation_delay::SingleSpecifiedValue,\n            animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,\n            animation_direction: animation_direction::SingleSpecifiedValue,\n            animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,\n            animation_play_state: animation_play_state::SingleSpecifiedValue,\n        }\n\n        fn parse_one_animation<'i, 't>(\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n        ) -> Result<SingleAnimation, ParseError<'i>> {\n            let mut name = None;\n            let mut duration = None;\n            let mut timing_function = None;\n            let mut delay = None;\n            let mut iteration_count = None;\n            let mut direction = None;\n            let mut fill_mode = None;\n            let mut play_state = None;\n\n            let mut parsed = 0;\n            loop {\n                parsed += 1;\n                try_parse_one!(\n                    context,\n                    input,\n                    duration,\n                    animation_duration::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    timing_function,\n                    animation_timing_function::single_value::parse\n                );\n                try_parse_one!(context, input, delay, animation_delay::single_value::parse);\n                try_parse_one!(\n                    context,\n                    input,\n                    iteration_count,\n                    animation_iteration_count::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    direction,\n                    animation_direction::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    fill_mode,\n                    animation_fill_mode::single_value::parse\n                );\n                try_parse_one!(\n                    context,\n                    input,\n                    play_state,\n                    animation_play_state::single_value::parse\n                );\n                try_parse_one!(context, input, name, animation_name::single_value::parse);\n                parsed -= 1;\n                break;\n            }\n\n            if parsed == 0 {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n            Ok(SingleAnimation {\n                animation_name: name\n                    .unwrap_or_else(animation_name::single_value::get_initial_specified_value),\n                animation_duration: duration\n                    .unwrap_or_else(animation_duration::single_value::get_initial_specified_value),\n                animation_timing_function: timing_function.unwrap_or_else(\n                    animation_timing_function::single_value::get_initial_specified_value,\n                ),\n                animation_delay: delay\n                    .unwrap_or_else(animation_delay::single_value::get_initial_specified_value),\n                animation_iteration_count: iteration_count.unwrap_or_else(\n                    animation_iteration_count::single_value::get_initial_specified_value,\n                ),\n                animation_direction: direction\n                    .unwrap_or_else(animation_direction::single_value::get_initial_specified_value),\n                animation_fill_mode: fill_mode\n                    .unwrap_or_else(animation_fill_mode::single_value::get_initial_specified_value),\n                animation_play_state: play_state.unwrap_or_else(\n                    animation_play_state::single_value::get_initial_specified_value,\n                ),\n            })\n        }\n\n        let mut names = vec![];\n        let mut durations = vec![];\n        let mut timing_functions = vec![];\n        let mut delays = vec![];\n        let mut iteration_counts = vec![];\n        let mut directions = vec![];\n        let mut fill_modes = vec![];\n        let mut play_states = vec![];\n\n        let results = input.parse_comma_separated(|i| parse_one_animation(context, i))?;\n        for result in results.into_iter() {\n            names.push(result.animation_name);\n            durations.push(result.animation_duration);\n            timing_functions.push(result.animation_timing_function);\n            delays.push(result.animation_delay);\n            iteration_counts.push(result.animation_iteration_count);\n            directions.push(result.animation_direction);\n            fill_modes.push(result.animation_fill_mode);\n            play_states.push(result.animation_play_state);\n        }\n\n        Ok(expanded! {\n            animation_name: animation_name::SpecifiedValue(names.into()),\n            animation_duration: animation_duration::SpecifiedValue(durations.into()),\n            animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions.into()),\n            animation_delay: animation_delay::SpecifiedValue(delays.into()),\n            animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts.into()),\n            animation_direction: animation_direction::SpecifiedValue(directions.into()),\n            animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes.into()),\n            animation_play_state: animation_play_state::SpecifiedValue(play_states.into()),\n            animation_timeline: animation_timeline::SpecifiedValue(\n                vec![animation_timeline::single_value::get_initial_specified_value()].into()\n            ),\n            // The animation-range properties are reset-only sub-properties of the animation\n            // shorthand.\n            // https://drafts.csswg.org/scroll-animations-1/#named-range-animation-declaration\n            animation_range_start: animation_range_start::SpecifiedValue(\n                vec![animation_range_start::single_value::get_initial_specified_value()].into()\n            ),\n            animation_range_end: animation_range_end::SpecifiedValue(\n                vec![animation_range_end::single_value::get_initial_specified_value()].into()\n            ),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::values::specified::easing::TimingFunction;\n            use crate::values::specified::{\n                AnimationDirection, AnimationFillMode, AnimationPlayState,\n            };\n            use crate::Zero;\n            use style_traits::values::SequenceWriter;\n\n            let len = self.animation_name.0.len();\n            if len == 0 {\n                return Ok(());\n            }\n\n            if len != self.animation_duration.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_timing_function.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_delay.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_iteration_count.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_direction.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_fill_mode.0.len() {\n                return Ok(());\n            }\n            if len != self.animation_play_state.0.len() {\n                return Ok(());\n            }\n\n            // We don't serialize animation-timeline, animation-range-start and animation-range-end\n            // if any of them are not the initial value.\n            if self\n                .animation_timeline\n                .map_or(false, |v| v.0.len() != 1 || !v.0[0].is_auto())\n            {\n                return Ok(());\n            }\n            if self\n                .animation_range_start\n                .map_or(false, |v| v.0.len() != 1 || !v.0[0].0.is_normal())\n            {\n                return Ok(());\n            }\n            if self\n                .animation_range_end\n                .map_or(false, |v| v.0.len() != 1 || !v.0[0].0.is_normal())\n            {\n                return Ok(());\n            }\n\n            for i in 0..len {\n                if i != 0 {\n                    dest.write_str(\", \")?;\n                }\n\n                let has_duration = !self.animation_duration.0[i].is_auto()\n                    && !self.animation_duration.0[i].is_zero();\n                let has_timing_function = !self.animation_timing_function.0[i].is_ease();\n                let has_delay = !self.animation_delay.0[i].is_zero();\n                let has_iteration_count = !self.animation_iteration_count.0[i].is_one();\n                let has_direction =\n                    !matches!(self.animation_direction.0[i], AnimationDirection::Normal);\n                let has_fill_mode =\n                    !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);\n                let has_play_state =\n                    !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);\n                let animation_name = &self.animation_name.0[i];\n                let has_name = !animation_name.is_none();\n\n                let mut writer = SequenceWriter::new(dest, \" \");\n\n                if has_duration || has_delay {\n                    writer.item(&self.animation_duration.0[i])?;\n                }\n\n                if has_timing_function || TimingFunction::match_keywords(animation_name) {\n                    writer.item(&self.animation_timing_function.0[i])?;\n                }\n\n                if has_delay {\n                    writer.item(&self.animation_delay.0[i])?;\n                }\n                if has_iteration_count {\n                    writer.item(&self.animation_iteration_count.0[i])?;\n                }\n\n                if has_direction || AnimationDirection::match_keywords(animation_name) {\n                    writer.item(&self.animation_direction.0[i])?;\n                }\n\n                if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {\n                    writer.item(&self.animation_fill_mode.0[i])?;\n                }\n\n                if has_play_state || AnimationPlayState::match_keywords(animation_name) {\n                    writer.item(&self.animation_play_state.0[i])?;\n                }\n\n                if has_name || !writer.has_written() {\n                    writer.item(animation_name)?;\n                }\n            }\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod mask {\n    pub use crate::properties::generated::shorthands::mask::*;\n\n    use super::*;\n    use crate::parser::Parse;\n    use crate::properties::longhands::{\n        mask_clip, mask_composite, mask_mode, mask_origin, mask_position_x, mask_position_y,\n        mask_repeat,\n    };\n    use crate::properties::longhands::{mask_image, mask_size};\n    use crate::values::specified::{Position, PositionComponent};\n\n    impl From<mask_origin::single_value::SpecifiedValue> for mask_clip::single_value::SpecifiedValue {\n        fn from(\n            origin: mask_origin::single_value::SpecifiedValue,\n        ) -> mask_clip::single_value::SpecifiedValue {\n            match origin {\n                mask_origin::single_value::SpecifiedValue::ContentBox => {\n                    mask_clip::single_value::SpecifiedValue::ContentBox\n                },\n                mask_origin::single_value::SpecifiedValue::PaddingBox => {\n                    mask_clip::single_value::SpecifiedValue::PaddingBox\n                },\n                mask_origin::single_value::SpecifiedValue::BorderBox => {\n                    mask_clip::single_value::SpecifiedValue::BorderBox\n                },\n                mask_origin::single_value::SpecifiedValue::FillBox => {\n                    mask_clip::single_value::SpecifiedValue::FillBox\n                },\n                mask_origin::single_value::SpecifiedValue::StrokeBox => {\n                    mask_clip::single_value::SpecifiedValue::StrokeBox\n                },\n                mask_origin::single_value::SpecifiedValue::ViewBox => {\n                    mask_clip::single_value::SpecifiedValue::ViewBox\n                },\n            }\n        }\n    }\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut mask_image = Vec::with_capacity(1);\n        let mut mask_mode = Vec::with_capacity(1);\n        let mut mask_position_x = Vec::with_capacity(1);\n        let mut mask_position_y = Vec::with_capacity(1);\n        let mut mask_size = Vec::with_capacity(1);\n        let mut mask_repeat = Vec::with_capacity(1);\n        let mut mask_origin = Vec::with_capacity(1);\n        let mut mask_clip = Vec::with_capacity(1);\n        let mut mask_composite = Vec::with_capacity(1);\n\n        input.parse_comma_separated(|input| {\n            let mut image = None;\n            let mut mode = None;\n            let mut position = None;\n            let mut size = None;\n            let mut repeat = None;\n            let mut origin = None;\n            let mut clip = None;\n            let mut composite = None;\n            let mut parsed = 0;\n            loop {\n                parsed += 1;\n\n                try_parse_one!(context, input, image, mask_image::single_value::parse);\n                if position.is_none() {\n                    if let Ok(value) = input.try_parse(|input| Position::parse(context, input)) {\n                        position = Some(value);\n                        size = input\n                            .try_parse(|input| {\n                                input.expect_delim('/')?;\n                                mask_size::single_value::parse(context, input)\n                            })\n                            .ok();\n\n                        continue;\n                    }\n                }\n                try_parse_one!(context, input, repeat, mask_repeat::single_value::parse);\n                try_parse_one!(context, input, origin, mask_origin::single_value::parse);\n                try_parse_one!(context, input, clip, mask_clip::single_value::parse);\n                try_parse_one!(\n                    context,\n                    input,\n                    composite,\n                    mask_composite::single_value::parse\n                );\n                try_parse_one!(context, input, mode, mask_mode::single_value::parse);\n\n                parsed -= 1;\n                break;\n            }\n            if clip.is_none() {\n                if let Some(origin) = origin {\n                    clip = Some(mask_clip::single_value::SpecifiedValue::from(origin));\n                }\n            }\n            if parsed == 0 {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n            if let Some(position) = position {\n                mask_position_x.push(position.horizontal);\n                mask_position_y.push(position.vertical);\n            } else {\n                mask_position_x.push(PositionComponent::zero());\n                mask_position_y.push(PositionComponent::zero());\n            }\n            if let Some(m_image) = image {\n                mask_image.push(m_image);\n            } else {\n                mask_image.push(mask_image::single_value::get_initial_specified_value());\n            }\n            if let Some(m_mode) = mode {\n                mask_mode.push(m_mode);\n            } else {\n                mask_mode.push(mask_mode::single_value::get_initial_specified_value());\n            }\n            if let Some(m_size) = size {\n                mask_size.push(m_size);\n            } else {\n                mask_size.push(mask_size::single_value::get_initial_specified_value());\n            }\n            if let Some(m_repeat) = repeat {\n                mask_repeat.push(m_repeat);\n            } else {\n                mask_repeat.push(mask_repeat::single_value::get_initial_specified_value());\n            }\n            if let Some(m_origin) = origin {\n                mask_origin.push(m_origin);\n            } else {\n                mask_origin.push(mask_origin::single_value::get_initial_specified_value());\n            }\n            if let Some(m_clip) = clip {\n                mask_clip.push(m_clip);\n            } else {\n                mask_clip.push(mask_clip::single_value::get_initial_specified_value());\n            }\n            if let Some(m_composite) = composite {\n                mask_composite.push(m_composite);\n            } else {\n                mask_composite.push(mask_composite::single_value::get_initial_specified_value());\n            }\n            Ok(())\n        })?;\n\n        Ok(expanded! {\n           mask_image: mask_image::SpecifiedValue(mask_image.into()),\n           mask_mode: mask_mode::SpecifiedValue(mask_mode.into()),\n           mask_position_x: mask_position_x::SpecifiedValue(mask_position_x.into()),\n           mask_position_y: mask_position_y::SpecifiedValue(mask_position_y.into()),\n           mask_size: mask_size::SpecifiedValue(mask_size.into()),\n           mask_repeat: mask_repeat::SpecifiedValue(mask_repeat.into()),\n           mask_origin: mask_origin::SpecifiedValue(mask_origin.into()),\n           mask_clip: mask_clip::SpecifiedValue(mask_clip.into()),\n           mask_composite: mask_composite::SpecifiedValue(mask_composite.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            use crate::properties::longhands::mask_clip::single_value::computed_value::T as Clip;\n            use crate::properties::longhands::mask_origin::single_value::computed_value::T as Origin;\n            use style_traits::values::SequenceWriter;\n\n            let len = self.mask_image.0.len();\n            if len == 0 {\n                return Ok(());\n            }\n            if self.mask_mode.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_position_x.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_position_y.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_size.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_repeat.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_origin.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_clip.0.len() != len {\n                return Ok(());\n            }\n            if self.mask_composite.0.len() != len {\n                return Ok(());\n            }\n\n            for i in 0..len {\n                if i > 0 {\n                    dest.write_str(\", \")?;\n                }\n\n                let image = &self.mask_image.0[i];\n                let mode = &self.mask_mode.0[i];\n                let position_x = &self.mask_position_x.0[i];\n                let position_y = &self.mask_position_y.0[i];\n                let size = &self.mask_size.0[i];\n                let repeat = &self.mask_repeat.0[i];\n                let origin = &self.mask_origin.0[i];\n                let clip = &self.mask_clip.0[i];\n                let composite = &self.mask_composite.0[i];\n\n                let mut has_other = false;\n                let has_image = *image != mask_image::single_value::get_initial_specified_value();\n                has_other |= has_image;\n                let has_mode = *mode != mask_mode::single_value::get_initial_specified_value();\n                has_other |= has_mode;\n                let has_size = *size != mask_size::single_value::get_initial_specified_value();\n                has_other |= has_size;\n                let has_repeat =\n                    *repeat != mask_repeat::single_value::get_initial_specified_value();\n                has_other |= has_repeat;\n                let has_composite =\n                    *composite != mask_composite::single_value::get_initial_specified_value();\n                has_other |= has_composite;\n                let has_position = *position_x != PositionComponent::zero()\n                    || *position_y != PositionComponent::zero();\n                let has_origin = *origin != Origin::BorderBox;\n                let has_clip = *clip != Clip::BorderBox;\n\n                if !has_other && !has_position && !has_origin && !has_clip {\n                    return image.to_css(dest);\n                }\n\n                let mut writer = SequenceWriter::new(dest, \" \");\n                if has_image {\n                    writer.item(image)?;\n                }\n                if has_position || has_size {\n                    writer.write_item(|dest| {\n                        Position {\n                            horizontal: position_x.clone(),\n                            vertical: position_y.clone(),\n                        }\n                        .to_css(dest)?;\n                        if has_size {\n                            dest.write_str(\" / \")?;\n                            size.to_css(dest)?;\n                        }\n                        Ok(())\n                    })?;\n                }\n\n                if has_repeat {\n                    writer.item(repeat)?;\n                }\n\n                if has_origin || (has_clip && *clip != Clip::NoClip) {\n                    writer.item(origin)?;\n                }\n\n                if has_clip && *clip != From::from(*origin) {\n                    writer.item(clip)?;\n                }\n\n                if has_composite {\n                    writer.item(composite)?;\n                }\n\n                if has_mode {\n                    writer.item(mode)?;\n                }\n            }\n\n            Ok(())\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\npub mod mask_position {\n    pub use crate::properties::generated::shorthands::mask_position::*;\n\n    use super::*;\n    use crate::properties::longhands::{mask_position_x, mask_position_y};\n    use crate::values::specified::Position;\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        // Vec grows from 0 to 4 by default on first push().  So allocate with capacity 1, so in\n        // the common case of only one item we don't way overallocate, then shrink.  Note that we\n        // always push at least one item if parsing succeeds.\n        let mut position_x = Vec::with_capacity(1);\n        let mut position_y = Vec::with_capacity(1);\n        input.parse_comma_separated(|input| {\n            let value = Position::parse(context, input)?;\n            position_x.push(value.horizontal);\n            position_y.push(value.vertical);\n            Ok(())\n        })?;\n\n        if position_x.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(expanded! {\n            mask_position_x: mask_position_x::SpecifiedValue(position_x.into()),\n            mask_position_y: mask_position_y::SpecifiedValue(position_y.into()),\n        })\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            let len = self.mask_position_x.0.len();\n            if len == 0 || self.mask_position_y.0.len() != len {\n                return Ok(());\n            }\n\n            for i in 0..len {\n                Position {\n                    horizontal: self.mask_position_x.0[i].clone(),\n                    vertical: self.mask_position_y.0[i].clone(),\n                }\n                .to_css(dest)?;\n\n                if i < len - 1 {\n                    dest.write_str(\", \")?;\n                }\n            }\n\n            Ok(())\n        }\n    }\n}\n\npub mod grid_template {\n    pub use crate::properties::generated::shorthands::grid_template::*;\n\n    use super::*;\n    use crate::parser::Parse;\n    use crate::values::generics::grid::{concat_serialize_idents, TrackListValue};\n    use crate::values::generics::grid::{TrackList, TrackSize};\n    use crate::values::specified::grid::parse_line_names;\n    use crate::values::specified::position::{\n        GridTemplateAreas, TemplateAreasArc, TemplateAreasParser,\n    };\n    use crate::values::specified::{GenericGridTemplateComponent, GridTemplateComponent};\n    use servo_arc::Arc;\n\n    pub fn parse_grid_template<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<\n        (\n            GridTemplateComponent,\n            GridTemplateComponent,\n            GridTemplateAreas,\n        ),\n        ParseError<'i>,\n    > {\n        if let Ok(x) = input.try_parse(|i| {\n            if i.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n                if !i.is_exhausted() {\n                    return Err(());\n                }\n                return Ok((\n                    GenericGridTemplateComponent::None,\n                    GenericGridTemplateComponent::None,\n                    GridTemplateAreas::None,\n                ));\n            }\n            Err(())\n        }) {\n            return Ok(x);\n        }\n\n        let first_line_names = input.try_parse(parse_line_names).unwrap_or_default();\n        let mut areas_parser = TemplateAreasParser::default();\n        if areas_parser.try_parse_string(input).is_ok() {\n            let mut values = vec![];\n            let mut line_names = vec![];\n            line_names.push(first_line_names);\n            loop {\n                let size = input\n                    .try_parse(|i| TrackSize::parse(context, i))\n                    .unwrap_or_default();\n                values.push(TrackListValue::TrackSize(size));\n                let mut names = input.try_parse(parse_line_names).unwrap_or_default();\n                let more_names = input.try_parse(parse_line_names);\n\n                match areas_parser.try_parse_string(input) {\n                    Ok(()) => {\n                        if let Ok(v) = more_names {\n                            let mut names_vec = names.into_vec();\n                            names_vec.extend(v.into_iter());\n                            names = names_vec.into();\n                        }\n                        line_names.push(names);\n                    },\n                    Err(e) => {\n                        if more_names.is_ok() {\n                            return Err(e);\n                        }\n                        line_names.push(names);\n                        break;\n                    },\n                };\n            }\n\n            if line_names.len() == values.len() {\n                line_names.push(Default::default());\n            }\n\n            let template_areas = areas_parser\n                .finish()\n                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;\n            let template_rows = TrackList {\n                values: values.into(),\n                line_names: line_names.into(),\n                auto_repeat_index: std::usize::MAX,\n            };\n\n            let template_cols = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n                let value = GridTemplateComponent::parse_without_none(context, input)?;\n                if let GenericGridTemplateComponent::TrackList(ref list) = value {\n                    if !list.is_explicit() {\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                }\n\n                value\n            } else {\n                GridTemplateComponent::default()\n            };\n\n            Ok((\n                GenericGridTemplateComponent::TrackList(Box::new(template_rows)),\n                template_cols,\n                GridTemplateAreas::Areas(TemplateAreasArc(Arc::new(template_areas))),\n            ))\n        } else {\n            let mut template_rows = GridTemplateComponent::parse(context, input)?;\n            if let GenericGridTemplateComponent::TrackList(ref mut list) = template_rows {\n                if list.line_names[0].is_empty() {\n                    list.line_names[0] = first_line_names;\n                } else {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n            }\n\n            input.expect_delim('/')?;\n            Ok((\n                template_rows,\n                GridTemplateComponent::parse(context, input)?,\n                GridTemplateAreas::None,\n            ))\n        }\n    }\n\n    #[inline]\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let (rows, columns, areas) = parse_grid_template(context, input)?;\n        Ok(expanded! {\n            grid_template_rows: rows,\n            grid_template_columns: columns,\n            grid_template_areas: areas,\n        })\n    }\n\n    pub fn serialize_grid_template<W>(\n        template_rows: &GridTemplateComponent,\n        template_columns: &GridTemplateComponent,\n        template_areas: &GridTemplateAreas,\n        dest: &mut CssWriter<W>,\n    ) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *template_areas {\n            GridTemplateAreas::None => {\n                if template_rows.is_initial() && template_columns.is_initial() {\n                    return GridTemplateComponent::default().to_css(dest);\n                }\n                template_rows.to_css(dest)?;\n                dest.write_str(\" / \")?;\n                template_columns.to_css(dest)\n            },\n            GridTemplateAreas::Areas(ref areas) => {\n                if areas.0.strings.len() != template_rows.track_list_len() {\n                    return Ok(());\n                }\n\n                let track_list = match *template_rows {\n                    GenericGridTemplateComponent::TrackList(ref list) => {\n                        if !list.is_explicit() {\n                            return Ok(());\n                        }\n                        list\n                    },\n                    _ => return Ok(()),\n                };\n\n                match *template_columns {\n                    GenericGridTemplateComponent::TrackList(ref list) => {\n                        if !list.is_explicit() {\n                            return Ok(());\n                        }\n                    },\n                    GenericGridTemplateComponent::Subgrid(_) => {\n                        return Ok(());\n                    },\n                    _ => {},\n                }\n\n                let mut names_iter = track_list.line_names.iter();\n                for (((i, string), names), value) in areas\n                    .0\n                    .strings\n                    .iter()\n                    .enumerate()\n                    .zip(&mut names_iter)\n                    .zip(track_list.values.iter())\n                {\n                    if i > 0 {\n                        dest.write_char(' ')?;\n                    }\n\n                    if !names.is_empty() {\n                        concat_serialize_idents(\"[\", \"] \", names, \" \", dest)?;\n                    }\n\n                    string.to_css(dest)?;\n\n                    if !value.is_initial() {\n                        dest.write_char(' ')?;\n                        value.to_css(dest)?;\n                    }\n                }\n\n                if let Some(names) = names_iter.next() {\n                    concat_serialize_idents(\" [\", \"]\", names, \" \", dest)?;\n                }\n\n                if let GenericGridTemplateComponent::TrackList(ref list) = *template_columns {\n                    dest.write_str(\" / \")?;\n                    list.to_css(dest)?;\n                }\n\n                Ok(())\n            },\n        }\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        #[inline]\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            serialize_grid_template(\n                self.grid_template_rows,\n                self.grid_template_columns,\n                self.grid_template_areas,\n                dest,\n            )\n        }\n    }\n}\n\npub mod grid {\n    pub use crate::properties::generated::shorthands::grid::*;\n\n    use super::*;\n    use crate::parser::Parse;\n    use crate::properties::longhands::{grid_auto_columns, grid_auto_flow, grid_auto_rows};\n    use crate::values::generics::grid::GridTemplateComponent;\n    use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};\n    use crate::values::specified::{GenericGridTemplateComponent, ImplicitGridTracks};\n\n    pub fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Longhands, ParseError<'i>> {\n        let mut temp_rows = GridTemplateComponent::default();\n        let mut temp_cols = GridTemplateComponent::default();\n        let mut temp_areas = GridTemplateAreas::None;\n        let mut auto_rows = ImplicitGridTracks::default();\n        let mut auto_cols = ImplicitGridTracks::default();\n        let mut flow = grid_auto_flow::get_initial_value();\n\n        fn parse_auto_flow<'i, 't>(\n            input: &mut Parser<'i, 't>,\n            is_row: bool,\n        ) -> Result<GridAutoFlow, ParseError<'i>> {\n            let mut track = None;\n            let mut dense = GridAutoFlow::empty();\n\n            for _ in 0..2 {\n                if input\n                    .try_parse(|i| i.expect_ident_matching(\"auto-flow\"))\n                    .is_ok()\n                {\n                    track = if is_row {\n                        Some(GridAutoFlow::ROW)\n                    } else {\n                        Some(GridAutoFlow::COLUMN)\n                    };\n                } else if input\n                    .try_parse(|i| i.expect_ident_matching(\"dense\"))\n                    .is_ok()\n                {\n                    dense = GridAutoFlow::DENSE\n                } else {\n                    break;\n                }\n            }\n\n            if track.is_some() {\n                Ok(track.unwrap() | dense)\n            } else {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            }\n        }\n\n        if let Ok((rows, cols, areas)) =\n            input.try_parse(|i| super::grid_template::parse_grid_template(context, i))\n        {\n            temp_rows = rows;\n            temp_cols = cols;\n            temp_areas = areas;\n        } else if let Ok(rows) = input.try_parse(|i| GridTemplateComponent::parse(context, i)) {\n            temp_rows = rows;\n            input.expect_delim('/')?;\n            flow = parse_auto_flow(input, false)?;\n            auto_cols = input\n                .try_parse(|i| grid_auto_columns::parse(context, i))\n                .unwrap_or_default();\n        } else {\n            flow = parse_auto_flow(input, true)?;\n            auto_rows = input\n                .try_parse(|i| grid_auto_rows::parse(context, i))\n                .unwrap_or_default();\n            input.expect_delim('/')?;\n            temp_cols = GridTemplateComponent::parse(context, input)?;\n        }\n\n        Ok(expanded! {\n            grid_template_rows: temp_rows,\n            grid_template_columns: temp_cols,\n            grid_template_areas: temp_areas,\n            grid_auto_rows: auto_rows,\n            grid_auto_columns: auto_cols,\n            grid_auto_flow: flow,\n        })\n    }\n\n    impl<'a> LonghandsToSerialize<'a> {\n        fn is_grid_template(&self) -> bool {\n            self.grid_auto_rows.is_initial()\n                && self.grid_auto_columns.is_initial()\n                && *self.grid_auto_flow == grid_auto_flow::get_initial_value()\n        }\n    }\n\n    impl<'a> ToCss for LonghandsToSerialize<'a> {\n        fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n        where\n            W: fmt::Write,\n        {\n            if self.is_grid_template() {\n                return super::grid_template::serialize_grid_template(\n                    self.grid_template_rows,\n                    self.grid_template_columns,\n                    self.grid_template_areas,\n                    dest,\n                );\n            }\n\n            if *self.grid_template_areas != GridTemplateAreas::None {\n                return Ok(());\n            }\n\n            if self.grid_auto_flow.contains(GridAutoFlow::COLUMN) {\n                if !self.grid_auto_rows.is_initial() || !self.grid_template_columns.is_initial() {\n                    return Ok(());\n                }\n\n                if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_rows\n                {\n                    if !list.is_explicit() {\n                        return Ok(());\n                    }\n                }\n\n                self.grid_template_rows.to_css(dest)?;\n                dest.write_str(\" / auto-flow\")?;\n                if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {\n                    dest.write_str(\" dense\")?;\n                }\n\n                if !self.grid_auto_columns.is_initial() {\n                    dest.write_char(' ')?;\n                    self.grid_auto_columns.to_css(dest)?;\n                }\n\n                return Ok(());\n            }\n\n            if !self.grid_auto_columns.is_initial() || !self.grid_template_rows.is_initial() {\n                return Ok(());\n            }\n\n            if let GenericGridTemplateComponent::TrackList(ref list) = *self.grid_template_columns {\n                if !list.is_explicit() {\n                    return Ok(());\n                }\n            }\n\n            dest.write_str(\"auto-flow\")?;\n            if self.grid_auto_flow.contains(GridAutoFlow::DENSE) {\n                dest.write_str(\" dense\")?;\n            }\n\n            if !self.grid_auto_rows.is_initial() {\n                dest.write_char(' ')?;\n                self.grid_auto_rows.to_css(dest)?;\n            }\n\n            dest.write_str(\" / \")?;\n            self.grid_template_columns.to_css(dest)?;\n            Ok(())\n        }\n    }\n}\n"
  },
  {
    "path": "style/properties/shorthands.toml",
    "content": "[margin]\nsub_properties = [\"margin-top\", \"margin-right\", \"margin-bottom\", \"margin-left\"]\nspec = \"https://drafts.csswg.org/css-box/#propdef-margin\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"page\", \"position-try\"]\nkind = \"four_sides\"\nallow_quirks = true\n\n[margin-block]\nsub_properties = [\"margin-block-start\", \"margin-block-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-margin-block\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[margin-inline]\nsub_properties = [\"margin-inline-start\", \"margin-inline-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-margin-inline\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[scroll-margin]\nengine = \"gecko\"\nsub_properties = [\"scroll-margin-top\", \"scroll-margin-right\", \"scroll-margin-bottom\", \"scroll-margin-left\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin\"\nkind = \"four_sides\"\n\n[scroll-margin-block]\nengine = \"gecko\"\nsub_properties = [\"scroll-margin-block-start\", \"scroll-margin-block-end\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block\"\nkind = \"two_properties\"\n\n[scroll-margin-inline]\nengine = \"gecko\"\nsub_properties = [\"scroll-margin-inline-start\", \"scroll-margin-inline-end\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline\"\nkind = \"two_properties\"\n\n[padding]\nsub_properties = [\"padding-top\", \"padding-right\", \"padding-bottom\", \"padding-left\"]\nspec = \"https://drafts.csswg.org/css-box/#propdef-padding\"\nkind = \"four_sides\"\nallow_quirks = true\n\n[padding-block]\nsub_properties = [\"padding-block-start\", \"padding-block-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-padding-block\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[padding-inline]\nsub_properties = [\"padding-inline-start\", \"padding-inline-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-padding-inline\"\nrule_types_allowed = [\"style\", \"keyframe\", \"page\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[scroll-padding]\nengine = \"gecko\"\nsub_properties = [\"scroll-padding-top\", \"scroll-padding-right\", \"scroll-padding-bottom\", \"scroll-padding-left\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding\"\nkind = \"four_sides\"\n\n[scroll-padding-block]\nengine = \"gecko\"\nsub_properties = [\"scroll-padding-block-start\", \"scroll-padding-block-end\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block\"\nkind = \"two_properties\"\n\n[scroll-padding-inline]\nengine = \"gecko\"\nsub_properties = [\"scroll-padding-inline-start\", \"scroll-padding-inline-end\"]\nspec = \"https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline\"\nkind = \"two_properties\"\n\n[border-color]\nsub_properties = [\"border-top-color\", \"border-right-color\", \"border-bottom-color\", \"border-left-color\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-color\"\nkind = \"four_sides\"\nallow_quirks = true\n\n[border-style]\nsub_properties = [\"border-top-style\", \"border-right-style\", \"border-bottom-style\", \"border-left-style\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-style\"\nkind = \"four_sides\"\n\n[border-width]\nsub_properties = [\"border-top-width\", \"border-right-width\", \"border-bottom-width\", \"border-left-width\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-width\"\nkind = \"four_sides\"\nallow_quirks = true\n\n[border-block-color]\nsub_properties = [\"border-block-start-color\", \"border-block-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block-color\"\nkind = \"two_properties\"\n\n[border-inline-color]\nsub_properties = [\"border-inline-start-color\", \"border-inline-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline-color\"\nkind = \"two_properties\"\n\n[border-block-width]\nsub_properties = [\"border-block-start-width\", \"border-block-end-width\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block-width\"\nkind = \"two_properties\"\n\n[border-inline-width]\nsub_properties = [\"border-inline-start-width\", \"border-inline-end-width\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline-width\"\nkind = \"two_properties\"\n\n[border-block-style]\nsub_properties = [\"border-block-start-style\", \"border-block-end-style\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block-style\"\nkind = \"two_properties\"\n\n[border-inline-style]\nsub_properties = [\"border-inline-start-style\", \"border-inline-end-style\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline-style\"\nkind = \"two_properties\"\n\n[border-top]\nsub_properties = [\"border-top-width\", \"border-top-style\", \"border-top-color\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#propdef-border-top\"\nkind = \"single_border\"\n\n[border-right]\nsub_properties = [\"border-right-width\", \"border-right-style\", \"border-right-color\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#propdef-border-right\"\nkind = \"single_border\"\n\n[border-bottom]\nsub_properties = [\"border-bottom-width\", \"border-bottom-style\", \"border-bottom-color\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#propdef-border-top\"\nkind = \"single_border\"\n\n[border-left]\nsub_properties = [\"border-left-width\", \"border-left-style\", \"border-left-color\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#propdef-border-left\"\nkind = \"single_border\"\n\n[border-block-start]\nsub_properties = [\"border-block-start-width\", \"border-block-start-style\", \"border-block-start-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block-start\"\nkind = \"single_border\"\n\n[border-block-end]\nsub_properties = [\"border-block-end-width\", \"border-block-end-style\", \"border-block-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block-end\"\nkind = \"single_border\"\n\n[border-inline-start]\nsub_properties = [\"border-inline-start-width\", \"border-inline-start-style\", \"border-inline-start-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline-start\"\nkind = \"single_border\"\nextra_gecko_aliases = [\"-moz-border-start\"]\n\n[border-inline-end]\nsub_properties = [\"border-inline-end-width\", \"border-inline-end-style\", \"border-inline-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline-end\"\nkind = \"single_border\"\nextra_gecko_aliases = [\"-moz-border-end\"]\n\n[border-block]\nsub_properties = [\"border-block-start-width\", \"border-block-start-style\", \"border-block-start-color\", \"border-block-end-width\", \"border-block-end-style\", \"border-block-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-block\"\n\n[border-inline]\nsub_properties = [\"border-inline-start-width\", \"border-inline-start-style\", \"border-inline-start-color\", \"border-inline-end-width\", \"border-inline-end-style\", \"border-inline-end-color\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-border-inline\"\n\n[border-image]\nsub_properties = [\"border-image-outset\", \"border-image-repeat\", \"border-image-slice\", \"border-image-source\", \"border-image-width\"]\nextra_prefixes = [\"moz:layout.css.prefixes.border-image\", \"webkit\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#border-image\"\n\n[border]\nsub_properties = [\n  \"border-top-width\",\n  \"border-top-style\",\n  \"border-top-color\",\n  \"border-right-width\",\n  \"border-right-style\",\n  \"border-right-color\",\n  \"border-bottom-width\",\n  \"border-bottom-style\",\n  \"border-bottom-color\",\n  \"border-left-width\",\n  \"border-left-style\",\n  \"border-left-color\",\n  \"border-image-outset\",\n  \"border-image-repeat\",\n  \"border-image-slice\",\n  \"border-image-source\",\n  \"border-image-width\"\n]\nspec = \"https://drafts.csswg.org/css-backgrounds-3/#border\"\nderive_value_info = false\n\n[overflow]\nsub_properties = [\"overflow-x\", \"overflow-y\"]\nspec=\"https://drafts.csswg.org/css-overflow/#propdef-overflow\"\nkind = \"two_properties\"\n\n[overscroll-behavior]\nengine = \"gecko\"\nsub_properties = [\"overscroll-behavior-x\", \"overscroll-behavior-y\"]\nspec = \"https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties\"\nkind = \"two_properties\"\n\n[container]\nengine = \"gecko\"\nsub_properties = [\"container-name\", \"container-type\"]\nspec = \"https://drafts.csswg.org/css-contain-3/#container-shorthand\"\n\n[vertical-align]\nsub_properties = [\"alignment-baseline\", \"baseline-shift\", \"baseline-source\"]\nspec = \"https://drafts.csswg.org/css-inline-3/#transverse-alignment\"\n\n[page-break-before]\nengine = \"gecko\"\nflags = \"IS_LEGACY_SHORTHAND\"\nsub_properties = [\"break-before\"]\nspec = \"https://drafts.csswg.org/css-break-3/#page-break-properties\"\n\n[page-break-after]\nengine = \"gecko\"\nflags = \"IS_LEGACY_SHORTHAND\"\nsub_properties = [\"break-after\"]\nspec = \"https://drafts.csswg.org/css-break-3/#page-break-properties\"\n\n[page-break-inside]\nengine = \"gecko\"\nflags = \"IS_LEGACY_SHORTHAND\"\nsub_properties = [\"break-inside\"]\nspec = \"https://drafts.csswg.org/css-break-3/#page-break-properties\"\n\n[offset]\nengine = \"gecko\"\nsub_properties = [\"offset-path\", \"offset-distance\", \"offset-rotate\", \"offset-anchor\", \"offset-position\"]\nspec = \"https://drafts.fxtf.org/motion-1/#offset-shorthand\"\n\n[-webkit-perspective]\nsub_properties = [\"perspective\"]\nderive_serialize = true\nflags = \"IS_LEGACY_SHORTHAND\"\nspec = \"https://github.com/whatwg/compat/issues/100\"\n\n[-webkit-transform]\nsub_properties = [\"transform\"]\nderive_serialize = true\nflags = \"IS_LEGACY_SHORTHAND\"\nspec = \"https://github.com/whatwg/compat/issues/100\"\n\n[background]\nsub_properties = [\n  \"background-color\",\n  \"background-position-x\",\n  \"background-position-y\",\n  \"background-repeat\",\n  \"background-attachment\",\n  \"background-image\",\n  \"background-size\",\n  \"background-origin\",\n  \"background-clip\"\n]\nspec = \"https://drafts.csswg.org/css-backgrounds/#the-background\"\n\n[inset]\nsub_properties = [\"top\", \"right\", \"bottom\", \"left\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-inset\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nkind = \"four_sides\"\n\n[inset-block]\nsub_properties = [\"inset-block-start\", \"inset-block-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-inset-block\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[inset-inline]\nsub_properties = [\"inset-inline-start\", \"inset-inline-end\"]\nspec = \"https://drafts.csswg.org/css-logical/#propdef-inset-inline\"\nrule_types_allowed = [\"style\", \"keyframe\", \"scope\", \"position-try\"]\nkind = \"two_properties\"\n\n[contain-intrinsic-size]\nengine = \"gecko\"\nsub_properties = [\"contain-intrinsic-width\", \"contain-intrinsic-height\"]\nspec = \"https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\"\nkind = \"two_properties\"\n\n[flex-flow]\nsub_properties = [\"flex-direction\", \"flex-wrap\"]\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-flow-property\"\nextra_prefixes = [\"webkit\"]\n\n[flex]\nsub_properties = [\"flex-grow\", \"flex-shrink\", \"flex-basis\"]\nspec = \"https://drafts.csswg.org/css-flexbox/#flex-property\"\nextra_prefixes = [\"webkit\"]\nderive_serialize = true\n\n[gap]\nsub_properties = [\"row-gap\", \"column-gap\"]\nspec = \"https://drafts.csswg.org/css-align-3/#gap-shorthand\"\naliases = [\"grid-gap\"]\n\n[grid-row]\nsub_properties = [\"grid-row-start\", \"grid-row-end\"]\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-row\"\nservo_pref = \"layout.grid.enabled\"\n\n[grid-column]\nsub_properties = [\"grid-column-start\", \"grid-column-end\"]\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-column\"\nservo_pref = \"layout.grid.enabled\"\n\n[grid-area]\nsub_properties = [\"grid-row-start\", \"grid-row-end\", \"grid-column-start\", \"grid-column-end\"]\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-area\"\nservo_pref = \"layout.grid.enabled\"\n\n[grid-template]\nsub_properties = [\"grid-template-rows\", \"grid-template-columns\", \"grid-template-areas\"]\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid-template\"\nservo_pref = \"layout.grid.enabled\"\n\n[grid]\nsub_properties = [\"grid-template-rows\", \"grid-template-columns\", \"grid-template-areas\", \"grid-auto-rows\", \"grid-auto-columns\", \"grid-auto-flow\"]\nspec = \"https://drafts.csswg.org/css-grid/#propdef-grid\"\nservo_pref = \"layout.grid.enabled\"\n\n[place-content]\nsub_properties = [\"align-content\", \"justify-content\"]\nspec = \"https://drafts.csswg.org/css-align/#propdef-place-content\"\n\n[place-self]\nsub_properties = [\"align-self\", \"justify-self\"]\nspec = \"https://drafts.csswg.org/css-align/#place-self-property\"\nrule_types_allowed = [\"style\", \"position-try\"]\n\n[place-items]\nsub_properties = [\"align-items\", \"justify-items\"]\nspec = \"https://drafts.csswg.org/css-align/#place-items-property\"\n\n[position-try]\nengine = \"gecko\"\nsub_properties = [\"position-try-order\", \"position-try-fallbacks\"]\nspec = \"https://drafts.csswg.org/css-anchor-position-1/#position-try-prop\"\ngecko_pref = \"layout.css.anchor-positioning.enabled\"\n\n[marker]\nengine = \"gecko\"\nsub_properties = [\"marker-start\", \"marker-end\", \"marker-mid\"]\nspec = \"https://svgwg.org/svg2-draft/painting.html#MarkerShorthand\"\n\n[transition]\nsub_properties = [\"transition-property\", \"transition-duration\", \"transition-timing-function\", \"transition-delay\", \"transition-behavior\"]\nspec = \"https://drafts.csswg.org/css-transitions/#propdef-transition\"\nextra_prefixes = [\"moz:layout.css.prefixes.transitions\", \"webkit\"]\n\n[animation]\nsub_properties = [\"animation-name\", \"animation-duration\", \"animation-timing-function\", \"animation-delay\", \"animation-iteration-count\", \"animation-direction\", \"animation-fill-mode\", \"animation-play-state\", \"animation-timeline\", \"animation-range-start\", \"animation-range-end\"]\nspec = \"https://drafts.csswg.org/css-animations/#propdef-animation\"\nextra_prefixes = [\"moz:layout.css.prefixes.animations\", \"webkit\"]\nrule_types_allowed = [\"style\"]\n\n[scroll-timeline]\nengine = \"gecko\"\nsub_properties = [\"scroll-timeline-name\", \"scroll-timeline-axis\"]\nspec = \"https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-shorthand\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\n\n[view-timeline]\nengine = \"gecko\"\nsub_properties = [\"view-timeline-name\", \"view-timeline-axis\"]\nspec = \"https://drafts.csswg.org/scroll-animations-1/#view-timeline-shorthand\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\n\n[animation-range]\nengine = \"gecko\"\nsub_properties = [\"animation-range-start\", \"animation-range-end\"]\nspec = \"https://drafts.csswg.org/scroll-animations-1/#animation-range\"\ngecko_pref = \"layout.css.scroll-driven-animations.enabled\"\n\n[list-style]\nsub_properties = [\"list-style-position\", \"list-style-image\", \"list-style-type\"]\nspec = \"https://drafts.csswg.org/css-lists/#propdef-list-style\"\n\n[background-position]\nsub_properties = [\"background-position-x\", \"background-position-y\"]\nspec = \"https://drafts.csswg.org/css-backgrounds-4/#the-background-position\"\n\n[mask]\nengine = \"gecko\"\nsub_properties = [\"mask-mode\", \"mask-repeat\", \"mask-clip\", \"mask-origin\", \"mask-composite\", \"mask-position-x\", \"mask-position-y\", \"mask-size\", \"mask-image\"]\nspec = \"https://drafts.fxtf.org/css-masking/#propdef-mask\"\nextra_prefixes = [\"webkit\"]\n\n[mask-position]\nengine = \"gecko\"\nsub_properties = [\"mask-position-x\", \"mask-position-y\"]\nspec = \"https://drafts.csswg.org/css-masks-4/#the-mask-position\"\nextra_prefixes = [\"webkit\"]\n\n[border-radius]\nsub_properties = [\"border-top-left-radius\", \"border-top-right-radius\", \"border-bottom-right-radius\", \"border-bottom-left-radius\"]\nspec = \"https://drafts.csswg.org/css-backgrounds/#border-radius\"\nextra_prefixes = [\"webkit\"]\n\n[outline]\nsub_properties = [\"outline-color\", \"outline-style\", \"outline-width\"]\nspec = \"https://drafts.csswg.org/css-ui/#propdef-outline\"\n\n[columns]\nsub_properties = [\"column-width\", \"column-count\"]\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-columns\"\nservo_pref = \"layout.columns.enabled\"\n\n[column-rule]\nengine = \"gecko\"\nsub_properties = [\"column-rule-width\", \"column-rule-style\", \"column-rule-color\"]\nspec = \"https://drafts.csswg.org/css-multicol/#propdef-column-rule\"\nderive_serialize = true\n\n[text-decoration]\nsub_properties = [\"text-decoration-color\", \"text-decoration-line\", \"text-decoration-style\"]\nextra_gecko_sub_properties = [\"text-decoration-thickness\"]\nspec = \"https://drafts.csswg.org/css-text-decor/#propdef-text-decoration\"\n\n[font]\nsub_properties = [\n  \"font-style\",\n  \"font-variant-caps\",\n  \"font-weight\",\n  \"font-stretch\",\n  \"font-size\",\n  \"line-height\",\n  \"font-family\",\n  \"font-optical-sizing\",\n  \"font-variation-settings\",\n  \"font-kerning\"\n]\nextra_gecko_sub_properties = [\n  \"font-size-adjust\",\n  \"font-variant-alternates\",\n  \"font-variant-east-asian\",\n  \"font-variant-emoji\",\n  \"font-variant-ligatures\",\n  \"font-variant-numeric\",\n  \"font-variant-position\",\n  \"font-language-override\",\n  \"font-feature-settings\"\n]\nspec = \"https://drafts.csswg.org/css-fonts-3/#propdef-font\"\nderive_value_info = false\n\n[font-variant]\nsub_properties = [\"font-variant-caps\"]\nextra_gecko_sub_properties = [\"font-variant-alternates\", \"font-variant-east-asian\", \"font-variant-emoji\", \"font-variant-ligatures\", \"font-variant-numeric\", \"font-variant-position\"]\nspec = \"https://drafts.csswg.org/css-fonts-3/#propdef-font-variant\"\n\n[font-synthesis]\nengine = \"gecko\"\nsub_properties = [\"font-synthesis-weight\", \"font-synthesis-style\", \"font-synthesis-small-caps\", \"font-synthesis-position\"]\nspec = \"https://drafts.csswg.org/css-fonts-3/#propdef-font-variant\"\nderive_value_info = false\n\n[text-box]\nengine = \"gecko\"\nsub_properties = [\"text-box-trim\", \"text-box-edge\"]\nspec = \"https://drafts.csswg.org/css-inline/#text-box-shorthand\"\ngecko_pref = \"layout.css.text-box.enabled\"\n\n[text-emphasis]\nengine = \"gecko\"\nsub_properties = [\"text-emphasis-style\", \"text-emphasis-color\"]\nspec = \"https://drafts.csswg.org/css-text-decor-3/#text-emphasis-property\"\nderive_serialize = true\n\n[text-wrap]\nengine = \"gecko\"\nsub_properties = [\"text-wrap-mode\", \"text-wrap-style\"]\nspec = \"https://www.w3.org/TR/css-text-4/#text-wrap\"\n\n[white-space]\nsub_properties = [\"text-wrap-mode\", \"white-space-collapse\"]\nspec = \"https://www.w3.org/TR/css-text-4/#white-space-property\"\nderive_value_info = false\n\n[-webkit-text-stroke]\nengine = \"gecko\"\nsub_properties = [\"-webkit-text-stroke-width\", \"-webkit-text-stroke-color\"]\nspec = \"https://compat.spec.whatwg.org/#the-webkit-text-stroke\"\nderive_serialize = true\n"
  },
  {
    "path": "style/properties/vendored_python/markupsafe/LICENSE.txt",
    "content": "Copyright 2010 Pallets\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n1.  Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n\n2.  Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n\n3.  Neither the name of the copyright holder nor the names of its\n    contributors may be used to endorse or promote products derived from\n    this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nHOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\nTO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "style/properties/vendored_python/markupsafe/__init__.py",
    "content": "# Vendored from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/__init__.py\n# with patched section from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/_native.py\nfrom __future__ import annotations\n\nimport collections.abc as cabc\nimport string\nimport typing as t\n\n# BEGIN PATCHED SECTION\ndef _escape_inner(s: str, /) -> str:\n    return (\n        s.replace(\"&\", \"&amp;\")\n        .replace(\">\", \"&gt;\")\n        .replace(\"<\", \"&lt;\")\n        .replace(\"'\", \"&#39;\")\n        .replace('\"', \"&#34;\")\n    )\n# BEGIN PATCHED SECTION\n\n\nclass _HasHTML(t.Protocol):\n    def __html__(self, /) -> str: ...\n\n\nclass _TPEscape(t.Protocol):\n    def __call__(self, s: t.Any, /) -> Markup: ...\n\n\ndef escape(s: t.Any, /) -> Markup:\n    \"\"\"Replace the characters ``&``, ``<``, ``>``, ``'``, and ``\"`` in\n    the string with HTML-safe sequences. Use this if you need to display\n    text that might contain such characters in HTML.\n\n    If the object has an ``__html__`` method, it is called and the\n    return value is assumed to already be safe for HTML.\n\n    :param s: An object to be converted to a string and escaped.\n    :return: A :class:`Markup` string with the escaped text.\n    \"\"\"\n    # If the object is already a plain string, skip __html__ check and string\n    # conversion. This is the most common use case.\n    # Use type(s) instead of s.__class__ because a proxy object may be reporting\n    # the __class__ of the proxied value.\n    if type(s) is str:\n        return Markup(_escape_inner(s))\n\n    if hasattr(s, \"__html__\"):\n        return Markup(s.__html__())\n\n    return Markup(_escape_inner(str(s)))\n\n\ndef escape_silent(s: t.Any | None, /) -> Markup:\n    \"\"\"Like :func:`escape` but treats ``None`` as the empty string.\n    Useful with optional values, as otherwise you get the string\n    ``'None'`` when the value is ``None``.\n\n    >>> escape(None)\n    Markup('None')\n    >>> escape_silent(None)\n    Markup('')\n    \"\"\"\n    if s is None:\n        return Markup()\n\n    return escape(s)\n\n\ndef soft_str(s: t.Any, /) -> str:\n    \"\"\"Convert an object to a string if it isn't already. This preserves\n    a :class:`Markup` string rather than converting it back to a basic\n    string, so it will still be marked as safe and won't be escaped\n    again.\n\n    >>> value = escape(\"<User 1>\")\n    >>> value\n    Markup('&lt;User 1&gt;')\n    >>> escape(str(value))\n    Markup('&amp;lt;User 1&amp;gt;')\n    >>> escape(soft_str(value))\n    Markup('&lt;User 1&gt;')\n    \"\"\"\n    if not isinstance(s, str):\n        return str(s)\n\n    return s\n\n\nclass Markup(str):\n    \"\"\"A string that is ready to be safely inserted into an HTML or XML\n    document, either because it was escaped or because it was marked\n    safe.\n\n    Passing an object to the constructor converts it to text and wraps\n    it to mark it safe without escaping. To escape the text, use the\n    :meth:`escape` class method instead.\n\n    >>> Markup(\"Hello, <em>World</em>!\")\n    Markup('Hello, <em>World</em>!')\n    >>> Markup(42)\n    Markup('42')\n    >>> Markup.escape(\"Hello, <em>World</em>!\")\n    Markup('Hello &lt;em&gt;World&lt;/em&gt;!')\n\n    This implements the ``__html__()`` interface that some frameworks\n    use. Passing an object that implements ``__html__()`` will wrap the\n    output of that method, marking it safe.\n\n    >>> class Foo:\n    ...     def __html__(self):\n    ...         return '<a href=\"/foo\">foo</a>'\n    ...\n    >>> Markup(Foo())\n    Markup('<a href=\"/foo\">foo</a>')\n\n    This is a subclass of :class:`str`. It has the same methods, but\n    escapes their arguments and returns a ``Markup`` instance.\n\n    >>> Markup(\"<em>%s</em>\") % (\"foo & bar\",)\n    Markup('<em>foo &amp; bar</em>')\n    >>> Markup(\"<em>Hello</em> \") + \"<foo>\"\n    Markup('<em>Hello</em> &lt;foo&gt;')\n    \"\"\"\n\n    __slots__ = ()\n\n    def __new__(\n        cls, object: t.Any = \"\", encoding: str | None = None, errors: str = \"strict\"\n    ) -> te.Self:\n        if hasattr(object, \"__html__\"):\n            object = object.__html__()\n\n        if encoding is None:\n            return super().__new__(cls, object)\n\n        return super().__new__(cls, object, encoding, errors)\n\n    def __html__(self, /) -> te.Self:\n        return self\n\n    def __add__(self, value: str | _HasHTML, /) -> te.Self:\n        if isinstance(value, str) or hasattr(value, \"__html__\"):\n            return self.__class__(super().__add__(self.escape(value)))\n\n        return NotImplemented\n\n    def __radd__(self, value: str | _HasHTML, /) -> te.Self:\n        if isinstance(value, str) or hasattr(value, \"__html__\"):\n            return self.escape(value).__add__(self)\n\n        return NotImplemented\n\n    def __mul__(self, value: t.SupportsIndex, /) -> te.Self:\n        return self.__class__(super().__mul__(value))\n\n    def __rmul__(self, value: t.SupportsIndex, /) -> te.Self:\n        return self.__class__(super().__mul__(value))\n\n    def __mod__(self, value: t.Any, /) -> te.Self:\n        if isinstance(value, tuple):\n            # a tuple of arguments, each wrapped\n            value = tuple(_MarkupEscapeHelper(x, self.escape) for x in value)\n        elif hasattr(type(value), \"__getitem__\") and not isinstance(value, str):\n            # a mapping of arguments, wrapped\n            value = _MarkupEscapeHelper(value, self.escape)\n        else:\n            # a single argument, wrapped with the helper and a tuple\n            value = (_MarkupEscapeHelper(value, self.escape),)\n\n        return self.__class__(super().__mod__(value))\n\n    def __repr__(self, /) -> str:\n        return f\"{self.__class__.__name__}({super().__repr__()})\"\n\n    def join(self, iterable: cabc.Iterable[str | _HasHTML], /) -> te.Self:\n        return self.__class__(super().join(map(self.escape, iterable)))\n\n    def split(  # type: ignore[override]\n        self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1\n    ) -> list[te.Self]:\n        return [self.__class__(v) for v in super().split(sep, maxsplit)]\n\n    def rsplit(  # type: ignore[override]\n        self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1\n    ) -> list[te.Self]:\n        return [self.__class__(v) for v in super().rsplit(sep, maxsplit)]\n\n    def splitlines(  # type: ignore[override]\n        self, /, keepends: bool = False\n    ) -> list[te.Self]:\n        return [self.__class__(v) for v in super().splitlines(keepends)]\n\n    def unescape(self, /) -> str:\n        \"\"\"Convert escaped markup back into a text string. This replaces\n        HTML entities with the characters they represent.\n\n        >>> Markup(\"Main &raquo; <em>About</em>\").unescape()\n        'Main » <em>About</em>'\n        \"\"\"\n        from html import unescape\n\n        return unescape(str(self))\n\n    def striptags(self, /) -> str:\n        \"\"\":meth:`unescape` the markup, remove tags, and normalize\n        whitespace to single spaces.\n\n        >>> Markup(\"Main &raquo;\\t<em>About</em>\").striptags()\n        'Main » About'\n        \"\"\"\n        value = str(self)\n\n        # Look for comments then tags separately. Otherwise, a comment that\n        # contains a tag would end early, leaving some of the comment behind.\n\n        # keep finding comment start marks\n        while (start := value.find(\"<!--\")) != -1:\n            # find a comment end mark beyond the start, otherwise stop\n            if (end := value.find(\"-->\", start)) == -1:\n                break\n\n            value = f\"{value[:start]}{value[end + 3 :]}\"\n\n        # remove tags using the same method\n        while (start := value.find(\"<\")) != -1:\n            if (end := value.find(\">\", start)) == -1:\n                break\n\n            value = f\"{value[:start]}{value[end + 1 :]}\"\n\n        # collapse spaces\n        value = \" \".join(value.split())\n        return self.__class__(value).unescape()\n\n    @classmethod\n    def escape(cls, s: t.Any, /) -> te.Self:\n        \"\"\"Escape a string. Calls :func:`escape` and ensures that for\n        subclasses the correct type is returned.\n        \"\"\"\n        rv = escape(s)\n\n        if rv.__class__ is not cls:\n            return cls(rv)\n\n        return rv  # type: ignore[return-value]\n\n    def __getitem__(self, key: t.SupportsIndex | slice, /) -> te.Self:\n        return self.__class__(super().__getitem__(key))\n\n    def capitalize(self, /) -> te.Self:\n        return self.__class__(super().capitalize())\n\n    def title(self, /) -> te.Self:\n        return self.__class__(super().title())\n\n    def lower(self, /) -> te.Self:\n        return self.__class__(super().lower())\n\n    def upper(self, /) -> te.Self:\n        return self.__class__(super().upper())\n\n    def replace(self, old: str, new: str, count: t.SupportsIndex = -1, /) -> te.Self:\n        return self.__class__(super().replace(old, self.escape(new), count))\n\n    def ljust(self, width: t.SupportsIndex, fillchar: str = \" \", /) -> te.Self:\n        return self.__class__(super().ljust(width, self.escape(fillchar)))\n\n    def rjust(self, width: t.SupportsIndex, fillchar: str = \" \", /) -> te.Self:\n        return self.__class__(super().rjust(width, self.escape(fillchar)))\n\n    def lstrip(self, chars: str | None = None, /) -> te.Self:\n        return self.__class__(super().lstrip(chars))\n\n    def rstrip(self, chars: str | None = None, /) -> te.Self:\n        return self.__class__(super().rstrip(chars))\n\n    def center(self, width: t.SupportsIndex, fillchar: str = \" \", /) -> te.Self:\n        return self.__class__(super().center(width, self.escape(fillchar)))\n\n    def strip(self, chars: str | None = None, /) -> te.Self:\n        return self.__class__(super().strip(chars))\n\n    def translate(\n        self,\n        table: cabc.Mapping[int, str | int | None],  # type: ignore[override]\n        /,\n    ) -> str:\n        return self.__class__(super().translate(table))\n\n    def expandtabs(self, /, tabsize: t.SupportsIndex = 8) -> te.Self:\n        return self.__class__(super().expandtabs(tabsize))\n\n    def swapcase(self, /) -> te.Self:\n        return self.__class__(super().swapcase())\n\n    def zfill(self, width: t.SupportsIndex, /) -> te.Self:\n        return self.__class__(super().zfill(width))\n\n    def casefold(self, /) -> te.Self:\n        return self.__class__(super().casefold())\n\n    def removeprefix(self, prefix: str, /) -> te.Self:\n        return self.__class__(super().removeprefix(prefix))\n\n    def removesuffix(self, suffix: str) -> te.Self:\n        return self.__class__(super().removesuffix(suffix))\n\n    def partition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]:\n        left, sep, right = super().partition(sep)\n        cls = self.__class__\n        return cls(left), cls(sep), cls(right)\n\n    def rpartition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]:\n        left, sep, right = super().rpartition(sep)\n        cls = self.__class__\n        return cls(left), cls(sep), cls(right)\n\n    def format(self, *args: t.Any, **kwargs: t.Any) -> te.Self:\n        formatter = EscapeFormatter(self.escape)\n        return self.__class__(formatter.vformat(self, args, kwargs))\n\n    def format_map(\n        self,\n        mapping: cabc.Mapping[str, t.Any],  # type: ignore[override]\n        /,\n    ) -> te.Self:\n        formatter = EscapeFormatter(self.escape)\n        return self.__class__(formatter.vformat(self, (), mapping))\n\n    def __html_format__(self, format_spec: str, /) -> te.Self:\n        if format_spec:\n            raise ValueError(\"Unsupported format specification for Markup.\")\n\n        return self\n\n\nclass EscapeFormatter(string.Formatter):\n    __slots__ = (\"escape\",)\n\n    def __init__(self, escape: _TPEscape) -> None:\n        self.escape: _TPEscape = escape\n        super().__init__()\n\n    def format_field(self, value: t.Any, format_spec: str) -> str:\n        if hasattr(value, \"__html_format__\"):\n            rv = value.__html_format__(format_spec)\n        elif hasattr(value, \"__html__\"):\n            if format_spec:\n                raise ValueError(\n                    f\"Format specifier {format_spec} given, but {type(value)} does not\"\n                    \" define __html_format__. A class that defines __html__ must define\"\n                    \" __html_format__ to work with format specifiers.\"\n                )\n            rv = value.__html__()\n        else:\n            # We need to make sure the format spec is str here as\n            # otherwise the wrong callback methods are invoked.\n            rv = super().format_field(value, str(format_spec))\n        return str(self.escape(rv))\n\n\nclass _MarkupEscapeHelper:\n    \"\"\"Helper for :meth:`Markup.__mod__`.\"\"\"\n\n    __slots__ = (\"obj\", \"escape\")\n\n    def __init__(self, obj: t.Any, escape: _TPEscape) -> None:\n        self.obj: t.Any = obj\n        self.escape: _TPEscape = escape\n\n    def __getitem__(self, key: t.Any, /) -> te.Self:\n        return self.__class__(self.obj[key], self.escape)\n\n    def __str__(self, /) -> str:\n        return str(self.escape(self.obj))\n\n    def __repr__(self, /) -> str:\n        return str(self.escape(repr(self.obj)))\n\n    def __int__(self, /) -> int:\n        return int(self.obj)\n\n    def __float__(self, /) -> float:\n        return float(self.obj)\n"
  },
  {
    "path": "style/properties/view_transition_descriptors.toml",
    "content": "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# The descriptors of the @view-transition rule.\n\n[navigation]\ntype = \"NavigationType\"\n\n[types]\ntype = \"TransitionTypes\"\n"
  },
  {
    "path": "style/properties_and_values/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Properties and Values\n//!\n//! https://drafts.css-houdini.org/css-properties-values-api-1/\n\npub mod registry;\npub mod rule;\npub mod syntax;\npub mod value;\n"
  },
  {
    "path": "style/properties_and_values/registry.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Registered custom properties.\n\nuse super::rule::{Descriptors, PropertyRuleName};\nuse crate::derives::*;\nuse crate::selector_map::PrecomputedHashMap;\nuse crate::stylesheets::UrlExtraData;\nuse crate::Atom;\nuse cssparser::SourceLocation;\n\n/// A computed, already-validated property registration.\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#custom-property-registration>\n#[derive(Debug, Clone, MallocSizeOf)]\npub struct PropertyRegistration {\n    /// The custom property name.\n    pub name: PropertyRuleName,\n    /// The actual information about the property.\n    pub descriptors: Descriptors,\n    /// The url data that is used to parse and compute the registration's initial value. Note that\n    /// it's not the url data that should be used to parse other values. Other values should use\n    /// the data of the style sheet where they came from.\n    pub url_data: UrlExtraData,\n    /// The source location of this registration, if it comes from a CSS rule.\n    pub source_location: SourceLocation,\n}\n\n/// The script registry of custom properties.\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#dom-window-registeredpropertyset-slot>\n#[derive(Default)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct ScriptRegistry {\n    properties: PrecomputedHashMap<Atom, PropertyRegistration>,\n}\n\nimpl ScriptRegistry {\n    /// Gets an already-registered custom property via script.\n    #[inline]\n    pub fn get(&self, name: &Atom) -> Option<&PropertyRegistration> {\n        self.properties.get(name)\n    }\n\n    /// Gets already-registered custom properties via script.\n    #[inline]\n    pub fn properties(&self) -> &PrecomputedHashMap<Atom, PropertyRegistration> {\n        &self.properties\n    }\n\n    /// Register a given property. As per\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>\n    /// we don't allow overriding the registration.\n    #[inline]\n    pub fn register(&mut self, registration: PropertyRegistration) {\n        let name = registration.name.0.clone();\n        let old = self.properties.insert(name, registration);\n        debug_assert!(old.is_none(), \"Already registered? Should be an error\");\n    }\n\n    /// Returns the properties hashmap.\n    #[inline]\n    pub fn get_all(&self) -> &PrecomputedHashMap<Atom, PropertyRegistration> {\n        &self.properties\n    }\n}\n"
  },
  {
    "path": "style/properties_and_values/rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@property`] at-rule.\n//!\n//! https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule\n\nuse super::{\n    registry::PropertyRegistration,\n    value::{\n        AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,\n        SpecifiedValue as SpecifiedRegisteredValue,\n    },\n};\nuse crate::custom_properties::{Name as CustomPropertyName, SpecifiedValue};\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::values::{computed, serialize_atom_name};\nuse cssparser::{\n    BasicParseErrorKind, ParseErrorKind, Parser, ParserInput, RuleBodyParser, SourceLocation,\n};\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{\n    CssStringWriter, CssWriter, ParseError, PropertyInheritsParseError, PropertySyntaxParseError,\n    StyleParseErrorKind, ToCss,\n};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\npub use super::syntax::Descriptor as SyntaxDescriptor;\npub use crate::properties::property::{DescriptorId, DescriptorParser, Descriptors};\n\n/// Parse the block inside a `@property` rule.\n///\n/// Valid `@property` rules result in a registered custom property, as if `registerProperty()` had\n/// been called with equivalent parameters.\npub fn parse_property_block<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    name: PropertyRuleName,\n    source_location: SourceLocation,\n) -> Result<PropertyRegistration, ParseError<'i>> {\n    let mut descriptors = Descriptors::default();\n    let mut parser = DescriptorParser {\n        context,\n        descriptors: &mut descriptors,\n    };\n    let mut iter = RuleBodyParser::new(input, &mut parser);\n    let mut syntax_err = None;\n    let mut inherits_err = None;\n    while let Some(declaration) = iter.next() {\n        if !context.error_reporting_enabled() {\n            continue;\n        }\n        if let Err((error, slice)) = declaration {\n            let location = error.location;\n            let error = match error.kind {\n                // If the provided string is not a valid syntax string (if it\n                // returns failure when consume a syntax definition is called on\n                // it), the descriptor is invalid and must be ignored.\n                ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_)) => {\n                    syntax_err = Some(error.clone());\n                    ContextualParseError::UnsupportedValue(slice, error)\n                },\n\n                // If the provided string is not a valid inherits string,\n                // the descriptor is invalid and must be ignored.\n                ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(_)) => {\n                    inherits_err = Some(error.clone());\n                    ContextualParseError::UnsupportedValue(slice, error)\n                },\n\n                // Unknown descriptors are invalid and ignored, but do not\n                // invalidate the @property rule.\n                _ => ContextualParseError::UnsupportedPropertyDescriptor(slice, error),\n            };\n            context.log_css_error(location, error);\n        }\n    }\n\n    // https://drafts.css-houdini.org/css-properties-values-api-1/#the-syntax-descriptor:\n    //\n    //     The syntax descriptor is required for the @property rule to be valid; if it’s\n    //     missing, the @property rule is invalid.\n    let Some(ref syntax) = descriptors.syntax else {\n        return Err(if let Some(err) = syntax_err {\n            err\n        } else {\n            let err = input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(\n                PropertySyntaxParseError::NoSyntax,\n            ));\n            context.log_css_error(\n                source_location,\n                ContextualParseError::UnsupportedValue(\"\", err.clone()),\n            );\n            err\n        });\n    };\n\n    // https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor:\n    //\n    //     The inherits descriptor is required for the @property rule to be valid; if it’s\n    //     missing, the @property rule is invalid.\n    if descriptors.inherits.is_none() {\n        return Err(if let Some(err) = inherits_err {\n            err\n        } else {\n            let err = input.new_custom_error(StyleParseErrorKind::PropertyInheritsField(\n                PropertyInheritsParseError::NoInherits,\n            ));\n            context.log_css_error(\n                source_location,\n                ContextualParseError::UnsupportedValue(\"\", err.clone()),\n            );\n            err\n        });\n    };\n\n    if PropertyRegistration::validate_initial_value(syntax, descriptors.initial_value.as_deref())\n        .is_err()\n    {\n        return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid));\n    }\n\n    Ok(PropertyRegistration {\n        name,\n        descriptors,\n        url_data: context.url_data.clone(),\n        source_location,\n    })\n}\n\n/// Errors that can happen when registering a property.\n#[allow(missing_docs)]\npub enum PropertyRegistrationError {\n    NoInitialValue,\n    InvalidInitialValue,\n    InitialValueNotComputationallyIndependent,\n}\n\nimpl PropertyRegistration {\n    /// Measure heap usage.\n    pub fn size_of(&self, _: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        MallocSizeOf::size_of(self, ops)\n    }\n\n    /// Computes the value of the computationally independent initial value.\n    pub fn compute_initial_value(\n        &self,\n        computed_context: &computed::Context,\n    ) -> Result<ComputedRegisteredValue, ()> {\n        let Some(ref initial) = self.descriptors.initial_value else {\n            return Err(());\n        };\n\n        if self.descriptors.is_universal() {\n            return Ok(ComputedRegisteredValue::universal(Arc::clone(initial)));\n        }\n\n        let mut input = ParserInput::new(initial.css_text());\n        let mut input = Parser::new(&mut input);\n        input.skip_whitespace();\n\n        match SpecifiedRegisteredValue::compute(\n            &mut input,\n            &self.descriptors,\n            None,\n            &self.url_data,\n            computed_context,\n            AllowComputationallyDependent::No,\n            /* attr_taint */ Default::default(),\n        ) {\n            Ok(computed) => Ok(computed),\n            Err(_) => Err(()),\n        }\n    }\n\n    /// Performs syntax validation as per the initial value descriptor.\n    /// https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor\n    pub fn validate_initial_value(\n        syntax: &SyntaxDescriptor,\n        initial_value: Option<&SpecifiedValue>,\n    ) -> Result<(), PropertyRegistrationError> {\n        use crate::properties::CSSWideKeyword;\n        // If the value of the syntax descriptor is the universal syntax definition, then the\n        // initial-value descriptor is optional. If omitted, the initial value of the property is\n        // the guaranteed-invalid value.\n        if syntax.is_universal() && initial_value.is_none() {\n            return Ok(());\n        }\n\n        // Otherwise, if the value of the syntax descriptor is not the universal syntax definition,\n        // the following conditions must be met for the @property rule to be valid:\n\n        // The initial-value descriptor must be present.\n        let Some(initial) = initial_value else {\n            return Err(PropertyRegistrationError::NoInitialValue);\n        };\n\n        // A value that references the environment or other variables is not computationally\n        // independent.\n        if initial.has_references() {\n            return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);\n        }\n\n        let mut input = ParserInput::new(initial.css_text());\n        let mut input = Parser::new(&mut input);\n        input.skip_whitespace();\n\n        // The initial-value cannot include CSS-wide keywords.\n        if input.try_parse(CSSWideKeyword::parse).is_ok() {\n            return Err(PropertyRegistrationError::InitialValueNotComputationallyIndependent);\n        }\n\n        match SpecifiedRegisteredValue::parse(\n            &mut input,\n            syntax,\n            &initial.url_data,\n            None,\n            AllowComputationallyDependent::No,\n            /* attr_taint */ Default::default(),\n        ) {\n            Ok(_) => {},\n            Err(_) => return Err(PropertyRegistrationError::InvalidInitialValue),\n        }\n\n        Ok(())\n    }\n}\n\nimpl ToCssWithGuard for PropertyRegistration {\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#serialize-a-csspropertyrule>\n    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@property \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" { \")?;\n        self.descriptors.to_css(&mut CssWriter::new(dest))?;\n        dest.write_char('}')\n    }\n}\n\nimpl ToShmem for PropertyRegistration {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        Err(String::from(\n            \"ToShmem failed for PropertyRule: cannot handle @property rules\",\n        ))\n    }\n}\n\n/// A custom property name wrapper that includes the `--` prefix in its serialization\n#[derive(Clone, Debug, PartialEq, MallocSizeOf)]\npub struct PropertyRuleName(pub CustomPropertyName);\n\nimpl ToCss for PropertyRuleName {\n    fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {\n        dest.write_str(\"--\")?;\n        serialize_atom_name(&self.0, dest)\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor>\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub enum Inherits {\n    /// `true` value for the `inherits` descriptor\n    True,\n    /// `false` value for the `inherits` descriptor\n    False,\n}\n\nimpl Parse for Inherits {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // FIXME(bug 1927012): Remove `return` from try_match_ident_ignore_ascii_case so the closure\n        // can be removed.\n        let result: Result<Inherits, ParseError> = (|| {\n            try_match_ident_ignore_ascii_case! { input,\n                \"true\" => Ok(Inherits::True),\n                \"false\" => Ok(Inherits::False),\n            }\n        })();\n        if let Err(err) = result {\n            Err(ParseError {\n                kind: ParseErrorKind::Custom(StyleParseErrorKind::PropertyInheritsField(\n                    PropertyInheritsParseError::InvalidInherits,\n                )),\n                location: err.location,\n            })\n        } else {\n            result\n        }\n    }\n}\n\n/// Specifies the initial value of the custom property registration represented by the @property\n/// rule, controlling the property’s initial value.\n///\n/// The SpecifiedValue is wrapped in an Arc to avoid copying when using it.\npub type InitialValue = Arc<SpecifiedValue>;\n\nimpl Parse for InitialValue {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.skip_whitespace();\n        Ok(Arc::new(SpecifiedValue::parse(\n            input,\n            Some(&context.namespaces.prefixes),\n            &context.url_data,\n        )?))\n    }\n}\n\nimpl Descriptors {\n    /// Returns descriptors for an unregistered property.\n    #[inline]\n    pub fn unregistered() -> &'static Self {\n        static UNREGISTERED: Descriptors = Descriptors {\n            inherits: Some(Inherits::True),\n            syntax: Some(SyntaxDescriptor::universal()),\n            initial_value: None,\n        };\n        &UNREGISTERED\n    }\n\n    /// Whether this property inherits.\n    pub fn inherits(&self) -> bool {\n        self.inherits != Some(Inherits::False)\n    }\n\n    /// Whether this property uses universal syntax.\n    pub fn is_universal(&self) -> bool {\n        self.syntax.as_ref().map_or(true, |s| s.is_universal())\n    }\n}\n"
  },
  {
    "path": "style/properties_and_values/syntax/ascii.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n/// Trims ASCII whitespace characters from a slice, and returns the trimmed input.\npub fn trim_ascii_whitespace(input: &str) -> &str {\n    if input.is_empty() {\n        return input;\n    }\n\n    let mut start = 0;\n    {\n        let mut iter = input.as_bytes().iter();\n        loop {\n            let byte = match iter.next() {\n                Some(b) => b,\n                None => return \"\",\n            };\n\n            if !byte.is_ascii_whitespace() {\n                break;\n            }\n            start += 1;\n        }\n    }\n\n    let mut end = input.len();\n    assert!(start < end);\n    {\n        let mut iter = input.as_bytes()[start..].iter().rev();\n        loop {\n            let byte = match iter.next() {\n                Some(b) => b,\n                None => {\n                    debug_assert!(false, \"We should have caught this in the loop above!\");\n                    return \"\";\n                },\n            };\n\n            if !byte.is_ascii_whitespace() {\n                break;\n            }\n            end -= 1;\n        }\n    }\n\n    &input[start..end]\n}\n\n#[test]\nfn trim_ascii_whitespace_test() {\n    fn test(i: &str, o: &str) {\n        assert_eq!(trim_ascii_whitespace(i), o)\n    }\n\n    test(\"\", \"\");\n    test(\" \", \"\");\n    test(\" a b c \", \"a b c\");\n    test(\" \\t \\t \\ta b c \\t \\t \\t \\t\", \"a b c\");\n}\n"
  },
  {
    "path": "style/properties_and_values/syntax/data_type.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Used for parsing and serializing component names from the syntax string.\n\nuse super::{Component, ComponentName, Multiplier};\nuse crate::derives::*;\nuse std::fmt::{self, Debug, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// Some types (lengths and colors) depend on other properties to resolve correctly.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, MallocSizeOf, ToShmem)]\npub struct DependentDataTypes(u8);\nbitflags! {\n    impl DependentDataTypes: u8 {\n        /// <length> values depend on font-size/line-height/zoom...\n        const LENGTH = 1 << 0;\n        /// <color> values depend on color-scheme, etc..\n        const COLOR= 1 << 1;\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#supported-names>\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub enum DataType {\n    /// Any valid `<length>` value\n    Length,\n    /// `<number>` values\n    Number,\n    /// Any valid <percentage> value\n    Percentage,\n    /// Any valid `<length>` or `<percentage>` value, any valid `<calc()>` expression combining\n    /// `<length>` and `<percentage>` components.\n    LengthPercentage,\n    /// Any valid `<color>` value\n    Color,\n    /// Any valid `<image>` value\n    Image,\n    /// Any valid `<url>` value\n    Url,\n    /// Any valid `<integer>` value\n    Integer,\n    /// Any valid `<angle>` value\n    Angle,\n    /// Any valid `<time>` value\n    Time,\n    /// Any valid `<resolution>` value\n    Resolution,\n    /// Any valid `<transform-function>` value\n    TransformFunction,\n    /// Any valid `<custom-ident>` value\n    CustomIdent,\n    /// A list of valid `<transform-function>` values. Note that \"<transform-list>\" is a pre-multiplied\n    /// data type name equivalent to \"<transform-function>+\"\n    TransformList,\n    /// Any valid `<string>` value\n    ///\n    /// <https://github.com/w3c/css-houdini-drafts/issues/1103>\n    String,\n}\n\nimpl DataType {\n    /// Converts a component name from a pre-multiplied data type to its un-pre-multiplied equivalent.\n    ///\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#pre-multiplied-data-type-name>\n    pub fn unpremultiply(&self) -> Option<Component> {\n        match *self {\n            DataType::TransformList => Some(Component {\n                name: ComponentName::DataType(DataType::TransformFunction),\n                multiplier: Some(Multiplier::Space),\n            }),\n            _ => None,\n        }\n    }\n\n    /// Parses a syntax component name.\n    pub fn from_str(ty: &str) -> Option<Self> {\n        Some(match ty.as_bytes() {\n            b\"length\" => DataType::Length,\n            b\"number\" => DataType::Number,\n            b\"percentage\" => DataType::Percentage,\n            b\"length-percentage\" => DataType::LengthPercentage,\n            b\"color\" => DataType::Color,\n            b\"image\" => DataType::Image,\n            b\"url\" => DataType::Url,\n            b\"integer\" => DataType::Integer,\n            b\"angle\" => DataType::Angle,\n            b\"time\" => DataType::Time,\n            b\"resolution\" => DataType::Resolution,\n            b\"transform-function\" => DataType::TransformFunction,\n            b\"custom-ident\" => DataType::CustomIdent,\n            b\"transform-list\" => DataType::TransformList,\n            b\"string\" => DataType::String,\n            _ => return None,\n        })\n    }\n\n    /// Returns which kinds of dependent data types this property might contain.\n    pub fn dependent_types(&self) -> DependentDataTypes {\n        match self {\n            DataType::Length\n            | DataType::LengthPercentage\n            | DataType::TransformFunction\n            | DataType::TransformList => DependentDataTypes::LENGTH,\n            DataType::Color => DependentDataTypes::COLOR,\n            DataType::Number\n            | DataType::Percentage\n            | DataType::Image\n            | DataType::Url\n            | DataType::Integer\n            | DataType::Angle\n            | DataType::Time\n            | DataType::Resolution\n            | DataType::CustomIdent\n            | DataType::String => DependentDataTypes::empty(),\n        }\n    }\n}\n\nimpl ToCss for DataType {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_char('<')?;\n        dest.write_str(match *self {\n            DataType::Length => \"length\",\n            DataType::Number => \"number\",\n            DataType::Percentage => \"percentage\",\n            DataType::LengthPercentage => \"length-percentage\",\n            DataType::Color => \"color\",\n            DataType::Image => \"image\",\n            DataType::Url => \"url\",\n            DataType::Integer => \"integer\",\n            DataType::Angle => \"angle\",\n            DataType::Time => \"time\",\n            DataType::Resolution => \"resolution\",\n            DataType::TransformFunction => \"transform-function\",\n            DataType::CustomIdent => \"custom-ident\",\n            DataType::TransformList => \"transform-list\",\n            DataType::String => \"string\",\n        })?;\n        dest.write_char('>')\n    }\n}\n"
  },
  {
    "path": "style/properties_and_values/syntax/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Used for parsing and serializing the [`@property`] syntax string.\n//!\n//! <https://drafts.css-houdini.org/css-properties-values-api-1/#parsing-syntax>\n\nuse std::fmt::{self, Debug};\nuse std::{borrow::Cow, fmt::Write};\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::CustomIdent;\nuse cssparser::{Parser as CSSParser, ParserInput as CSSParserInput, Token};\nuse style_traits::{\n    CssWriter, ParseError as StyleParseError, PropertySyntaxParseError as ParseError,\n    StyleParseErrorKind, ToCss,\n};\n\nuse self::data_type::{DataType, DependentDataTypes};\n\nmod ascii;\npub mod data_type;\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#parsing-syntax>\n#[derive(Debug, Clone, Default, MallocSizeOf, PartialEq, ToShmem)]\npub struct Descriptor {\n    /// The parsed components, if any.\n    /// TODO: Could be a Box<[]> if that supported const construction.\n    pub components: Vec<Component>,\n    /// The specified css syntax, if any.\n    specified: Option<Box<str>>,\n}\n\nimpl Descriptor {\n    /// Returns the universal descriptor.\n    pub const fn universal() -> Self {\n        Self {\n            components: Vec::new(),\n            specified: None,\n        }\n    }\n\n    /// Returns whether this is the universal syntax descriptor.\n    #[inline]\n    pub fn is_universal(&self) -> bool {\n        self.components.is_empty()\n    }\n\n    /// Returns the specified string, if any.\n    #[inline]\n    pub fn specified_string(&self) -> Option<&str> {\n        self.specified.as_deref()\n    }\n\n    /// Parse a syntax descriptor from a stream of tokens\n    /// https://drafts.csswg.org/css-values-5/#typedef-syntax\n    #[inline]\n    pub fn from_css_parser<'i>(input: &mut CSSParser<'i, '_>) -> Result<Self, StyleParseError<'i>> {\n        let mut components = vec![];\n\n        if input.try_parse(|i| i.expect_delim('*')).is_ok() {\n            return Ok(Self::universal());\n        }\n\n        // Parse <syntax-string> if given.\n        if let Ok(syntax_string) = input.try_parse(|i| i.expect_string_cloned()) {\n            return Self::from_str(syntax_string.as_ref(), /* save_specified = */ true).or_else(\n                |err| Err(input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))),\n            );\n        }\n\n        loop {\n            let name = Self::try_parse_component_name(input).map_err(|err| {\n                input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err))\n            })?;\n\n            let multiplier = if name.is_pre_multiplied() {\n                None\n            } else {\n                Self::try_parse_multiplier(input)\n            };\n\n            let component = Component { multiplier, name };\n            components.push(component);\n            let Ok(delim) = input.next() else { break };\n\n            if delim != &Token::Delim('|') {\n                return Err(\n                    input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(\n                        ParseError::ExpectedPipeBetweenComponents,\n                    )),\n                );\n            }\n        }\n\n        Ok(Self {\n            components,\n            specified: None,\n        })\n    }\n\n    fn try_parse_multiplier<'i>(input: &mut CSSParser<'i, '_>) -> Option<Multiplier> {\n        input\n            .try_parse(|input| {\n                let next = input.next().map_err(|_| ())?;\n                match next {\n                    Token::Delim('+') => Ok(Multiplier::Space),\n                    Token::Delim('#') => Ok(Multiplier::Comma),\n                    _ => Err(()),\n                }\n            })\n            .ok()\n    }\n\n    fn try_parse_component_name<'i>(\n        input: &mut CSSParser<'i, '_>,\n    ) -> Result<ComponentName, ParseError> {\n        if input.try_parse(|input| input.expect_delim('<')).is_ok() {\n            let name = Self::parse_component_data_type_name(input)?;\n            input\n                .expect_delim('>')\n                .map_err(|_| ParseError::UnclosedDataTypeName)?;\n            Ok(ComponentName::DataType(name))\n        } else {\n            input.try_parse(|input| {\n                let name = CustomIdent::parse(input, &[]).map_err(|_| ParseError::InvalidName)?;\n                Ok(ComponentName::Ident(name))\n            })\n        }\n    }\n\n    fn parse_component_data_type_name<'i>(\n        input: &mut CSSParser<'i, '_>,\n    ) -> Result<DataType, ParseError> {\n        input\n            .expect_ident()\n            .ok()\n            .and_then(|n| DataType::from_str(n))\n            .ok_or(ParseError::UnknownDataTypeName)\n    }\n\n    /// Parse a syntax descriptor.\n    /// https://drafts.css-houdini.org/css-properties-values-api-1/#consume-a-syntax-definition\n    pub fn from_str(css: &str, save_specified: bool) -> Result<Self, ParseError> {\n        // 1. Strip leading and trailing ASCII whitespace from string.\n        let input = ascii::trim_ascii_whitespace(css);\n\n        // 2. If string's length is 0, return failure.\n        if input.is_empty() {\n            return Err(ParseError::EmptyInput);\n        }\n\n        let specified = if save_specified {\n            Some(Box::from(css))\n        } else {\n            None\n        };\n\n        // 3. If string's length is 1, and the only code point in string is U+002A\n        //    ASTERISK (*), return the universal syntax descriptor.\n        if input.len() == 1 && input.as_bytes()[0] == b'*' {\n            return Ok(Self {\n                components: Default::default(),\n                specified,\n            });\n        }\n\n        // 4. Let stream be an input stream created from the code points of string,\n        //    preprocessed as specified in [css-syntax-3]. Let descriptor be an\n        //    initially empty list of syntax components.\n        //\n        // NOTE(emilio): Instead of preprocessing we cheat and treat new-lines and\n        // nulls in the parser specially.\n        let mut components = vec![];\n        {\n            let mut input = Parser::new(input, &mut components);\n            // 5. Repeatedly consume the next input code point from stream.\n            input.parse()?;\n        }\n        Ok(Self {\n            components,\n            specified,\n        })\n    }\n\n    /// Returns the dependent types this syntax might contain.\n    pub fn dependent_types(&self) -> DependentDataTypes {\n        let mut types = DependentDataTypes::empty();\n        for component in self.components.iter() {\n            let t = match &component.name {\n                ComponentName::DataType(ref t) => t,\n                ComponentName::Ident(_) => continue,\n            };\n            types.insert(t.dependent_types());\n        }\n        types\n    }\n}\n\nimpl ToCss for Descriptor {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if let Some(ref specified) = self.specified {\n            return specified.to_css(dest);\n        }\n\n        if self.is_universal() {\n            return dest.write_char('*');\n        }\n\n        let mut first = true;\n        for component in &*self.components {\n            if !first {\n                dest.write_str(\" | \")?;\n            }\n            component.to_css(dest)?;\n            first = false;\n        }\n\n        Ok(())\n    }\n}\n\nimpl Parse for Descriptor {\n    /// Parse a syntax descriptor.\n    fn parse<'i>(\n        _: &ParserContext,\n        parser: &mut CSSParser<'i, '_>,\n    ) -> Result<Self, StyleParseError<'i>> {\n        let input = parser.expect_string()?;\n        Descriptor::from_str(input.as_ref(), /* save_specified = */ true)\n            .map_err(|err| parser.new_custom_error(StyleParseErrorKind::PropertySyntaxField(err)))\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#multipliers>\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\npub enum Multiplier {\n    /// Indicates a space-separated list.\n    Space,\n    /// Indicates a comma-separated list.\n    Comma,\n}\n\nimpl ToCss for Multiplier {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_char(match *self {\n            Multiplier::Space => '+',\n            Multiplier::Comma => '#',\n        })\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component>\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct Component {\n    name: ComponentName,\n    multiplier: Option<Multiplier>,\n}\n\nimpl Component {\n    /// Returns the component's name.\n    #[inline]\n    pub fn name(&self) -> &ComponentName {\n        &self.name\n    }\n\n    /// Returns the component's multiplier, if one exists.\n    #[inline]\n    pub fn multiplier(&self) -> Option<Multiplier> {\n        self.multiplier\n    }\n\n    /// If the component is premultiplied, return the un-premultiplied component.\n    #[inline]\n    pub fn unpremultiplied(&self) -> Cow<'_, Self> {\n        match self.name.unpremultiply() {\n            Some(component) => {\n                debug_assert!(\n                    self.multiplier.is_none(),\n                    \"Shouldn't have parsed a multiplier for a pre-multiplied data type name\",\n                );\n                Cow::Owned(component)\n            },\n            None => Cow::Borrowed(self),\n        }\n    }\n}\n\nimpl ToCss for Component {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.name().to_css(dest)?;\n        self.multiplier().to_css(dest)\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-properties-values-api-1/#syntax-component-name>\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub enum ComponentName {\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#data-type-name>\n    DataType(DataType),\n    /// <https://drafts.csswg.org/css-values-4/#custom-idents>\n    Ident(CustomIdent),\n}\n\nimpl ComponentName {\n    fn unpremultiply(&self) -> Option<Component> {\n        match *self {\n            ComponentName::DataType(ref t) => t.unpremultiply(),\n            ComponentName::Ident(..) => None,\n        }\n    }\n\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#pre-multiplied-data-type-name>\n    fn is_pre_multiplied(&self) -> bool {\n        self.unpremultiply().is_some()\n    }\n}\n\nstruct Parser<'a> {\n    input: &'a str,\n    position: usize,\n    output: &'a mut Vec<Component>,\n}\n\nimpl<'a> Parser<'a> {\n    fn new(input: &'a str, output: &'a mut Vec<Component>) -> Self {\n        Self {\n            input,\n            position: 0,\n            output,\n        }\n    }\n\n    fn peek(&self) -> Option<u8> {\n        self.input.as_bytes().get(self.position).cloned()\n    }\n\n    fn parse(&mut self) -> Result<(), ParseError> {\n        // 5. Repeatedly consume the next input code point from stream:\n        loop {\n            let component = self.parse_component()?;\n            self.output.push(component);\n            self.skip_whitespace();\n\n            let byte = match self.peek() {\n                None => return Ok(()),\n                Some(b) => b,\n            };\n\n            if byte != b'|' {\n                return Err(ParseError::ExpectedPipeBetweenComponents);\n            }\n\n            self.position += 1;\n        }\n    }\n\n    fn skip_whitespace(&mut self) {\n        loop {\n            match self.peek() {\n                Some(c) if c.is_ascii_whitespace() => self.position += 1,\n                _ => return,\n            }\n        }\n    }\n\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-data-type-name>\n    fn parse_data_type_name(&mut self) -> Result<DataType, ParseError> {\n        let start = self.position;\n        loop {\n            let byte = match self.peek() {\n                Some(b) => b,\n                None => return Err(ParseError::UnclosedDataTypeName),\n            };\n            if byte != b'>' {\n                self.position += 1;\n                continue;\n            }\n            let ty = match DataType::from_str(&self.input[start..self.position]) {\n                Some(ty) => ty,\n                None => return Err(ParseError::UnknownDataTypeName),\n            };\n            self.position += 1;\n            return Ok(ty);\n        }\n    }\n\n    fn parse_name(&mut self) -> Result<ComponentName, ParseError> {\n        let b = match self.peek() {\n            Some(b) => b,\n            None => return Err(ParseError::UnexpectedEOF),\n        };\n\n        if b == b'<' {\n            self.position += 1;\n            return Ok(ComponentName::DataType(self.parse_data_type_name()?));\n        }\n\n        let input = &self.input[self.position..];\n        let mut input = CSSParserInput::new(input);\n        let mut input = CSSParser::new(&mut input);\n        let name = match CustomIdent::parse(&mut input, &[]) {\n            Ok(name) => name,\n            Err(_) => return Err(ParseError::InvalidName),\n        };\n        self.position += input.position().byte_index();\n        return Ok(ComponentName::Ident(name));\n    }\n\n    fn parse_multiplier(&mut self) -> Option<Multiplier> {\n        let multiplier = match self.peek()? {\n            b'+' => Multiplier::Space,\n            b'#' => Multiplier::Comma,\n            _ => return None,\n        };\n        self.position += 1;\n        Some(multiplier)\n    }\n\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-a-syntax-component>\n    fn parse_component(&mut self) -> Result<Component, ParseError> {\n        // Consume as much whitespace as possible from stream.\n        self.skip_whitespace();\n        let name = self.parse_name()?;\n        let multiplier = if name.is_pre_multiplied() {\n            None\n        } else {\n            self.parse_multiplier()\n        };\n        Ok(Component { name, multiplier })\n    }\n}\n"
  },
  {
    "path": "style/properties_and_values/value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parsing for registered custom properties.\n\nuse super::{\n    rule::Descriptors as PropertyDescriptors,\n    syntax::{\n        data_type::DataType, Component as SyntaxComponent, ComponentName, Descriptor, Multiplier,\n    },\n};\nuse crate::custom_properties::{AttrTaint, ComputedValue as ComputedPropertyValue};\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties;\nuse crate::properties::{CSSWideKeyword, CustomDeclarationValue};\nuse crate::stylesheets::{CssRuleType, Origin, UrlExtraData};\nuse crate::values::{\n    animated::{self, Animate, Procedure},\n    computed::{self, ToComputedValue},\n    specified, CustomIdent,\n};\nuse crate::{Namespace, Prefix};\nuse cssparser::{BasicParseErrorKind, ParseErrorKind, Parser as CSSParser, TokenSerializationType};\nuse rustc_hash::FxHashMap;\nuse selectors::matching::QuirksMode;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::fmt::{self, Write};\nuse style_traits::{\n    owned_str::OwnedStr, CssWriter, ParseError as StyleParseError, ParsingMode,\n    PropertySyntaxParseError, StyleParseErrorKind, ToCss,\n};\n\n/// A single component of the computed value.\npub type ComputedValueComponent = GenericValueComponent<\n    computed::Length,\n    computed::Number,\n    computed::Percentage,\n    computed::LengthPercentage,\n    computed::Color,\n    computed::Image,\n    computed::url::ComputedUrl,\n    computed::Integer,\n    computed::Angle,\n    computed::Time,\n    computed::Resolution,\n    computed::Transform,\n>;\n\n/// A single component of the specified value.\npub type SpecifiedValueComponent = GenericValueComponent<\n    specified::Length,\n    specified::Number,\n    specified::Percentage,\n    specified::LengthPercentage,\n    specified::Color,\n    specified::Image,\n    specified::url::SpecifiedUrl,\n    specified::Integer,\n    specified::Angle,\n    specified::Time,\n    specified::Resolution,\n    specified::Transform,\n>;\n\nimpl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>\n    GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>\n{\n    fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {\n        let first_token_type = match self {\n            Self::Length(_) | Self::Angle(_) | Self::Time(_) | Self::Resolution(_) => {\n                TokenSerializationType::Dimension\n            },\n            Self::Number(_) | Self::Integer(_) => TokenSerializationType::Number,\n            Self::Percentage(_) | Self::LengthPercentage(_) => TokenSerializationType::Percentage,\n            Self::Color(_)\n            | Self::Image(_)\n            | Self::Url(_)\n            | Self::TransformFunction(_)\n            | Self::TransformList(_) => TokenSerializationType::Function,\n            Self::CustomIdent(_) => TokenSerializationType::Ident,\n            Self::String(_) => TokenSerializationType::Other,\n        };\n        let last_token_type = if first_token_type == TokenSerializationType::Function {\n            TokenSerializationType::Other\n        } else {\n            first_token_type\n        };\n        (first_token_type, last_token_type)\n    }\n}\n\n/// A generic enum used for both specified value components and computed value components.\n#[derive(\n    Animate, Clone, ToCss, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem,\n)]\n#[animation(no_bound(Image, Url))]\npub enum GenericValueComponent<\n    Length,\n    Number,\n    Percentage,\n    LengthPercentage,\n    Color,\n    Image,\n    Url,\n    Integer,\n    Angle,\n    Time,\n    Resolution,\n    TransformFunction,\n> {\n    /// A <length> value\n    Length(Length),\n    /// A <number> value\n    Number(Number),\n    /// A <percentage> value\n    Percentage(Percentage),\n    /// A <length-percentage> value\n    LengthPercentage(LengthPercentage),\n    /// A <color> value\n    Color(Color),\n    /// An <image> value\n    #[animation(error)]\n    Image(Image),\n    /// A <url> value\n    #[animation(error)]\n    Url(Url),\n    /// An <integer> value\n    Integer(Integer),\n    /// An <angle> value\n    Angle(Angle),\n    /// A <time> value\n    Time(Time),\n    /// A <resolution> value\n    Resolution(Resolution),\n    /// A <transform-function> value\n    /// TODO(bug 1884606): <transform-function> `none` should not interpolate.\n    TransformFunction(TransformFunction),\n    /// A <custom-ident> value\n    #[animation(error)]\n    CustomIdent(CustomIdent),\n    /// A <transform-list> value, equivalent to <transform-function>+\n    /// TODO(bug 1884606): <transform-list> `none` should not interpolate.\n    TransformList(ComponentList<Self>),\n    /// A <string> value\n    #[animation(error)]\n    String(OwnedStr),\n}\n\n/// A list of component values, including the list's multiplier.\n#[derive(Clone, ToComputedValue, ToResolvedValue, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct ComponentList<Component> {\n    /// Multiplier\n    pub multiplier: Multiplier,\n    /// The list of components contained.\n    pub components: crate::OwnedSlice<Component>,\n}\n\nimpl<Component: Animate> Animate for ComponentList<Component> {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.multiplier != other.multiplier {\n            return Err(());\n        }\n        let components = animated::lists::by_computed_value::animate(\n            &self.components,\n            &other.components,\n            procedure,\n        )?;\n        Ok(Self {\n            multiplier: self.multiplier,\n            components,\n        })\n    }\n}\n\nimpl<Component: ToCss> ToCss for ComponentList<Component> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let mut iter = self.components.iter();\n        let Some(first) = iter.next() else {\n            return Ok(());\n        };\n        first.to_css(dest)?;\n\n        // The separator implied by the multiplier for this list.\n        let separator = match self.multiplier {\n            // <https://drafts.csswg.org/cssom-1/#serialize-a-whitespace-separated-list>\n            Multiplier::Space => \" \",\n            // <https://drafts.csswg.org/cssom-1/#serialize-a-comma-separated-list>\n            Multiplier::Comma => \", \",\n        };\n        for component in iter {\n            dest.write_str(separator)?;\n            component.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// A struct for a single specified registered custom property value that includes its original URL\n/// data so the value can be uncomputed later.\n#[derive(Clone, Debug, MallocSizeOf, ToCss, ToComputedValue, ToResolvedValue, ToShmem)]\npub struct Value<Component> {\n    /// The registered custom property value.\n    pub(crate) v: ValueInner<Component>,\n    /// The URL data of the registered custom property from before it was computed. This is\n    /// necessary to uncompute registered custom properties.\n    #[css(skip)]\n    url_data: UrlExtraData,\n    /// Flag indicating whether this value is tainted by an attr().\n    #[css(skip)]\n    pub attr_tainted: bool,\n}\n\nimpl<Component: PartialEq> PartialEq for Value<Component> {\n    // Ignore the url_data field when comparing values for equality.\n    fn eq(&self, other: &Self) -> bool {\n        self.v == other.v\n    }\n}\n\nimpl<Component: Animate> Animate for Value<Component> {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let v = self.v.animate(&other.v, procedure)?;\n        Ok(Value {\n            v,\n            url_data: self.url_data.clone(),\n            attr_tainted: self.attr_tainted,\n        })\n    }\n}\n\nimpl<Component> Value<Component> {\n    /// Creates a new registered custom property value.\n    pub fn new(v: ValueInner<Component>, url_data: UrlExtraData) -> Self {\n        Self {\n            v,\n            url_data,\n            attr_tainted: Default::default(),\n        }\n    }\n\n    /// Creates a new registered custom property value presumed to have universal syntax.\n    pub fn universal(var: Arc<ComputedPropertyValue>) -> Self {\n        let attr_tainted = var.is_attr_tainted();\n        let url_data = var.url_data.clone();\n        let v = ValueInner::Universal(var);\n        Self {\n            v,\n            url_data,\n            attr_tainted,\n        }\n    }\n}\n\nimpl<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>\n    Value<GenericValueComponent<L, N, P, LP, C, Image, U, Integer, A, T, R, Transform>>\nwhere\n    Self: ToCss,\n{\n    fn serialization_types(&self) -> (TokenSerializationType, TokenSerializationType) {\n        match &self.v {\n            ValueInner::Component(component) => component.serialization_types(),\n            ValueInner::Universal(_) => unreachable!(),\n            ValueInner::List(list) => list\n                .components\n                .first()\n                .map_or(Default::default(), |f| f.serialization_types()),\n        }\n    }\n\n    /// Convert to an untyped variable value.\n    pub fn to_variable_value(&self) -> ComputedPropertyValue {\n        if let ValueInner::Universal(ref value) = self.v {\n            return (**value).clone();\n        }\n        let serialization_types = self.serialization_types();\n        ComputedPropertyValue::new(\n            self.to_css_string(),\n            &self.url_data,\n            serialization_types.0,\n            serialization_types.1,\n        )\n    }\n}\n\n/// A specified registered custom property value.\n#[derive(\n    Animate, ToComputedValue, ToResolvedValue, ToCss, Clone, Debug, MallocSizeOf, PartialEq, ToShmem,\n)]\npub enum ValueInner<Component> {\n    /// A single specified component value whose syntax descriptor component did not have a\n    /// multiplier.\n    Component(Component),\n    /// A specified value whose syntax descriptor was the universal syntax definition.\n    #[animation(error)]\n    Universal(#[ignore_malloc_size_of = \"Arc\"] Arc<ComputedPropertyValue>),\n    /// A list of specified component values whose syntax descriptor component had a multiplier.\n    List(#[animation(field_bound)] ComponentList<Component>),\n}\n\n/// Specified custom property value.\npub type SpecifiedValue = Value<SpecifiedValueComponent>;\n\n/// Computed custom property value.\npub type ComputedValue = Value<ComputedValueComponent>;\n\nimpl SpecifiedValue {\n    /// Convert a registered custom property to a Computed custom property value, given input and a\n    /// property registration.\n    pub fn compute<'i, 't>(\n        input: &mut CSSParser<'i, 't>,\n        registration: &PropertyDescriptors,\n        namespaces: Option<&FxHashMap<Prefix, Namespace>>,\n        url_data: &UrlExtraData,\n        context: &computed::Context,\n        allow_computationally_dependent: AllowComputationallyDependent,\n        attr_taint: AttrTaint,\n    ) -> Result<ComputedValue, ()> {\n        debug_assert!(!registration.is_universal(), \"Shouldn't be needed\");\n        let Some(ref syntax) = registration.syntax else {\n            return Err(());\n        };\n        let Ok(value) = Self::parse(\n            input,\n            syntax,\n            url_data,\n            namespaces,\n            allow_computationally_dependent,\n            attr_taint,\n        ) else {\n            return Err(());\n        };\n\n        Ok(value.to_computed_value(context))\n    }\n\n    /// Parse and validate a registered custom property value according to its syntax descriptor,\n    /// and check for computational independence.\n    pub fn parse<'i, 't>(\n        mut input: &mut CSSParser<'i, 't>,\n        syntax: &Descriptor,\n        url_data: &UrlExtraData,\n        namespaces: Option<&FxHashMap<Prefix, Namespace>>,\n        allow_computationally_dependent: AllowComputationallyDependent,\n        attr_taint: AttrTaint,\n    ) -> Result<Self, StyleParseError<'i>> {\n        if syntax.is_universal() {\n            let parsed = ComputedPropertyValue::parse(&mut input, namespaces, url_data)?;\n            return Ok(Self::new(\n                ValueInner::Universal(Arc::new(parsed)),\n                url_data.clone(),\n            ));\n        }\n\n        let mut values = SmallComponentVec::new();\n        let mut multiplier = None;\n        {\n            let mut parser = Parser::new(syntax, &mut values, &mut multiplier);\n            parser.parse(\n                &mut input,\n                url_data,\n                allow_computationally_dependent,\n                attr_taint,\n            )?;\n        }\n        let v = if let Some(multiplier) = multiplier {\n            ValueInner::List(ComponentList {\n                multiplier,\n                components: values.to_vec().into(),\n            })\n        } else {\n            ValueInner::Component(values[0].clone())\n        };\n        Ok(Self::new(v, url_data.clone()))\n    }\n}\n\nimpl ComputedValue {\n    fn to_declared_value(&self) -> properties::CustomDeclarationValue {\n        if let ValueInner::Universal(ref var) = self.v {\n            return properties::CustomDeclarationValue::Unparsed(Arc::clone(var));\n        }\n        properties::CustomDeclarationValue::Parsed(Arc::new(ToComputedValue::from_computed_value(\n            self,\n        )))\n    }\n\n    /// Returns the contained variable value if it exists, otherwise `None`.\n    pub fn as_universal(&self) -> Option<&Arc<ComputedPropertyValue>> {\n        if let ValueInner::Universal(ref var) = self.v {\n            Some(var)\n        } else {\n            None\n        }\n    }\n}\n\n/// Whether the computed value parsing should allow computationaly dependent values like 3em or\n/// var(-foo).\n///\n/// https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent\npub enum AllowComputationallyDependent {\n    /// Only computationally independent values are allowed.\n    No,\n    /// Computationally independent and dependent values are allowed.\n    Yes,\n}\n\ntype SmallComponentVec = SmallVec<[SpecifiedValueComponent; 1]>;\n\nstruct Parser<'a> {\n    syntax: &'a Descriptor,\n    output: &'a mut SmallComponentVec,\n    output_multiplier: &'a mut Option<Multiplier>,\n}\n\nimpl<'a> Parser<'a> {\n    fn new(\n        syntax: &'a Descriptor,\n        output: &'a mut SmallComponentVec,\n        output_multiplier: &'a mut Option<Multiplier>,\n    ) -> Self {\n        Self {\n            syntax,\n            output,\n            output_multiplier,\n        }\n    }\n\n    fn parse<'i, 't>(\n        &mut self,\n        input: &mut CSSParser<'i, 't>,\n        url_data: &UrlExtraData,\n        allow_computationally_dependent: AllowComputationallyDependent,\n        attr_taint: AttrTaint,\n    ) -> Result<(), StyleParseError<'i>> {\n        use self::AllowComputationallyDependent::*;\n        let parsing_mode = match allow_computationally_dependent {\n            No => ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT,\n            Yes => ParsingMode::DEFAULT,\n        };\n        let ref context = ParserContext::new(\n            Origin::Author,\n            url_data,\n            Some(CssRuleType::Style),\n            parsing_mode,\n            QuirksMode::NoQuirks,\n            /* namespaces = */ Default::default(),\n            None,\n            None,\n            attr_taint,\n        );\n        for component in self.syntax.components.iter() {\n            let result = input.try_parse(|input| {\n                input.parse_entirely(|input| {\n                    Self::parse_value(context, input, &component.unpremultiplied())\n                })\n            });\n            let Ok(values) = result else { continue };\n            self.output.extend(values);\n            *self.output_multiplier = component.multiplier();\n            break;\n        }\n        if self.output.is_empty() {\n            return Err(input.new_error(BasicParseErrorKind::EndOfInput));\n        }\n        Ok(())\n    }\n\n    fn parse_value<'i, 't>(\n        context: &ParserContext,\n        input: &mut CSSParser<'i, 't>,\n        component: &SyntaxComponent,\n    ) -> Result<SmallComponentVec, StyleParseError<'i>> {\n        let mut values = SmallComponentVec::new();\n        values.push(Self::parse_component_without_multiplier(\n            context, input, component,\n        )?);\n\n        if let Some(multiplier) = component.multiplier() {\n            loop {\n                let result = Self::expect_multiplier(input, &multiplier);\n                if Self::expect_multiplier_yielded_eof_error(&result) {\n                    break;\n                }\n                result?;\n                values.push(Self::parse_component_without_multiplier(\n                    context, input, component,\n                )?);\n            }\n        }\n        Ok(values)\n    }\n\n    fn parse_component_without_multiplier<'i, 't>(\n        context: &ParserContext,\n        input: &mut CSSParser<'i, 't>,\n        component: &SyntaxComponent,\n    ) -> Result<SpecifiedValueComponent, StyleParseError<'i>> {\n        let data_type = match component.name() {\n            ComponentName::DataType(ty) => ty,\n            ComponentName::Ident(ref name) => {\n                let ident = CustomIdent::parse(input, &[])?;\n                if ident != *name {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                return Ok(SpecifiedValueComponent::CustomIdent(ident));\n            },\n        };\n\n        let value = match data_type {\n            DataType::Length => {\n                SpecifiedValueComponent::Length(specified::Length::parse(context, input)?)\n            },\n            DataType::Number => {\n                SpecifiedValueComponent::Number(specified::Number::parse(context, input)?)\n            },\n            DataType::Percentage => {\n                SpecifiedValueComponent::Percentage(specified::Percentage::parse(context, input)?)\n            },\n            DataType::LengthPercentage => SpecifiedValueComponent::LengthPercentage(\n                specified::LengthPercentage::parse(context, input)?,\n            ),\n            DataType::Color => {\n                SpecifiedValueComponent::Color(specified::Color::parse(context, input)?)\n            },\n            DataType::Image => {\n                SpecifiedValueComponent::Image(specified::Image::parse(context, input)?)\n            },\n            DataType::Url => {\n                SpecifiedValueComponent::Url(specified::url::SpecifiedUrl::parse(context, input)?)\n            },\n            DataType::Integer => {\n                SpecifiedValueComponent::Integer(specified::Integer::parse(context, input)?)\n            },\n            DataType::Angle => {\n                SpecifiedValueComponent::Angle(specified::Angle::parse(context, input)?)\n            },\n            DataType::Time => {\n                SpecifiedValueComponent::Time(specified::Time::parse(context, input)?)\n            },\n            DataType::Resolution => {\n                SpecifiedValueComponent::Resolution(specified::Resolution::parse(context, input)?)\n            },\n            DataType::TransformFunction => SpecifiedValueComponent::TransformFunction(\n                specified::Transform::parse(context, input)?,\n            ),\n            DataType::CustomIdent => {\n                let name = CustomIdent::parse(input, &[])?;\n                SpecifiedValueComponent::CustomIdent(name)\n            },\n            DataType::TransformList => {\n                let mut values = vec![];\n                let Some(multiplier) = component.unpremultiplied().multiplier() else {\n                    debug_assert!(false, \"Unpremultiplied <transform-list> had no multiplier?\");\n                    return Err(\n                        input.new_custom_error(StyleParseErrorKind::PropertySyntaxField(\n                            PropertySyntaxParseError::UnexpectedEOF,\n                        )),\n                    );\n                };\n                debug_assert_eq!(multiplier, Multiplier::Space);\n                loop {\n                    values.push(SpecifiedValueComponent::TransformFunction(\n                        specified::Transform::parse(context, input)?,\n                    ));\n                    let result = Self::expect_multiplier(input, &multiplier);\n                    if Self::expect_multiplier_yielded_eof_error(&result) {\n                        break;\n                    }\n                    result?;\n                }\n                let list = ComponentList {\n                    multiplier,\n                    components: values.into(),\n                };\n                SpecifiedValueComponent::TransformList(list)\n            },\n            DataType::String => {\n                let string = input.expect_string()?;\n                SpecifiedValueComponent::String(string.as_ref().to_owned().into())\n            },\n        };\n        Ok(value)\n    }\n\n    fn expect_multiplier_yielded_eof_error<'i>(result: &Result<(), StyleParseError<'i>>) -> bool {\n        matches!(\n            result,\n            Err(StyleParseError {\n                kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),\n                ..\n            })\n        )\n    }\n\n    fn expect_multiplier<'i, 't>(\n        input: &mut CSSParser<'i, 't>,\n        multiplier: &Multiplier,\n    ) -> Result<(), StyleParseError<'i>> {\n        match multiplier {\n            Multiplier::Space => {\n                input.expect_whitespace()?;\n                if input.is_exhausted() {\n                    // If there was trailing whitespace, do not interpret it as a multiplier\n                    return Err(input.new_error(BasicParseErrorKind::EndOfInput));\n                }\n                Ok(())\n            },\n            Multiplier::Comma => Ok(input.expect_comma()?),\n        }\n    }\n}\n\n/// An animated value for custom property.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq)]\npub struct CustomAnimatedValue {\n    /// The name of the custom property.\n    pub(crate) name: crate::custom_properties::Name,\n    /// The computed value of the custom property.\n    /// `None` represents the guaranteed-invalid value.\n    pub(crate) value: Option<ComputedValue>,\n}\n\nimpl Animate for CustomAnimatedValue {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.name != other.name {\n            return Err(());\n        }\n        let value = self.value.animate(&other.value, procedure)?;\n        Ok(Self {\n            name: self.name.clone(),\n            value,\n        })\n    }\n}\n\nimpl CustomAnimatedValue {\n    pub(crate) fn from_computed(\n        name: &crate::custom_properties::Name,\n        value: Option<&ComputedValue>,\n    ) -> Self {\n        Self {\n            name: name.clone(),\n            value: value.cloned(),\n        }\n    }\n\n    pub(crate) fn from_declaration(\n        declaration: &properties::CustomDeclaration,\n        context: &mut computed::Context,\n    ) -> Option<Self> {\n        let computed_value = match declaration.value {\n            properties::CustomDeclarationValue::Unparsed(ref value) => Some({\n                debug_assert!(\n                    context.builder.stylist.is_some(),\n                    \"Need a Stylist to get property registration!\"\n                );\n                let registration = context\n                    .builder\n                    .stylist\n                    .unwrap()\n                    .get_custom_property_registration(&declaration.name);\n                if registration.is_universal() {\n                    // FIXME: Do we need to perform substitution here somehow?\n                    ComputedValue::new(\n                        ValueInner::Universal(Arc::clone(value)),\n                        value.url_data.clone(),\n                    )\n                } else {\n                    let mut input = cssparser::ParserInput::new(&value.css);\n                    let mut input = CSSParser::new(&mut input);\n                    SpecifiedValue::compute(\n                        &mut input,\n                        registration,\n                        None,\n                        &value.url_data,\n                        context,\n                        AllowComputationallyDependent::Yes,\n                        /* attr_taint */ Default::default(),\n                    )\n                    .unwrap_or_else(|_| {\n                        ComputedValue::new(\n                            ValueInner::Universal(Arc::clone(value)),\n                            value.url_data.clone(),\n                        )\n                    })\n                }\n            }),\n            properties::CustomDeclarationValue::Parsed(ref v) => Some(v.to_computed_value(context)),\n            properties::CustomDeclarationValue::CSSWideKeyword(keyword) => {\n                let stylist = context.builder.stylist.unwrap();\n                let registration = stylist.get_custom_property_registration(&declaration.name);\n                match keyword {\n                    CSSWideKeyword::Initial => stylist\n                        .get_custom_property_initial_values()\n                        .get(registration, &declaration.name),\n                    CSSWideKeyword::Inherit => context\n                        .builder\n                        .inherited_custom_properties()\n                        .get(registration, &declaration.name),\n                    CSSWideKeyword::Unset => {\n                        if registration.inherits() {\n                            context\n                                .builder\n                                .inherited_custom_properties()\n                                .get(registration, &declaration.name)\n                        } else {\n                            stylist\n                                .get_custom_property_initial_values()\n                                .get(registration, &declaration.name)\n                        }\n                    },\n                    // FIXME(emilio, bug 1533327): I think revert (and\n                    // revert-layer) handling is not fine here, but what to\n                    // do instead?\n                    //\n                    // Seems we'd need the computed value as if it was\n                    // revert, somehow. Returning `None` seems fine for now...\n                    //\n                    // Note that once this is fixed, this method should be\n                    // able to return `Self` instead of Option<Self>`.\n                    CSSWideKeyword::Revert\n                    | CSSWideKeyword::RevertRule\n                    | CSSWideKeyword::RevertLayer => return None,\n                }\n                .cloned()\n            },\n        };\n        Some(Self {\n            name: declaration.name.clone(),\n            value: computed_value,\n        })\n    }\n\n    pub(crate) fn to_declaration(&self) -> properties::PropertyDeclaration {\n        properties::PropertyDeclaration::Custom(properties::CustomDeclaration {\n            name: self.name.clone(),\n            value: match &self.value {\n                Some(value) => value.to_declared_value(),\n                None => CustomDeclarationValue::CSSWideKeyword(CSSWideKeyword::Initial),\n            },\n        })\n    }\n}\n"
  },
  {
    "path": "style/queries/condition.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A query condition:\n//!\n//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition\n//! https://drafts.csswg.org/css-contain-3/#typedef-container-condition\n\nuse super::{FeatureFlags, FeatureType, QueryFeatureExpression, QueryStyleRange};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::QuirksMode;\nuse crate::custom_properties;\nuse crate::derives::*;\nuse crate::dom::AttributeTracker;\nuse crate::properties::CSSWideKeyword;\nuse crate::properties_and_values::rule::Descriptors as PropertyDescriptors;\nuse crate::properties_and_values::value::{\n    AllowComputationallyDependent, ComputedValue as ComputedRegisteredValue,\n    SpecifiedValue as SpecifiedRegisteredValue,\n};\nuse crate::stylesheets::{CssRuleType, CustomMediaEvaluator, Origin, UrlExtraData};\nuse crate::stylist::Stylist;\nuse crate::values::{computed, AtomString, DashedIdent};\nuse crate::{error_reporting::ContextualParseError, parser::Parse, parser::ParserContext};\nuse cssparser::{\n    match_ignore_ascii_case, parse_important, Parser, ParserInput, SourcePosition, Token,\n};\nuse selectors::kleene_value::KleeneValue;\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};\n\n/// A binary `and` or `or` operator.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\n#[allow(missing_docs)]\npub enum Operator {\n    And,\n    Or,\n}\n\n/// Whether to allow an `or` condition or not during parsing.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]\nenum AllowOr {\n    Yes,\n    No,\n}\n\n#[derive(Clone, Debug, PartialEq, ToShmem)]\nenum StyleFeatureValue {\n    Value(Option<Arc<custom_properties::SpecifiedValue>>),\n    Keyword(CSSWideKeyword),\n}\n\n/// Trait for query elements that parse a series of conditions separated by\n/// AND or OR operators, or prefixed with NOT.\n///\n/// This is used by both QueryCondition and StyleQuery as they support similar\n/// syntax for combining multiple conditions with a boolean operator.\ntrait OperationParser: Sized {\n    /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition or\n    /// https://drafts.csswg.org/mediaqueries-5/#typedef-media-condition-without-or\n    /// (depending on `allow_or`).\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n        allow_or: AllowOr,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n\n        if input.try_parse(|i| i.expect_ident_matching(\"not\")).is_ok() {\n            let inner_condition = Self::parse_in_parens(context, input, feature_type)?;\n            return Ok(Self::new_not(Box::new(inner_condition)));\n        }\n\n        let first_condition = Self::parse_in_parens(context, input, feature_type)?;\n        let operator = match input.try_parse(Operator::parse) {\n            Ok(op) => op,\n            Err(..) => return Ok(first_condition),\n        };\n\n        if allow_or == AllowOr::No && operator == Operator::Or {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let mut conditions = vec![];\n        conditions.push(first_condition);\n        conditions.push(Self::parse_in_parens(context, input, feature_type)?);\n\n        let delim = match operator {\n            Operator::And => \"and\",\n            Operator::Or => \"or\",\n        };\n\n        loop {\n            if input.try_parse(|i| i.expect_ident_matching(delim)).is_err() {\n                return Ok(Self::new_operation(conditions.into_boxed_slice(), operator));\n            }\n\n            conditions.push(Self::parse_in_parens(context, input, feature_type)?);\n        }\n    }\n\n    // Parse a condition in parentheses, or `<general-enclosed>`.\n    fn parse_in_parens<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>>;\n\n    // Helpers to create the appropriate enum variant of the implementing type:\n    // Create a Not result that encapsulates the `inner` condition.\n    fn new_not(inner: Box<Self>) -> Self;\n\n    // Create an Operation result with the given list of `conditions` using `operator`.\n    fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self;\n}\n\n/// https://drafts.csswg.org/css-conditional-5/#typedef-style-query\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub enum StyleQuery {\n    /// A negation of a condition.\n    Not(Box<StyleQuery>),\n    /// A set of joint operations.\n    Operation(Box<[StyleQuery]>, Operator),\n    /// A condition wrapped in parenthesis.\n    InParens(Box<StyleQuery>),\n    /// A feature query (`--foo: bar` or just `--foo`).\n    Feature(StyleFeature),\n    /// An unknown \"general-enclosed\" term.\n    GeneralEnclosed(String),\n}\n\nimpl ToCss for StyleQuery {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *self {\n            StyleQuery::Not(ref c) => {\n                dest.write_str(\"not \")?;\n                c.maybe_parenthesized(dest)\n            },\n            StyleQuery::Operation(ref list, op) => {\n                let mut iter = list.iter();\n                let item = iter.next().unwrap();\n                item.maybe_parenthesized(dest)?;\n                for item in iter {\n                    dest.write_char(' ')?;\n                    op.to_css(dest)?;\n                    dest.write_char(' ')?;\n                    item.maybe_parenthesized(dest)?;\n                }\n                Ok(())\n            },\n            StyleQuery::InParens(ref c) => match &**c {\n                StyleQuery::Feature(_) | StyleQuery::InParens(_) => {\n                    dest.write_char('(')?;\n                    c.to_css(dest)?;\n                    dest.write_char(')')\n                },\n                _ => c.to_css(dest),\n            },\n            StyleQuery::Feature(ref f) => f.to_css(dest),\n            StyleQuery::GeneralEnclosed(ref s) => dest.write_str(&s),\n        }\n    }\n}\n\nimpl StyleQuery {\n    // Helper for to_css when handling values within boolean operators:\n    // GeneralEnclosed includes its parens in the string, so we don't need to\n    // wrap the value with an additional set here.\n    fn maybe_parenthesized<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if let StyleQuery::GeneralEnclosed(ref s) = self {\n            dest.write_str(&s)\n        } else {\n            dest.write_char('(')?;\n            self.to_css(dest)?;\n            dest.write_char(')')\n        }\n    }\n\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        if !static_prefs::pref!(\"layout.css.style-queries.enabled\")\n            || feature_type != FeatureType::Container\n        {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        if let Ok(feature) = input.try_parse(|input| StyleFeature::parse(context, input)) {\n            return Ok(Self::Feature(feature));\n        }\n\n        let inner = Self::parse_internal(context, input, feature_type, AllowOr::Yes)?;\n        Ok(Self::InParens(Box::new(inner)))\n    }\n\n    fn parse_in_parenthesis_block<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Base case. Make sure to preserve this error as it's more generally\n        // relevant.\n        let feature_error = match input.try_parse(|input| StyleFeature::parse(context, input)) {\n            Ok(feature) => return Ok(Self::Feature(feature)),\n            Err(e) => e,\n        };\n\n        if let Ok(inner) = Self::parse(context, input, FeatureType::Container) {\n            return Ok(inner);\n        }\n\n        Err(feature_error)\n    }\n\n    fn try_parse_block<'i, T, F>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n        start: SourcePosition,\n        parse: F,\n    ) -> Option<T>\n    where\n        F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,\n    {\n        let nested = input.try_parse(|input| input.parse_nested_block(parse));\n        match nested {\n            Ok(nested) => Some(nested),\n            Err(e) => {\n                // We're about to swallow the error in a `<general-enclosed>`\n                // condition, so report it while we can.\n                let loc = e.location;\n                let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);\n                context.log_css_error(loc, error);\n                None\n            },\n        }\n    }\n\n    fn matches(\n        &self,\n        ctx: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        ctx.builder\n            .add_flags(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);\n        match *self {\n            StyleQuery::Feature(ref f) => f.matches(ctx, attribute_tracker),\n            StyleQuery::Not(ref c) => !c.matches(ctx, attribute_tracker),\n            StyleQuery::InParens(ref c) => c.matches(ctx, attribute_tracker),\n            StyleQuery::Operation(ref conditions, op) => {\n                debug_assert!(!conditions.is_empty(), \"We never create an empty op\");\n                match op {\n                    Operator::And => KleeneValue::any_false(conditions.iter(), |c| {\n                        c.matches(ctx, attribute_tracker)\n                    }),\n                    Operator::Or => {\n                        KleeneValue::any(conditions.iter(), |c| c.matches(ctx, attribute_tracker))\n                    },\n                }\n            },\n            StyleQuery::GeneralEnclosed(_) => KleeneValue::Unknown,\n        }\n    }\n}\n\nimpl OperationParser for StyleQuery {\n    fn parse_in_parens<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        assert!(feature_type == FeatureType::Container);\n        input.skip_whitespace();\n        let start = input.position();\n        let start_location = input.current_source_location();\n        match *input.next()? {\n            Token::ParenthesisBlock => {\n                if let Some(nested) = Self::try_parse_block(context, input, start, |i| {\n                    Self::parse_in_parenthesis_block(context, i)\n                }) {\n                    return Ok(nested);\n                }\n                // Accept <ident>: <any-value> as a GeneralEnclosed (which evaluates\n                // to false, but does not invalidate the query as a whole).\n                input.parse_nested_block(|i| {\n                    i.expect_ident()?;\n                    i.expect_colon()?;\n                    consume_any_value(i)\n                })?;\n                Ok(Self::GeneralEnclosed(input.slice_from(start).to_owned()))\n            },\n            ref t => return Err(start_location.new_unexpected_token_error(t.clone())),\n        }\n    }\n\n    fn new_not(inner: Box<Self>) -> Self {\n        Self::Not(inner)\n    }\n\n    fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {\n        Self::Operation(conditions, operator)\n    }\n}\n\n/// A style query feature:\n/// https://drafts.csswg.org/css-conditional-5/#typedef-style-feature\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub enum StyleFeature {\n    /// A property name and optional value to match.\n    Plain(StyleFeaturePlain),\n    /// A style query range expression.\n    Range(QueryStyleRange),\n}\n\nimpl StyleFeature {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(range) = input.try_parse(|i| QueryStyleRange::parse(context, i)) {\n            return Ok(Self::Range(range));\n        }\n\n        Ok(Self::Plain(StyleFeaturePlain::parse(context, input)?))\n    }\n\n    fn matches(\n        &self,\n        ctx: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        match self {\n            Self::Plain(plain) => plain.matches(ctx, attribute_tracker),\n            Self::Range(range) => range.evaluate(ctx, attribute_tracker),\n        }\n    }\n}\n\n/// A style feature consisting of a custom property name and (optionally) value.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct StyleFeaturePlain {\n    name: custom_properties::Name,\n    #[ignore_malloc_size_of = \"StyleFeatureValue has an Arc variant\"]\n    value: StyleFeatureValue,\n}\n\nimpl ToCss for StyleFeaturePlain {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        dest.write_str(\"--\")?;\n        crate::values::serialize_atom_identifier(&self.name, dest)?;\n        match self.value {\n            StyleFeatureValue::Keyword(k) => {\n                dest.write_str(\": \")?;\n                k.to_css(dest)?;\n            },\n            StyleFeatureValue::Value(Some(ref v)) => {\n                dest.write_str(\": \")?;\n                v.to_css(dest)?;\n            },\n            StyleFeatureValue::Value(None) => (),\n        }\n        Ok(())\n    }\n}\n\nimpl StyleFeaturePlain {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let ident = input.expect_ident()?;\n        // TODO(emilio): Maybe support non-custom properties?\n        let name = match custom_properties::parse_name(ident.as_ref()) {\n            Ok(name) => custom_properties::Name::from(name),\n            Err(()) => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        };\n        let value = if input.try_parse(|i| i.expect_colon()).is_ok() {\n            input.skip_whitespace();\n            if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {\n                StyleFeatureValue::Keyword(keyword)\n            } else {\n                let value = custom_properties::SpecifiedValue::parse(\n                    input,\n                    Some(&context.namespaces.prefixes),\n                    &context.url_data,\n                )?;\n                // `!important` is allowed (but ignored) after the value.\n                let _ = input.try_parse(parse_important);\n                StyleFeatureValue::Value(Some(Arc::new(value)))\n            }\n        } else {\n            StyleFeatureValue::Value(None)\n        };\n        Ok(Self { name, value })\n    }\n\n    // Substitute custom-property references in `value`, then re-parse and compute it,\n    // and compare against `current_value`.\n    fn substitute_and_compare(\n        value: &Arc<custom_properties::SpecifiedValue>,\n        registration: &PropertyDescriptors,\n        stylist: &Stylist,\n        ctx: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n        current_value: Option<&ComputedRegisteredValue>,\n    ) -> bool {\n        let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(\n            Some(ctx.inherited_custom_properties().clone()),\n            None,\n        );\n        let custom_properties::SubstitutionResult { css, attr_taint } =\n            match custom_properties::substitute(\n                &value,\n                &substitution_functions,\n                stylist,\n                ctx,\n                attribute_tracker,\n            ) {\n                Ok(sub) => sub,\n                Err(_) => return current_value.is_none(),\n            };\n        if registration.is_universal() {\n            return match current_value {\n                Some(v) => v.as_universal().is_some_and(|v| v.css == css),\n                None => css.is_empty(),\n            };\n        }\n        let mut input = cssparser::ParserInput::new(&css);\n        let mut parser = Parser::new(&mut input);\n        let computed = SpecifiedRegisteredValue::compute(\n            &mut parser,\n            registration,\n            None,\n            &value.url_data,\n            ctx,\n            AllowComputationallyDependent::Yes,\n            attr_taint,\n        )\n        .ok();\n        computed.as_ref() == current_value\n    }\n\n    fn matches(\n        &self,\n        ctx: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        // FIXME(emilio): Confirm this is the right style to query.\n        let stylist = ctx\n            .builder\n            .stylist\n            .expect(\"container queries should have a stylist around\");\n        let registration = stylist.get_custom_property_registration(&self.name);\n        let current_value = ctx\n            .inherited_custom_properties()\n            .get(registration, &self.name);\n        KleeneValue::from(match self.value {\n            StyleFeatureValue::Value(Some(ref v)) => {\n                if ctx.container_info.is_none() {\n                    // If no container, custom props are guaranteed-unknown.\n                    false\n                } else if v.has_references() {\n                    // If there are --var() references in the query value,\n                    // try to substitute them before comparing to current.\n                    Self::substitute_and_compare(\n                        v,\n                        registration,\n                        stylist,\n                        ctx,\n                        attribute_tracker,\n                        current_value,\n                    )\n                } else {\n                    custom_properties::compute_variable_value(&v, registration, ctx).as_ref()\n                        == current_value\n                }\n            },\n            StyleFeatureValue::Value(None) => current_value.is_some(),\n            StyleFeatureValue::Keyword(kw) => {\n                match kw {\n                    CSSWideKeyword::Unset => current_value.is_none(),\n                    CSSWideKeyword::Initial => {\n                        if let Some(initial) = &registration.initial_value {\n                            let v = custom_properties::compute_variable_value(\n                                &initial,\n                                registration,\n                                ctx,\n                            );\n                            v.as_ref() == current_value\n                        } else {\n                            current_value.is_none()\n                        }\n                    },\n                    CSSWideKeyword::Inherit => {\n                        if let Some(inherited) = ctx\n                            .container_info\n                            .as_ref()\n                            .expect(\"queries should provide container info\")\n                            .inherited_style()\n                        {\n                            inherited.custom_properties().get(registration, &self.name)\n                                == current_value\n                        } else {\n                            false\n                        }\n                    },\n                    // Cascade-dependent keywords, such as revert and revert-layer,\n                    // are invalid as values in a style feature, and cause the\n                    // container style query to be false.\n                    // https://drafts.csswg.org/css-conditional-5/#evaluate-a-style-range\n                    CSSWideKeyword::Revert\n                    | CSSWideKeyword::RevertLayer\n                    | CSSWideKeyword::RevertRule => false,\n                }\n            },\n        })\n    }\n}\n\n/// A boolean value for a pref query.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Eq,\n    Parse,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToShmem,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum BoolValue {\n    False,\n    True,\n}\n\n/// Simple values we support for -moz-pref(). We don't want to deal with calc() and other\n/// shenanigans for now.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum MozPrefFeatureValue<I> {\n    /// No pref value, implicitly bool, but also used to represent missing prefs.\n    #[css(skip)]\n    None,\n    /// A bool value.\n    Boolean(BoolValue),\n    /// An integer value, useful for int prefs.\n    Integer(I),\n    /// A string pref value.\n    String(crate::values::AtomString),\n}\n\ntype SpecifiedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::specified::Integer>;\n/// The computed -moz-pref() value.\npub type ComputedMozPrefFeatureValue = MozPrefFeatureValue<crate::values::computed::Integer>;\n\n/// A custom -moz-pref(<name>, <value>) query feature.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct MozPrefFeature {\n    name: crate::values::AtomString,\n    value: SpecifiedMozPrefFeatureValue,\n}\n\nimpl MozPrefFeature {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::parser::Parse;\n        if !context.chrome_rules_enabled() || feature_type != FeatureType::Media {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let name = AtomString::parse(context, input)?;\n        let value = if input.try_parse(|i| i.expect_comma()).is_ok() {\n            SpecifiedMozPrefFeatureValue::parse(context, input)?\n        } else {\n            SpecifiedMozPrefFeatureValue::None\n        };\n        Ok(Self { name, value })\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn matches(&self, ctx: &computed::Context) -> KleeneValue {\n        use crate::values::computed::ToComputedValue;\n        let value = self.value.to_computed_value(ctx);\n        KleeneValue::from(unsafe {\n            crate::gecko_bindings::bindings::Gecko_EvalMozPrefFeature(self.name.as_ptr(), &value)\n        })\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn matches(&self, _: &computed::Context) -> KleeneValue {\n        KleeneValue::Unknown\n    }\n}\n\nimpl ToCss for MozPrefFeature {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.name.to_css(dest)?;\n        if !matches!(self.value, MozPrefFeatureValue::None) {\n            dest.write_str(\", \")?;\n            self.value.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// Represents a condition.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub enum QueryCondition {\n    /// A simple feature expression, implicitly parenthesized.\n    Feature(QueryFeatureExpression),\n    /// A custom media query reference in a boolean context, implicitly parenthesized.\n    Custom(DashedIdent),\n    /// A negation of a condition.\n    Not(Box<QueryCondition>),\n    /// A set of joint operations.\n    Operation(Box<[QueryCondition]>, Operator),\n    /// A condition wrapped in parenthesis.\n    InParens(Box<QueryCondition>),\n    /// A <style> query.\n    Style(StyleQuery),\n    /// A -moz-pref() query.\n    MozPref(MozPrefFeature),\n    /// [ <function-token> <any-value>? ) ] | [ ( <any-value>? ) ]\n    GeneralEnclosed(String, UrlExtraData),\n}\n\nimpl ToCss for QueryCondition {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *self {\n            // NOTE(emilio): QueryFeatureExpression already includes the\n            // parenthesis.\n            QueryCondition::Feature(ref f) => f.to_css(dest),\n            QueryCondition::Custom(ref name) => {\n                dest.write_char('(')?;\n                name.to_css(dest)?;\n                dest.write_char(')')\n            },\n            QueryCondition::Not(ref c) => {\n                dest.write_str(\"not \")?;\n                c.to_css(dest)\n            },\n            QueryCondition::InParens(ref c) => {\n                dest.write_char('(')?;\n                c.to_css(dest)?;\n                dest.write_char(')')\n            },\n            QueryCondition::Style(ref c) => {\n                dest.write_str(\"style(\")?;\n                c.to_css(dest)?;\n                dest.write_char(')')\n            },\n            QueryCondition::MozPref(ref c) => {\n                dest.write_str(\"-moz-pref(\")?;\n                c.to_css(dest)?;\n                dest.write_char(')')\n            },\n            QueryCondition::Operation(ref list, op) => {\n                let mut iter = list.iter();\n                iter.next().unwrap().to_css(dest)?;\n                for item in iter {\n                    dest.write_char(' ')?;\n                    op.to_css(dest)?;\n                    dest.write_char(' ')?;\n                    item.to_css(dest)?;\n                }\n                Ok(())\n            },\n            QueryCondition::GeneralEnclosed(ref s, _) => dest.write_str(&s),\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-syntax-3/#typedef-any-value>\nfn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {\n    input.expect_no_error_token().map_err(Into::into)\n}\n\nimpl QueryCondition {\n    /// Parse a single condition.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, feature_type, AllowOr::Yes)\n    }\n\n    fn visit<F>(&self, visitor: &mut F)\n    where\n        F: FnMut(&Self),\n    {\n        visitor(self);\n        match *self {\n            Self::Custom(..)\n            | Self::Feature(..)\n            | Self::GeneralEnclosed(..)\n            | Self::Style(..)\n            | Self::MozPref(..) => {},\n            Self::Not(ref cond) => cond.visit(visitor),\n            Self::Operation(ref conds, _op) => {\n                for cond in conds.iter() {\n                    cond.visit(visitor);\n                }\n            },\n            Self::InParens(ref cond) => cond.visit(visitor),\n        }\n    }\n\n    /// Returns the union of all flags in the expression. This is useful for\n    /// container queries.\n    pub fn cumulative_flags(&self) -> FeatureFlags {\n        let mut result = FeatureFlags::empty();\n        self.visit(&mut |condition| {\n            if let Self::Style(..) = condition {\n                result.insert(FeatureFlags::STYLE);\n            }\n            if let Self::Feature(ref f) = condition {\n                result.insert(f.feature_flags())\n            }\n        });\n        result\n    }\n\n    /// Parse a single condition, disallowing `or` expressions.\n    ///\n    /// To be used from the legacy query syntax.\n    pub fn parse_disallow_or<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, feature_type, AllowOr::No)\n    }\n\n    fn parse_in_parenthesis_block<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        // Base case. Make sure to preserve this error as it's more generally\n        // relevant.\n        let feature_error = match input.try_parse(|input| {\n            QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)\n        }) {\n            Ok(expr) => return Ok(Self::Feature(expr)),\n            Err(e) => e,\n        };\n        if static_prefs::pref!(\"layout.css.custom-media.enabled\") {\n            if let Ok(custom) = input.try_parse(|input| DashedIdent::parse(context, input)) {\n                return Ok(Self::Custom(custom));\n            }\n        }\n        if let Ok(inner) = Self::parse(context, input, feature_type) {\n            return Ok(Self::InParens(Box::new(inner)));\n        }\n        Err(feature_error)\n    }\n\n    fn try_parse_block<'i, T, F>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n        start: SourcePosition,\n        parse: F,\n    ) -> Option<T>\n    where\n        F: for<'tt> FnOnce(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i>>,\n    {\n        let nested = input.try_parse(|input| input.parse_nested_block(parse));\n        match nested {\n            Ok(nested) => Some(nested),\n            Err(e) => {\n                // We're about to swallow the error in a `<general-enclosed>`\n                // condition, so report it while we can.\n                let loc = e.location;\n                let error = ContextualParseError::InvalidMediaRule(input.slice_from(start), e);\n                context.log_css_error(loc, error);\n                None\n            },\n        }\n    }\n\n    /// Whether this condition matches the device and quirks mode.\n    /// https://drafts.csswg.org/mediaqueries/#evaluating\n    /// https://drafts.csswg.org/mediaqueries/#typedef-general-enclosed\n    /// Kleene 3-valued logic is adopted here due to the introduction of\n    /// <general-enclosed>.\n    pub fn matches(\n        &self,\n        context: &computed::Context,\n        custom: &mut CustomMediaEvaluator,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        match *self {\n            Self::Custom(ref f) => custom.matches(f, context),\n            Self::Feature(ref f) => f.matches(context),\n            Self::GeneralEnclosed(ref str, ref url_data) => {\n                self.matches_general(&str, url_data, context, custom, attribute_tracker)\n            },\n            Self::InParens(ref c) => c.matches(context, custom, attribute_tracker),\n            Self::Not(ref c) => !c.matches(context, custom, attribute_tracker),\n            Self::Style(ref c) => c.matches(context, attribute_tracker),\n            Self::MozPref(ref c) => c.matches(context),\n            Self::Operation(ref conditions, op) => {\n                debug_assert!(!conditions.is_empty(), \"We never create an empty op\");\n                match op {\n                    Operator::And => KleeneValue::any_false(conditions.iter(), |c| {\n                        c.matches(context, custom, attribute_tracker)\n                    }),\n                    Operator::Or => KleeneValue::any(conditions.iter(), |c| {\n                        c.matches(context, custom, attribute_tracker)\n                    }),\n                }\n            },\n        }\n    }\n\n    /// For a condition that was parsed as GeneralEnclosed, try applying custom-property\n    /// substitution and re-parse the result.\n    fn matches_general(\n        &self,\n        css_text: &str,\n        url_data: &UrlExtraData,\n        context: &computed::Context,\n        custom: &mut CustomMediaEvaluator,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        // This only applies (currently, at least) to container queries.\n        if !context.in_container_query {\n            return KleeneValue::Unknown;\n        }\n\n        let stylist = context\n            .builder\n            .stylist\n            .expect(\"container query should provide a Stylist\");\n\n        // Parse the text as a custom-property value to identify references.\n        let mut input = ParserInput::new(css_text);\n        let value = match custom_properties::SpecifiedValue::parse(\n            &mut Parser::new(&mut input),\n            None, // TODO: what Namespaces should we pass here?\n            url_data,\n        ) {\n            Ok(val) => val,\n            Err(_) => return KleeneValue::Unknown,\n        };\n\n        // If no references, we're not going to end up with a new result, just bail out.\n        if !value.has_references() {\n            return KleeneValue::Unknown;\n        }\n\n        // Substitute var() functions if possible.\n        let substitution_functions = custom_properties::ComputedSubstitutionFunctions::new(\n            Some(context.inherited_custom_properties().clone()),\n            None,\n        );\n        let custom_properties::SubstitutionResult { css, attr_taint } =\n            match custom_properties::substitute(\n                &value,\n                &substitution_functions,\n                stylist,\n                context,\n                attribute_tracker,\n            ) {\n                Ok(sub) => sub,\n                Err(_) => return KleeneValue::Unknown,\n            };\n\n        // Re-parse the result as a query-condition, and evaluate it.\n        let parser_context = ParserContext::new(\n            Origin::Author,\n            url_data,\n            Some(CssRuleType::Container),\n            ParsingMode::DEFAULT,\n            QuirksMode::NoQuirks,\n            /* namespaces = */ Default::default(),\n            /* error_reporter = */ None,\n            /* use_counters = */ None,\n            attr_taint,\n        );\n        let mut input = ParserInput::new(&css);\n        let result = match Self::parse(\n            &parser_context,\n            &mut Parser::new(&mut input),\n            FeatureType::Container,\n        ) {\n            Ok(Self::GeneralEnclosed(..)) => {\n                // If the result is still GeneralEnclosed, the query is unknown.\n                KleeneValue::Unknown\n            },\n            Ok(query) => query.matches(context, custom, attribute_tracker),\n            Err(_) => KleeneValue::Unknown,\n        };\n\n        result\n    }\n}\n\nimpl OperationParser for QueryCondition {\n    /// Parse a condition in parentheses, or `<general-enclosed>`.\n    ///\n    /// https://drafts.csswg.org/mediaqueries/#typedef-media-in-parens\n    fn parse_in_parens<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        input.skip_whitespace();\n        let start = input.position();\n        let start_location = input.current_source_location();\n        match *input.next()? {\n            Token::ParenthesisBlock => {\n                let nested = Self::try_parse_block(context, input, start, |input| {\n                    Self::parse_in_parenthesis_block(context, input, feature_type)\n                });\n                if let Some(nested) = nested {\n                    return Ok(nested);\n                }\n            },\n            Token::Function(ref name) => {\n                match_ignore_ascii_case! { name,\n                    \"style\" => {\n                        let query = Self::try_parse_block(context, input, start, |input| {\n                            StyleQuery::parse(context, input, feature_type)\n                        });\n                        if let Some(query) = query {\n                            return Ok(Self::Style(query));\n                        }\n                    },\n                    \"-moz-pref\" => {\n                        let feature = Self::try_parse_block(context, input, start, |input| {\n                            MozPrefFeature::parse(context, input, feature_type)\n                        });\n                        if let Some(feature) = feature {\n                            return Ok(Self::MozPref(feature));\n                        }\n                    },\n                    _ => {},\n                }\n            },\n            ref t => return Err(start_location.new_unexpected_token_error(t.clone())),\n        }\n        input.parse_nested_block(consume_any_value)?;\n        Ok(Self::GeneralEnclosed(\n            input.slice_from(start).to_owned(),\n            context.url_data.clone(),\n        ))\n    }\n\n    fn new_not(inner: Box<Self>) -> Self {\n        Self::Not(inner)\n    }\n\n    fn new_operation(conditions: Box<[Self]>, operator: Operator) -> Self {\n        Self::Operation(conditions, operator)\n    }\n}\n"
  },
  {
    "path": "style/queries/feature.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Query features.\n\nuse crate::derives::*;\nuse crate::parser::ParserContext;\nuse crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};\nuse crate::Atom;\nuse cssparser::Parser;\nuse selectors::kleene_value::KleeneValue;\nuse std::fmt;\nuse style_traits::ParseError;\n\n/// A generic discriminant for an enum value.\npub type KeywordDiscriminant = u8;\n\ntype QueryFeatureGetter<T> = fn(device: &computed::Context) -> T;\n\n/// Serializes a given discriminant.\n///\n/// FIXME(emilio): we could prevent this allocation if the ToCss code would\n/// generate a method for keywords to get the static string or something.\npub type KeywordSerializer = fn(KeywordDiscriminant) -> String;\n\n/// Parses a given identifier.\npub type KeywordParser = for<'a, 'i, 't> fn(\n    context: &'a ParserContext,\n    input: &'a mut Parser<'i, 't>,\n) -> Result<KeywordDiscriminant, ParseError<'i>>;\n\n/// An evaluator for a given feature.\n///\n/// This determines the kind of values that get parsed, too.\n#[allow(missing_docs)]\npub enum Evaluator {\n    Length(QueryFeatureGetter<CSSPixelLength>),\n    OptionalLength(QueryFeatureGetter<Option<CSSPixelLength>>),\n    Integer(QueryFeatureGetter<i32>),\n    Float(QueryFeatureGetter<f32>),\n    BoolInteger(QueryFeatureGetter<bool>),\n    /// A non-negative number ratio, such as the one from device-pixel-ratio.\n    NumberRatio(QueryFeatureGetter<Ratio>),\n    OptionalNumberRatio(QueryFeatureGetter<Option<Ratio>>),\n    /// A resolution.\n    Resolution(QueryFeatureGetter<Resolution>),\n    /// A keyword value.\n    Enumerated {\n        /// The parser to get a discriminant given a string.\n        parser: KeywordParser,\n        /// The serializer to get a string from a discriminant.\n        ///\n        /// This is guaranteed to be called with a keyword that `parser` has\n        /// produced.\n        serializer: KeywordSerializer,\n        /// The evaluator itself. This is guaranteed to be called with a\n        /// keyword that `parser` has produced.\n        evaluator: fn(&computed::Context, Option<KeywordDiscriminant>) -> KleeneValue,\n    },\n}\n\n/// A simple helper macro to create a keyword evaluator.\n///\n/// This assumes that keyword feature expressions don't accept ranges, and\n/// asserts if that's not true. As of today there's nothing like that (does that\n/// even make sense?).\nmacro_rules! keyword_evaluator {\n    ($actual_evaluator:ident, $keyword_type:ty) => {{\n        fn __parse<'i, 't>(\n            context: &$crate::parser::ParserContext,\n            input: &mut $crate::cssparser::Parser<'i, 't>,\n        ) -> Result<$crate::queries::feature::KeywordDiscriminant, ::style_traits::ParseError<'i>>\n        {\n            let kw = <$keyword_type as $crate::parser::Parse>::parse(context, input)?;\n            Ok(kw as $crate::queries::feature::KeywordDiscriminant)\n        }\n\n        fn __serialize(kw: $crate::queries::feature::KeywordDiscriminant) -> String {\n            // This unwrap is ok because the only discriminants that get\n            // back to us is the ones that `parse` produces.\n            let value: $keyword_type = ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap();\n            <$keyword_type as ::style_traits::ToCss>::to_css_string(&value)\n        }\n\n        fn __evaluate(\n            context: &$crate::values::computed::Context,\n            value: Option<$crate::queries::feature::KeywordDiscriminant>,\n        ) -> selectors::kleene_value::KleeneValue {\n            // This unwrap is ok because the only discriminants that get\n            // back to us is the ones that `parse` produces.\n            let value: Option<$keyword_type> =\n                value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap());\n            selectors::kleene_value::KleeneValue::from($actual_evaluator(context, value))\n        }\n\n        $crate::queries::feature::Evaluator::Enumerated {\n            parser: __parse,\n            serializer: __serialize,\n            evaluator: __evaluate,\n        }\n    }};\n}\n\n/// Different flags or toggles that change how a expression is parsed or\n/// evaluated.\n#[derive(Clone, Copy, Debug, ToShmem)]\npub struct FeatureFlags(u8);\nbitflags! {\n    impl FeatureFlags : u8 {\n        /// The feature should only be parsed in chrome and ua sheets.\n        const CHROME_AND_UA_ONLY = 1 << 0;\n        /// The feature requires a -webkit- prefix.\n        const WEBKIT_PREFIX = 1 << 1;\n        /// The feature requires the inline-axis containment.\n        const CONTAINER_REQUIRES_INLINE_AXIS = 1 << 2;\n        /// The feature requires the block-axis containment.\n        const CONTAINER_REQUIRES_BLOCK_AXIS = 1 << 3;\n        /// The feature requires containment in the physical width axis.\n        const CONTAINER_REQUIRES_WIDTH_AXIS = 1 << 4;\n        /// The feature requires containment in the physical height axis.\n        const CONTAINER_REQUIRES_HEIGHT_AXIS = 1 << 5;\n        /// The feature evaluation depends on the viewport size.\n        const VIEWPORT_DEPENDENT = 1 << 6;\n        /// The feature evaluation depends on style queries.\n        const STYLE = 1 << 7;\n    }\n}\n\nimpl FeatureFlags {\n    /// Returns parsing requirement flags.\n    pub fn parsing_requirements(self) -> Self {\n        self.intersection(Self::CHROME_AND_UA_ONLY | Self::WEBKIT_PREFIX)\n    }\n\n    /// Returns all the container axis flags.\n    pub fn all_container_axes() -> Self {\n        Self::CONTAINER_REQUIRES_INLINE_AXIS\n            | Self::CONTAINER_REQUIRES_BLOCK_AXIS\n            | Self::CONTAINER_REQUIRES_WIDTH_AXIS\n            | Self::CONTAINER_REQUIRES_HEIGHT_AXIS\n    }\n\n    /// Returns our subset of container axis flags.\n    pub fn container_axes(self) -> Self {\n        self.intersection(Self::all_container_axes())\n    }\n}\n\n/// Whether a feature allows ranges or not.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\n#[allow(missing_docs)]\npub enum AllowsRanges {\n    Yes,\n    No,\n}\n\n/// A description of a feature.\npub struct QueryFeatureDescription {\n    /// The feature name, in ascii lowercase.\n    pub name: Atom,\n    /// Whether min- / max- prefixes are allowed or not.\n    pub allows_ranges: AllowsRanges,\n    /// The evaluator, which we also use to determine which kind of value to\n    /// parse.\n    pub evaluator: Evaluator,\n    /// Different feature-specific flags.\n    pub flags: FeatureFlags,\n}\n\nimpl QueryFeatureDescription {\n    /// Whether this feature allows ranges.\n    #[inline]\n    pub fn allows_ranges(&self) -> bool {\n        self.allows_ranges == AllowsRanges::Yes\n    }\n}\n\n/// A simple helper to construct a `QueryFeatureDescription`.\nmacro_rules! feature {\n    ($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => {\n        $crate::queries::feature::QueryFeatureDescription {\n            name: $name,\n            allows_ranges: $allows_ranges,\n            evaluator: $evaluator,\n            flags: $flags,\n        }\n    };\n}\n\nimpl fmt::Debug for QueryFeatureDescription {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_struct(\"QueryFeatureDescription\")\n            .field(\"name\", &self.name)\n            .field(\"allows_ranges\", &self.allows_ranges)\n            .field(\"flags\", &self.flags)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "style/queries/feature_expression.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parsing for query feature expressions, like `(foo: bar)` or\n//! `(width >= 400px)`.\n\nuse super::feature::{Evaluator, QueryFeatureDescription};\nuse super::feature::{FeatureFlags, KeywordDiscriminant};\nuse crate::context::QuirksMode;\nuse crate::custom_properties::{\n    self, ComputedSubstitutionFunctions, VariableValue as CustomVariableValue,\n};\nuse crate::derives::*;\nuse crate::dom::AttributeTracker;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::{self, CSSWideKeyword};\nuse crate::properties_and_values::value::{ComputedValueComponent as Component, ValueInner};\nuse crate::selector_map::PrecomputedHashSet;\nuse crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};\nuse crate::stylesheets::{CssRuleType, Origin, UrlExtraData};\nuse crate::values::computed::{self, CSSPixelLength, Ratio, ToComputedValue};\nuse crate::values::specified::{Angle, Integer, Length, Number, Percentage, Resolution, Time};\nuse crate::values::{CSSFloat, DashedIdent};\nuse crate::{Atom, Zero};\nuse cssparser::{Parser, ParserInput, Token};\nuse selectors::kleene_value::KleeneValue;\nuse std::cmp::Ordering;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};\n\n/// Whether we're parsing a media or container query feature.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub enum FeatureType {\n    /// We're parsing a media feature.\n    Media,\n    /// We're parsing a container feature.\n    Container,\n}\n\nimpl FeatureType {\n    fn features(&self) -> &'static [QueryFeatureDescription] {\n        #[cfg(feature = \"gecko\")]\n        use crate::gecko::media_features::MEDIA_FEATURES;\n        #[cfg(feature = \"servo\")]\n        use crate::servo::media_features::MEDIA_FEATURES;\n\n        use crate::stylesheets::container_rule::CONTAINER_FEATURES;\n\n        match *self {\n            FeatureType::Media => &MEDIA_FEATURES,\n            FeatureType::Container => &CONTAINER_FEATURES,\n        }\n    }\n\n    fn find_feature(&self, name: &Atom) -> Option<(usize, &'static QueryFeatureDescription)> {\n        self.features()\n            .iter()\n            .enumerate()\n            .find(|(_, f)| f.name == *name)\n    }\n}\n\n/// The kind of matching that should be performed on a feature value.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\nenum LegacyRange {\n    /// At least the specified value.\n    Min,\n    /// At most the specified value.\n    Max,\n}\n\n/// The operator that was specified in this feature.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub enum Operator {\n    /// =\n    Equal,\n    /// >\n    GreaterThan,\n    /// >=\n    GreaterThanEqual,\n    /// <\n    LessThan,\n    /// <=\n    LessThanEqual,\n}\n\nimpl ToCss for Operator {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        dest.write_str(match *self {\n            Self::Equal => \"=\",\n            Self::LessThan => \"<\",\n            Self::LessThanEqual => \"<=\",\n            Self::GreaterThan => \">\",\n            Self::GreaterThanEqual => \">=\",\n        })\n    }\n}\n\nimpl Operator {\n    fn is_compatible_with(self, right_op: Self) -> bool {\n        // Some operators are not compatible with each other in multi-range\n        // context.\n        match self {\n            Self::Equal => false,\n            Self::GreaterThan | Self::GreaterThanEqual => {\n                matches!(right_op, Self::GreaterThan | Self::GreaterThanEqual)\n            },\n            Self::LessThan | Self::LessThanEqual => {\n                matches!(right_op, Self::LessThan | Self::LessThanEqual)\n            },\n        }\n    }\n\n    fn evaluate(&self, cmp: Ordering) -> bool {\n        match *self {\n            Self::Equal => cmp == Ordering::Equal,\n            Self::GreaterThan => cmp == Ordering::Greater,\n            Self::GreaterThanEqual => cmp == Ordering::Equal || cmp == Ordering::Greater,\n            Self::LessThan => cmp == Ordering::Less,\n            Self::LessThanEqual => cmp == Ordering::Equal || cmp == Ordering::Less,\n        }\n    }\n\n    fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let operator = match *input.next()? {\n            Token::Delim('=') => return Ok(Operator::Equal),\n            Token::Delim('>') => Operator::GreaterThan,\n            Token::Delim('<') => Operator::LessThan,\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        };\n\n        // https://drafts.csswg.org/mediaqueries-4/#mq-syntax:\n        //\n        //     No whitespace is allowed between the “<” or “>”\n        //     <delim-token>s and the following “=” <delim-token>, if it’s\n        //     present.\n        //\n        // TODO(emilio): Maybe we should ignore comments as well?\n        // https://github.com/w3c/csswg-drafts/issues/6248\n        let parsed_equal = input\n            .try_parse(|i| {\n                let t = i.next_including_whitespace().map_err(|_| ())?;\n                if !matches!(t, Token::Delim('=')) {\n                    return Err(());\n                }\n                Ok(())\n            })\n            .is_ok();\n\n        if !parsed_equal {\n            return Ok(operator);\n        }\n\n        Ok(match operator {\n            Operator::GreaterThan => Operator::GreaterThanEqual,\n            Operator::LessThan => Operator::LessThanEqual,\n            _ => unreachable!(),\n        })\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]\nenum QueryFeatureExpressionKind {\n    /// Just the media feature name.\n    Empty,\n\n    /// A single value.\n    Single(QueryExpressionValue),\n\n    /// Legacy range syntax (min-*: value) or so.\n    LegacyRange(LegacyRange, QueryExpressionValue),\n\n    /// Modern range context syntax:\n    /// https://drafts.csswg.org/mediaqueries-5/#mq-range-context\n    Range {\n        left: Option<(Operator, QueryExpressionValue)>,\n        right: Option<(Operator, QueryExpressionValue)>,\n    },\n}\n\nimpl QueryFeatureExpressionKind {\n    /// Evaluate a given range given an optional query value and a value from\n    /// the browser.\n    fn evaluate<T>(\n        &self,\n        context_value: T,\n        mut compute: impl FnMut(&QueryExpressionValue) -> T,\n    ) -> bool\n    where\n        T: PartialOrd + Zero,\n    {\n        match *self {\n            Self::Empty => return !context_value.is_zero(),\n            Self::Single(ref value) => {\n                let value = compute(value);\n                let cmp = match context_value.partial_cmp(&value) {\n                    Some(c) => c,\n                    None => return false,\n                };\n                cmp == Ordering::Equal\n            },\n            Self::LegacyRange(ref range, ref value) => {\n                let value = compute(value);\n                let cmp = match context_value.partial_cmp(&value) {\n                    Some(c) => c,\n                    None => return false,\n                };\n                cmp == Ordering::Equal\n                    || match range {\n                        LegacyRange::Min => cmp == Ordering::Greater,\n                        LegacyRange::Max => cmp == Ordering::Less,\n                    }\n            },\n            Self::Range {\n                ref left,\n                ref right,\n            } => {\n                debug_assert!(left.is_some() || right.is_some());\n                if let Some((ref op, ref value)) = left {\n                    let value = compute(value);\n                    let cmp = match value.partial_cmp(&context_value) {\n                        Some(c) => c,\n                        None => return false,\n                    };\n                    if !op.evaluate(cmp) {\n                        return false;\n                    }\n                }\n                if let Some((ref op, ref value)) = right {\n                    let value = compute(value);\n                    let cmp = match context_value.partial_cmp(&value) {\n                        Some(c) => c,\n                        None => return false,\n                    };\n                    if !op.evaluate(cmp) {\n                        return false;\n                    }\n                }\n                true\n            },\n        }\n    }\n\n    /// Non-ranged features only need to compare to one value at most.\n    fn non_ranged_value(&self) -> Option<&QueryExpressionValue> {\n        match *self {\n            Self::Empty => None,\n            Self::Single(ref v) => Some(v),\n            Self::LegacyRange(..) | Self::Range { .. } => {\n                debug_assert!(false, \"Unexpected ranged value in non-ranged feature!\");\n                None\n            },\n        }\n    }\n}\n\n/// A feature expression contains a reference to the feature, the value the\n/// query contained, and the range to evaluate.\n#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]\npub struct QueryFeatureExpression {\n    feature_type: FeatureType,\n    feature_index: usize,\n    kind: QueryFeatureExpressionKind,\n}\n\nimpl ToCss for QueryFeatureExpression {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        dest.write_char('(')?;\n\n        match self.kind {\n            QueryFeatureExpressionKind::Empty => self.write_name(dest)?,\n            QueryFeatureExpressionKind::Single(ref v)\n            | QueryFeatureExpressionKind::LegacyRange(_, ref v) => {\n                self.write_name(dest)?;\n                dest.write_str(\": \")?;\n                v.to_css(dest, Some(self))?;\n            },\n            QueryFeatureExpressionKind::Range {\n                ref left,\n                ref right,\n            } => {\n                if let Some((ref op, ref val)) = left {\n                    val.to_css(dest, Some(self))?;\n                    dest.write_char(' ')?;\n                    op.to_css(dest)?;\n                    dest.write_char(' ')?;\n                }\n                self.write_name(dest)?;\n                if let Some((ref op, ref val)) = right {\n                    dest.write_char(' ')?;\n                    op.to_css(dest)?;\n                    dest.write_char(' ')?;\n                    val.to_css(dest, Some(self))?;\n                }\n            },\n        }\n        dest.write_char(')')\n    }\n}\n\nfn consume_operation_or_colon<'i>(\n    input: &mut Parser<'i, '_>,\n) -> Result<Option<Operator>, ParseError<'i>> {\n    if input.try_parse(|input| input.expect_colon()).is_ok() {\n        return Ok(None);\n    }\n    Operator::parse(input).map(|op| Some(op))\n}\n\n#[allow(unused_variables)]\nfn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {\n    #[cfg(feature = \"gecko\")]\n    {\n        // prefers-reduced-transparency is always enabled in the ua and chrome. On\n        // the web it is hidden behind a preference (see Bug 1822176).\n        if *feature == atom!(\"prefers-reduced-transparency\") {\n            return !context.chrome_rules_enabled()\n                && !static_prefs::pref!(\"layout.css.prefers-reduced-transparency.enabled\");\n        }\n\n        // inverted-colors is always enabled in the ua and chrome. On\n        // the web it is hidden behind a preference.\n        if *feature == atom!(\"inverted-colors\") {\n            return !context.chrome_rules_enabled()\n                && !static_prefs::pref!(\"layout.css.inverted-colors.enabled\");\n        }\n    }\n    false\n}\n\nimpl QueryFeatureExpression {\n    fn new(\n        feature_type: FeatureType,\n        feature_index: usize,\n        kind: QueryFeatureExpressionKind,\n    ) -> Self {\n        debug_assert!(feature_index < feature_type.features().len());\n        Self {\n            feature_type,\n            feature_index,\n            kind,\n        }\n    }\n\n    fn write_name<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        let feature = self.feature();\n        if feature.flags.contains(FeatureFlags::WEBKIT_PREFIX) {\n            dest.write_str(\"-webkit-\")?;\n        }\n\n        if let QueryFeatureExpressionKind::LegacyRange(range, _) = self.kind {\n            match range {\n                LegacyRange::Min => dest.write_str(\"min-\")?,\n                LegacyRange::Max => dest.write_str(\"max-\")?,\n            }\n        }\n\n        // NB: CssStringWriter not needed, feature names are under control.\n        write!(dest, \"{}\", feature.name)?;\n\n        Ok(())\n    }\n\n    fn feature(&self) -> &'static QueryFeatureDescription {\n        &self.feature_type.features()[self.feature_index]\n    }\n\n    /// Returns the feature flags for our feature.\n    pub fn feature_flags(&self) -> FeatureFlags {\n        self.feature().flags\n    }\n\n    fn parse_feature_name<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<(usize, Option<LegacyRange>), ParseError<'i>> {\n        let mut flags = FeatureFlags::empty();\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n\n        if context.chrome_rules_enabled() {\n            flags.insert(FeatureFlags::CHROME_AND_UA_ONLY);\n        }\n\n        let mut feature_name = &**ident;\n        if starts_with_ignore_ascii_case(feature_name, \"-webkit-\") {\n            feature_name = &feature_name[8..];\n            flags.insert(FeatureFlags::WEBKIT_PREFIX);\n        }\n\n        let range = if starts_with_ignore_ascii_case(feature_name, \"min-\") {\n            feature_name = &feature_name[4..];\n            Some(LegacyRange::Min)\n        } else if starts_with_ignore_ascii_case(feature_name, \"max-\") {\n            feature_name = &feature_name[4..];\n            Some(LegacyRange::Max)\n        } else {\n            None\n        };\n\n        let atom = Atom::from(string_as_ascii_lowercase(feature_name));\n        let (feature_index, feature) = match feature_type.find_feature(&atom) {\n            Some((i, f)) => (i, f),\n            None => {\n                return Err(location.new_custom_error(\n                    StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),\n                ))\n            },\n        };\n\n        if disabled_by_pref(&feature.name, context)\n            || !flags.contains(feature.flags.parsing_requirements())\n            || (range.is_some() && !feature.allows_ranges())\n        {\n            return Err(location.new_custom_error(\n                StyleParseErrorKind::MediaQueryExpectedFeatureName(ident.clone()),\n            ));\n        }\n\n        Ok((feature_index, range))\n    }\n\n    /// Parses the following range syntax:\n    ///\n    ///   (feature-value <operator> feature-name)\n    ///   (feature-value <operator> feature-name <operator> feature-value)\n    fn parse_multi_range_syntax<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        let start = input.state();\n\n        // To parse the values, we first need to find the feature name. We rely\n        // on feature values for ranged features not being able to be top-level\n        // <ident>s, which holds.\n        let feature_index = loop {\n            // NOTE: parse_feature_name advances the input.\n            if let Ok((index, range)) = Self::parse_feature_name(context, input, feature_type) {\n                if range.is_some() {\n                    // Ranged names are not allowed here.\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                break index;\n            }\n            if input.is_exhausted() {\n                return Err(start\n                    .source_location()\n                    .new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n        };\n\n        input.reset(&start);\n\n        let feature = &feature_type.features()[feature_index];\n        let left_val = QueryExpressionValue::parse(feature, context, input)?;\n        let left_op = Operator::parse(input)?;\n\n        {\n            let (parsed_index, _) = Self::parse_feature_name(context, input, feature_type)?;\n            debug_assert_eq!(\n                parsed_index, feature_index,\n                \"How did we find a different feature?\"\n            );\n        }\n\n        let right_op = input.try_parse(Operator::parse).ok();\n        let right = match right_op {\n            Some(op) => {\n                if !left_op.is_compatible_with(op) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                Some((op, QueryExpressionValue::parse(feature, context, input)?))\n            },\n            None => None,\n        };\n        Ok(Self::new(\n            feature_type,\n            feature_index,\n            QueryFeatureExpressionKind::Range {\n                left: Some((left_op, left_val)),\n                right,\n            },\n        ))\n    }\n\n    /// Parse a feature expression where we've already consumed the parenthesis.\n    pub fn parse_in_parenthesis_block<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        feature_type: FeatureType,\n    ) -> Result<Self, ParseError<'i>> {\n        let (feature_index, range) =\n            match input.try_parse(|input| Self::parse_feature_name(context, input, feature_type)) {\n                Ok(v) => v,\n                Err(e) => {\n                    if let Ok(expr) = Self::parse_multi_range_syntax(context, input, feature_type) {\n                        return Ok(expr);\n                    }\n                    return Err(e);\n                },\n            };\n        let operator = input.try_parse(consume_operation_or_colon);\n        let operator = match operator {\n            Err(..) => {\n                // If there's no colon, this is a query of the form\n                // '(<feature>)', that is, there's no value specified.\n                //\n                // Gecko doesn't allow ranged expressions without a\n                // value, so just reject them here too.\n                if range.is_some() {\n                    return Err(\n                        input.new_custom_error(StyleParseErrorKind::RangedExpressionWithNoValue)\n                    );\n                }\n\n                return Ok(Self::new(\n                    feature_type,\n                    feature_index,\n                    QueryFeatureExpressionKind::Empty,\n                ));\n            },\n            Ok(operator) => operator,\n        };\n\n        let feature = &feature_type.features()[feature_index];\n\n        let value = QueryExpressionValue::parse(feature, context, input).map_err(|err| {\n            err.location\n                .new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)\n        })?;\n\n        let kind = match range {\n            Some(range) => {\n                if operator.is_some() {\n                    return Err(\n                        input.new_custom_error(StyleParseErrorKind::MediaQueryUnexpectedOperator)\n                    );\n                }\n                QueryFeatureExpressionKind::LegacyRange(range, value)\n            },\n            None => match operator {\n                Some(operator) => {\n                    if !feature.allows_ranges() {\n                        return Err(input\n                            .new_custom_error(StyleParseErrorKind::MediaQueryUnexpectedOperator));\n                    }\n                    QueryFeatureExpressionKind::Range {\n                        left: None,\n                        right: Some((operator, value)),\n                    }\n                },\n                None => QueryFeatureExpressionKind::Single(value),\n            },\n        };\n\n        Ok(Self::new(feature_type, feature_index, kind))\n    }\n\n    /// Returns whether this \"plain\" feature query evaluates to true for the given device.\n    pub fn matches(&self, context: &computed::Context) -> KleeneValue {\n        macro_rules! expect {\n            ($variant:ident, $v:expr) => {\n                match *$v {\n                    QueryExpressionValue::$variant(ref v) => v,\n                    _ => unreachable!(\"Unexpected QueryExpressionValue\"),\n                }\n            };\n        }\n\n        KleeneValue::from(match self.feature().evaluator {\n            Evaluator::Length(eval) => {\n                let v = eval(context);\n                self.kind\n                    .evaluate(v, |v| expect!(Length, v).to_computed_value(context))\n            },\n            Evaluator::OptionalLength(eval) => {\n                let v = match eval(context) {\n                    Some(v) => v,\n                    None => return KleeneValue::Unknown,\n                };\n                self.kind\n                    .evaluate(v, |v| expect!(Length, v).to_computed_value(context))\n            },\n            Evaluator::Integer(eval) => {\n                let v = eval(context);\n                self.kind.evaluate(v, |v| *expect!(Integer, v))\n            },\n            Evaluator::Float(eval) => {\n                let v = eval(context);\n                self.kind.evaluate(v, |v| *expect!(Float, v))\n            },\n            Evaluator::NumberRatio(eval) => {\n                let ratio = eval(context);\n                // A ratio of 0/0 behaves as the ratio 1/0, so we need to call used_value()\n                // to convert it if necessary.\n                // FIXME: we may need to update here once\n                // https://github.com/w3c/csswg-drafts/issues/4954 got resolved.\n                self.kind\n                    .evaluate(ratio, |v| expect!(NumberRatio, v).used_value())\n            },\n            Evaluator::OptionalNumberRatio(eval) => {\n                let ratio = match eval(context) {\n                    Some(v) => v,\n                    None => return KleeneValue::Unknown,\n                };\n                // See above for subtleties here.\n                self.kind\n                    .evaluate(ratio, |v| expect!(NumberRatio, v).used_value())\n            },\n            Evaluator::Resolution(eval) => {\n                let v = eval(context).dppx();\n                self.kind.evaluate(v, |v| {\n                    expect!(Resolution, v).to_computed_value(context).dppx()\n                })\n            },\n            Evaluator::Enumerated { evaluator, .. } => {\n                let computed = self\n                    .kind\n                    .non_ranged_value()\n                    .map(|v| *expect!(Enumerated, v));\n                return evaluator(context, computed);\n            },\n            Evaluator::BoolInteger(eval) => {\n                let computed = self\n                    .kind\n                    .non_ranged_value()\n                    .map(|v| *expect!(BoolInteger, v));\n                let boolean = eval(context);\n                computed.map_or(boolean, |v| v == boolean)\n            },\n        })\n    }\n}\n\n/// A value found or expected in a expression.\n///\n/// FIXME(emilio): How should calc() serialize in the Number / Integer /\n/// BoolInteger / NumberRatio case, as computed or as specified value?\n///\n/// If the first, this would need to store the relevant values.\n///\n/// See: https://github.com/w3c/csswg-drafts/issues/1968\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub enum QueryExpressionValue {\n    /// A length.\n    Length(Length),\n    /// An integer.\n    Integer(i32),\n    /// A floating point value.\n    Float(CSSFloat),\n    /// A boolean value, specified as an integer (i.e., either 0 or 1).\n    BoolInteger(bool),\n    /// A single non-negative number or two non-negative numbers separated by '/',\n    /// with optional whitespace on either side of the '/'.\n    NumberRatio(Ratio),\n    /// A resolution.\n    Resolution(Resolution),\n    /// An enumerated value, defined by the variant keyword table in the\n    /// feature's `mData` member.\n    Enumerated(KeywordDiscriminant),\n    /// Value types only used by style-range query expressions, not feature queries.\n    /// A CSS-wide keyword.\n    Keyword(CSSWideKeyword),\n    /// A percentage.\n    Percentage(Percentage),\n    /// An angle.\n    Angle(Angle),\n    /// A time value.\n    Time(Time),\n    /// A custom property name.\n    Custom(DashedIdent),\n    /// An arbitrary substitution function (var(), attr(), env()), stored as a string\n    /// for later evaluation. We store this as a custom-property value to make it easy\n    /// to resolve later.\n    Function(Box<CustomVariableValue>),\n}\n\nimpl QueryExpressionValue {\n    fn to_css<W>(\n        &self,\n        dest: &mut CssWriter<W>,\n        for_expr: Option<&QueryFeatureExpression>,\n    ) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *self {\n            QueryExpressionValue::Length(ref l) => l.to_css(dest),\n            QueryExpressionValue::Integer(v) => v.to_css(dest),\n            QueryExpressionValue::Float(v) => v.to_css(dest),\n            QueryExpressionValue::BoolInteger(v) => dest.write_str(if v { \"1\" } else { \"0\" }),\n            QueryExpressionValue::NumberRatio(ratio) => ratio.to_css(dest),\n            QueryExpressionValue::Resolution(ref r) => r.to_css(dest),\n            QueryExpressionValue::Keyword(k) => k.to_css(dest),\n            QueryExpressionValue::Percentage(v) => v.to_css(dest),\n            QueryExpressionValue::Angle(v) => v.to_css(dest),\n            QueryExpressionValue::Time(v) => v.to_css(dest),\n            QueryExpressionValue::Custom(ref v) => v.to_css(dest),\n            QueryExpressionValue::Function(ref f) => f.to_css(dest),\n            QueryExpressionValue::Enumerated(value) => match for_expr\n                .expect(\"caller should have passed for_expr\")\n                .feature()\n                .evaluator\n            {\n                Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),\n                _ => unreachable!(),\n            },\n        }\n    }\n\n    fn parse<'i, 't>(\n        for_feature: &QueryFeatureDescription,\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<QueryExpressionValue, ParseError<'i>> {\n        Ok(match for_feature.evaluator {\n            Evaluator::OptionalLength(..) | Evaluator::Length(..) => {\n                let length = Length::parse(context, input)?;\n                QueryExpressionValue::Length(length)\n            },\n            Evaluator::Integer(..) => {\n                let integer = Integer::parse(context, input)?;\n                QueryExpressionValue::Integer(integer.value())\n            },\n            Evaluator::BoolInteger(..) => {\n                let integer = Integer::parse_non_negative(context, input)?;\n                let value = integer.value();\n                if value > 1 {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                QueryExpressionValue::BoolInteger(value == 1)\n            },\n            Evaluator::Float(..) => {\n                let number = Number::parse(context, input)?;\n                QueryExpressionValue::Float(number.get())\n            },\n            Evaluator::OptionalNumberRatio(..) | Evaluator::NumberRatio(..) => {\n                use crate::values::specified::Ratio as SpecifiedRatio;\n                let ratio = SpecifiedRatio::parse(context, input)?;\n                QueryExpressionValue::NumberRatio(Ratio::new(ratio.0.get(), ratio.1.get()))\n            },\n            Evaluator::Resolution(..) => {\n                QueryExpressionValue::Resolution(Resolution::parse(context, input)?)\n            },\n            Evaluator::Enumerated { parser, .. } => {\n                QueryExpressionValue::Enumerated(parser(context, input)?)\n            },\n        })\n    }\n\n    // Parse any of the types that can occur in a <style-range> query:\n    // <number>, <percentage>, <length>, <angle>, <time>, <frequency> or <resolution>,\n    // or a custom property name.\n    // NB: we don't currently implement the <frequency> type anywhere, so it is not\n    // parsed here.\n    fn parse_for_style_range<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(number) = input.try_parse(|i| Number::parse(context, i)) {\n            return Ok(Self::Float(number.get()));\n        }\n        if let Ok(percent) = input.try_parse(|i| Percentage::parse(context, i)) {\n            return Ok(Self::Percentage(percent));\n        }\n        if let Ok(length) = input.try_parse(|i| Length::parse(context, i)) {\n            return Ok(Self::Length(length));\n        }\n        if let Ok(angle) = input.try_parse(|i| Angle::parse(context, i)) {\n            return Ok(Self::Angle(angle));\n        }\n        if let Ok(time) = input.try_parse(|i| Time::parse(context, i)) {\n            return Ok(Self::Time(time));\n        }\n        if let Ok(resolution) = input.try_parse(|i| Resolution::parse(context, i)) {\n            return Ok(Self::Resolution(resolution));\n        }\n        if let Ok(ident) = input.try_parse(|i| DashedIdent::parse(context, i)) {\n            return Ok(Self::Custom(ident));\n        }\n        if let Ok(keyword) = input.try_parse(|i| CSSWideKeyword::parse(i)) {\n            return Ok(Self::Keyword(keyword));\n        }\n        input.skip_whitespace();\n        let start = input.position();\n        if let Ok(Token::Function(ref name)) = input.next() {\n            // Helper to parse the function arg and store the complete expression (function\n            // name and parenthesized argument) into a CustomVariableValue.\n            let parse_func =\n                |input: &mut Parser<'i, 't>| -> Result<CustomVariableValue, ParseError<'i>> {\n                    input.parse_nested_block(|i| i.expect_no_error_token().map_err(Into::into))?;\n                    let mut input = ParserInput::new(input.slice_from(start));\n                    CustomVariableValue::parse(\n                        &mut Parser::new(&mut input),\n                        Some(&context.namespaces.prefixes),\n                        context.url_data,\n                    )\n                };\n\n            if properties::enabled_arbitrary_substitution_functions()\n                .iter()\n                .any(|n| n.eq_ignore_ascii_case(name))\n            {\n                return Ok(Self::Function(Box::new(parse_func(input)?)));\n            }\n        }\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n/// https://drafts.csswg.org/css-conditional-5/#typedef-style-range\n#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]\npub enum QueryStyleRange {\n    /// A style-range for style container queries with two values\n    /// (val1 OP val2).\n    #[allow(missing_docs)]\n    StyleRange2 {\n        value1: QueryExpressionValue,\n        op1: Operator,\n        value2: QueryExpressionValue,\n    },\n\n    /// A style-range for style container queries with three values\n    /// (val1 OP val2 OP val3).\n    #[allow(missing_docs)]\n    StyleRange3 {\n        value1: QueryExpressionValue,\n        op1: Operator,\n        value2: QueryExpressionValue,\n        op2: Operator,\n        value3: QueryExpressionValue,\n    },\n}\n\nimpl ToCss for QueryStyleRange {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        dest.write_char('(')?;\n        match self {\n            Self::StyleRange2 {\n                ref value1,\n                ref op1,\n                ref value2,\n            } => {\n                value1.to_css(dest, None)?;\n                dest.write_char(' ')?;\n                op1.to_css(dest)?;\n                dest.write_char(' ')?;\n                value2.to_css(dest, None)?;\n            },\n            Self::StyleRange3 {\n                ref value1,\n                ref op1,\n                ref value2,\n                ref op2,\n                ref value3,\n            } => {\n                value1.to_css(dest, None)?;\n                dest.write_char(' ')?;\n                op1.to_css(dest)?;\n                dest.write_char(' ')?;\n                value2.to_css(dest, None)?;\n                dest.write_char(' ')?;\n                op2.to_css(dest)?;\n                dest.write_char(' ')?;\n                value3.to_css(dest, None)?;\n            },\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl QueryStyleRange {\n    /// Parses the following range syntax:\n    ///\n    ///   value <operator> value\n    ///   value <operator> value <operator> value\n    ///\n    /// This is only used when parsing @container style() queries; the feature_type\n    /// and index is hardcoded (and ignored).\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let value1 = QueryExpressionValue::parse_for_style_range(context, input)?;\n        let op1 = Operator::parse(input)?;\n        let value2 = QueryExpressionValue::parse_for_style_range(context, input)?;\n\n        if let Ok(op2) = input.try_parse(|i| Operator::parse(i)) {\n            if op1.is_compatible_with(op2) {\n                let value3 = QueryExpressionValue::parse_for_style_range(context, input)?;\n                return Ok(Self::StyleRange3 {\n                    value1,\n                    op1,\n                    value2,\n                    op2,\n                    value3,\n                });\n            }\n        }\n\n        Ok(Self::StyleRange2 {\n            value1,\n            op1,\n            value2,\n        })\n    }\n\n    /// Returns whether this style-range query evaluates to true for the given context.\n    pub fn evaluate(\n        &self,\n        context: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n    ) -> KleeneValue {\n        match self {\n            QueryStyleRange::StyleRange2 {\n                ref value1,\n                ref op1,\n                ref value2,\n            } => Self::compare_values(\n                Self::resolve_value(\n                    value1,\n                    context,\n                    attribute_tracker,\n                    &mut PrecomputedHashSet::default(),\n                )\n                .as_ref(),\n                Self::resolve_value(\n                    value2,\n                    context,\n                    attribute_tracker,\n                    &mut PrecomputedHashSet::default(),\n                )\n                .as_ref(),\n            )\n            .is_some_and(|c| op1.evaluate(c))\n            .into(),\n\n            QueryStyleRange::StyleRange3 {\n                ref value1,\n                ref op1,\n                ref value2,\n                ref op2,\n                ref value3,\n            } => {\n                let v1 = Self::resolve_value(\n                    value1,\n                    context,\n                    attribute_tracker,\n                    &mut PrecomputedHashSet::default(),\n                );\n                let v2 = Self::resolve_value(\n                    value2,\n                    context,\n                    attribute_tracker,\n                    &mut PrecomputedHashSet::default(),\n                );\n                Self::compare_values(v1.as_ref(), v2.as_ref())\n                    .is_some_and(|c1| {\n                        op1.evaluate(c1)\n                            && Self::compare_values(\n                                v2.as_ref(),\n                                Self::resolve_value(\n                                    value3,\n                                    context,\n                                    attribute_tracker,\n                                    &mut PrecomputedHashSet::default(),\n                                )\n                                .as_ref(),\n                            )\n                            .is_some_and(|c2| op2.evaluate(c2))\n                    })\n                    .into()\n            },\n        }\n    }\n\n    // Resolve a QueryExpressionValue to its computed value for comparison.\n    fn resolve_value(\n        value: &QueryExpressionValue,\n        context: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n        visited_set: &mut PrecomputedHashSet<DashedIdent>,\n    ) -> Option<Component> {\n        match value {\n            QueryExpressionValue::Custom(ident) => {\n                // `ident` is the dashed ident, but we need the name\n                // without \"--\" for custom-property lookup.\n                let name = ident.undashed();\n                let stylist = context\n                    .builder\n                    .stylist\n                    .expect(\"container queries should have a stylist around\");\n                let registration = stylist.get_custom_property_registration(&name);\n                let current_value = context\n                    .inherited_custom_properties()\n                    .get(registration, &name)?;\n                match &current_value.v {\n                    ValueInner::Component(component) => Some(component.clone()),\n                    ValueInner::Universal(v) => {\n                        // If visited_set.insert() returns false, ident was already seen\n                        // and we risk infinite recursion, so instead return None\n                        // (i.e. the value cannot be resolved).\n                        if visited_set.insert(ident.clone()) {\n                            Self::resolve_universal(\n                                &v.css,\n                                &v.url_data,\n                                context,\n                                attribute_tracker,\n                                visited_set,\n                            )\n                        } else {\n                            None\n                        }\n                    },\n                    ValueInner::List(_) => {\n                        debug_assert!(false, \"We don't parse list values in style queries\");\n                        None\n                    },\n                }\n            },\n            QueryExpressionValue::Function(value) => {\n                let sub_funcs = ComputedSubstitutionFunctions::new(\n                    Some(context.inherited_custom_properties().clone()),\n                    None,\n                );\n                let stylist = context\n                    .builder\n                    .stylist\n                    .expect(\"container queries should have a stylist around\");\n                let substituted = custom_properties::substitute(\n                    &value,\n                    &sub_funcs,\n                    stylist,\n                    context,\n                    attribute_tracker,\n                )\n                .ok()?;\n                Self::resolve_universal(\n                    &substituted.css,\n                    &value.url_data,\n                    context,\n                    attribute_tracker,\n                    visited_set,\n                )\n            },\n            QueryExpressionValue::Length(v) => {\n                Some(Component::Length(v.to_computed_value(context)))\n            },\n            QueryExpressionValue::Float(v) => Some(Component::Number(v.to_computed_value(context))),\n            QueryExpressionValue::Resolution(v) => {\n                Some(Component::Resolution(v.to_computed_value(context)))\n            },\n            QueryExpressionValue::Percentage(v) => {\n                Some(Component::Percentage(v.to_computed_value(context)))\n            },\n            QueryExpressionValue::Angle(v) => Some(Component::Angle(v.to_computed_value(context))),\n            QueryExpressionValue::Time(v) => Some(Component::Time(v.to_computed_value(context))),\n            // It's unclear to me what CSS-wide keywords would mean in a style-range query;\n            // for now, at least, they'll just fail to resolve.\n            QueryExpressionValue::Keyword(_) => None,\n            _ => {\n                debug_assert!(false, \"unexpected value type in style range\");\n                None\n            },\n        }\n    }\n\n    // If a custom-property QueryExpressionValue has a \"universal-syntax\" value, we need to\n    // send the current CSS text of the value to QueryExpressionValue::parse_for_style_range\n    // to try and resolve to a specific typed value.\n    // After parsing, this will call back to QueryExpressionValue::resolve_value with the\n    // parsed result, which has the potential for mutual recursion; we keep track of a\n    // visited_set of custom property names to protect against this.\n    fn resolve_universal(\n        css_text: &str,\n        url_data: &UrlExtraData,\n        context: &computed::Context,\n        attribute_tracker: &mut AttributeTracker,\n        visited_set: &mut PrecomputedHashSet<DashedIdent>,\n    ) -> Option<Component> {\n        let parser_context = ParserContext::new(\n            Origin::Author,\n            url_data,\n            Some(CssRuleType::Container),\n            ParsingMode::DEFAULT,\n            QuirksMode::NoQuirks,\n            /* namespaces = */ Default::default(),\n            /* error_reporter = */ None,\n            /* use_counters = */ None,\n            /* attr_taint */ Default::default(),\n        );\n        let mut input = ParserInput::new(css_text);\n        QueryExpressionValue::parse_for_style_range(&parser_context, &mut Parser::new(&mut input))\n            .ok()\n            .and_then(|parsed| {\n                Self::resolve_value(&parsed, context, attribute_tracker, visited_set)\n            })\n    }\n\n    fn compare_values(value1: Option<&Component>, value2: Option<&Component>) -> Option<Ordering> {\n        let value1 = value1?;\n        let value2 = value2?;\n        match (value1, value2) {\n            (Component::Length(v1), Component::Length(v2)) => v1.partial_cmp(&v2),\n            (Component::Number(v1), Component::Number(v2)) => v1.partial_cmp(&v2),\n            (Component::Resolution(v1), Component::Resolution(v2)) => {\n                v1.dppx().partial_cmp(&v2.dppx())\n            },\n            (Component::Percentage(v1), Component::Percentage(v2)) => v1.partial_cmp(&v2),\n            (Component::Angle(v1), Component::Angle(v2)) => v1.partial_cmp(&v2),\n            (Component::Time(v1), Component::Time(v2)) => v1.partial_cmp(&v2),\n            (Component::Length(v1), Component::Number(v2)) => {\n                if v2.is_zero() {\n                    v1.partial_cmp(&CSSPixelLength::zero())\n                } else {\n                    None\n                }\n            },\n            (Component::Number(v1), Component::Length(v2)) => {\n                if v1.is_zero() {\n                    CSSPixelLength::zero().partial_cmp(&v2)\n                } else {\n                    None\n                }\n            },\n            _ => None,\n        }\n    }\n}\n"
  },
  {
    "path": "style/queries/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Code shared between [media queries][mq] and [container queries][cq].\n//!\n//! [mq]: https://drafts.csswg.org/mediaqueries/\n//! [cq]: https://drafts.csswg.org/css-contain-3/#container-rule\n\npub mod condition;\n\n#[macro_use]\npub mod feature;\npub mod feature_expression;\npub mod values;\n\npub use self::condition::QueryCondition;\npub use self::feature::FeatureFlags;\npub use self::feature_expression::{FeatureType, QueryFeatureExpression, QueryStyleRange};\n"
  },
  {
    "path": "style/queries/values.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common feature values between media and container features.\n\nuse crate::derives::*;\nuse app_units::Au;\nuse euclid::default::Size2D;\n\n/// The orientation media / container feature.\n/// https://drafts.csswg.org/mediaqueries-5/#orientation\n/// https://drafts.csswg.org/css-contain-3/#orientation\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum Orientation {\n    Portrait,\n    Landscape,\n}\n\nimpl Orientation {\n    /// A helper to evaluate a orientation query given a generic size getter.\n    pub fn eval(size: Size2D<Au>, value: Option<Self>) -> bool {\n        let query_orientation = match value {\n            Some(v) => v,\n            None => return true,\n        };\n\n        // Per spec, square viewports should be 'portrait'\n        let is_landscape = size.width > size.height;\n        match query_orientation {\n            Self::Landscape => is_landscape,\n            Self::Portrait => !is_landscape,\n        }\n    }\n}\n\n/// Values for the prefers-color-scheme media feature.\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum PrefersColorScheme {\n    Light,\n    Dark,\n}\n"
  },
  {
    "path": "style/rule_cache.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A cache from rule node to computed values, in order to cache reset\n//! properties.\n\nuse crate::context::CascadeInputs;\nuse crate::logical_geometry::WritingMode;\nuse crate::properties::{ComputedValues, StyleBuilder};\nuse crate::rule_tree::StrongRuleNode;\nuse crate::selector_parser::PseudoElement;\nuse crate::shared_lock::StylesheetGuards;\nuse crate::values::computed::{Context, NonNegativeLength, Zoom};\nuse crate::values::specified::color::ColorSchemeFlags;\nuse rustc_hash::FxHashMap;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\n\n/// The conditions for caching and matching a style in the rule cache.\n#[derive(Clone, Debug, Default)]\npub struct RuleCacheConditions {\n    uncacheable: bool,\n    font_size: Option<NonNegativeLength>,\n    line_height: Option<NonNegativeLength>,\n    writing_mode: Option<WritingMode>,\n    color_scheme: Option<ColorSchemeFlags>,\n}\n\nimpl RuleCacheConditions {\n    /// Sets the style as depending in the font-size value.\n    pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) {\n        debug_assert!(self.font_size.map_or(true, |f| f == font_size));\n        self.font_size = Some(font_size);\n    }\n\n    /// Sets the style as depending in the line-height value.\n    pub fn set_line_height_dependency(&mut self, line_height: NonNegativeLength) {\n        debug_assert!(self.line_height.map_or(true, |l| l == line_height));\n        self.line_height = Some(line_height);\n    }\n\n    /// Sets the style as depending in the color-scheme property value.\n    pub fn set_color_scheme_dependency(&mut self, color_scheme: ColorSchemeFlags) {\n        debug_assert!(self.color_scheme.map_or(true, |cs| cs == color_scheme));\n        self.color_scheme = Some(color_scheme);\n    }\n\n    /// Sets the style as uncacheable.\n    pub fn set_uncacheable(&mut self) {\n        self.uncacheable = true;\n    }\n\n    /// Sets the style as depending in the writing-mode value `writing_mode`.\n    pub fn set_writing_mode_dependency(&mut self, writing_mode: WritingMode) {\n        debug_assert!(self.writing_mode.map_or(true, |wm| wm == writing_mode));\n        self.writing_mode = Some(writing_mode);\n    }\n\n    /// Returns whether the current style's reset properties are cacheable.\n    fn cacheable(&self) -> bool {\n        !self.uncacheable\n    }\n}\n\n#[derive(Debug)]\nstruct CachedConditions {\n    font_size: Option<NonNegativeLength>,\n    line_height: Option<NonNegativeLength>,\n    color_scheme: Option<ColorSchemeFlags>,\n    writing_mode: Option<WritingMode>,\n    zoom: Zoom,\n}\n\nimpl CachedConditions {\n    /// Returns whether `style` matches the conditions.\n    fn matches(&self, style: &StyleBuilder) -> bool {\n        if style.effective_zoom != self.zoom {\n            return false;\n        }\n\n        if let Some(fs) = self.font_size {\n            if style.get_font().clone_font_size().computed_size != fs {\n                return false;\n            }\n        }\n\n        if let Some(lh) = self.line_height {\n            let new_line_height =\n                style\n                    .device\n                    .calc_line_height(&style.get_font(), style.writing_mode, None);\n            if new_line_height != lh {\n                return false;\n            }\n        }\n\n        if let Some(cs) = self.color_scheme {\n            if style.get_inherited_ui().color_scheme_bits() != cs {\n                return false;\n            }\n        }\n\n        if let Some(wm) = self.writing_mode {\n            if style.writing_mode != wm {\n                return false;\n            }\n        }\n\n        true\n    }\n}\n\n/// A TLS cache from rules matched to computed values.\npub struct RuleCache {\n    // FIXME(emilio): Consider using LRUCache or something like that?\n    map: FxHashMap<StrongRuleNode, SmallVec<[(CachedConditions, Arc<ComputedValues>); 1]>>,\n}\n\nimpl RuleCache {\n    /// Creates an empty `RuleCache`.\n    pub fn new() -> Self {\n        Self {\n            map: FxHashMap::default(),\n        }\n    }\n\n    /// Walk the rule tree and return a rule node for using as the key\n    /// for rule cache.\n    ///\n    /// It currently skips animation / style attribute / preshint rules when they don't contain any\n    /// declaration of a reset property. We don't skip other levels because walking the whole\n    /// parent chain can be expensive.\n    ///\n    /// TODO(emilio): Measure this, this was not super-well measured for performance (this was done\n    /// for memory in bug 1427681)... Walking the rule tree might be worth it if we hit the cache\n    /// enough?\n    fn get_rule_node_for_cache<'r>(\n        guards: &StylesheetGuards,\n        mut rule_node: Option<&'r StrongRuleNode>,\n    ) -> Option<&'r StrongRuleNode> {\n        use crate::rule_tree::CascadeOrigin;\n        while let Some(node) = rule_node {\n            let priority = node.cascade_priority();\n            let cascade_level = priority.cascade_level();\n            let should_try_to_skip = cascade_level.is_animation()\n                || cascade_level.origin() == CascadeOrigin::PresHints\n                || priority.layer_order().is_style_attribute_layer();\n            if !should_try_to_skip {\n                break;\n            }\n            if let Some(source) = node.style_source() {\n                let decls = source.get().read_with(cascade_level.guard(guards));\n                if decls.contains_any_reset() {\n                    break;\n                }\n            }\n            rule_node = node.parent();\n        }\n        rule_node\n    }\n\n    /// Finds a node in the properties matched cache.\n    ///\n    /// This needs to receive a `StyleBuilder` with the `early` properties\n    /// already applied.\n    pub fn find(&self, guards: &StylesheetGuards, context: &Context) -> Option<&ComputedValues> {\n        // A pseudo-element with property restrictions can result in different\n        // computed values if it's also used for a non-pseudo.\n        if context\n            .builder\n            .pseudo\n            .and_then(|p| p.property_restriction())\n            .is_some()\n        {\n            return None;\n        }\n\n        if !context.included_cascade_flags.is_empty() {\n            return None;\n        }\n\n        let rules = context.builder.rules.as_ref();\n        let rules = Self::get_rule_node_for_cache(guards, rules)?;\n        let cached_values = self.map.get(rules)?;\n\n        for &(ref conditions, ref values) in cached_values.iter() {\n            if conditions.matches(&context.builder) {\n                debug!(\"Using cached reset style with conditions {:?}\", conditions);\n                return Some(&**values);\n            }\n        }\n        None\n    }\n\n    /// Inserts a node into the rules cache if possible.\n    ///\n    /// Returns whether the style was inserted into the cache.\n    pub fn insert_if_possible(\n        &mut self,\n        guards: &StylesheetGuards,\n        style: &Arc<ComputedValues>,\n        pseudo: Option<&PseudoElement>,\n        inputs: &CascadeInputs,\n        conditions: &RuleCacheConditions,\n    ) -> bool {\n        if !conditions.cacheable() {\n            return false;\n        }\n\n        // A pseudo-element with property restrictions can result in different\n        // computed values if it's also used for a non-pseudo.\n        if pseudo.and_then(|p| p.property_restriction()).is_some() {\n            return false;\n        }\n\n        // Don't insert @starting-style styles in the cache, for the same reason.\n        if !inputs.included_cascade_flags.is_empty() {\n            return false;\n        }\n\n        let rules = style.rules.as_ref();\n        let rules = match Self::get_rule_node_for_cache(guards, rules) {\n            Some(r) => r.clone(),\n            None => return false,\n        };\n\n        debug!(\n            \"Inserting cached reset style with conditions {:?}\",\n            conditions\n        );\n        let cached_conditions = CachedConditions {\n            writing_mode: conditions.writing_mode,\n            font_size: conditions.font_size,\n            line_height: conditions.line_height,\n            color_scheme: conditions.color_scheme,\n            zoom: style.effective_zoom,\n        };\n        self.map\n            .entry(rules)\n            .or_default()\n            .push((cached_conditions, style.clone()));\n        true\n    }\n}\n"
  },
  {
    "path": "style/rule_collector.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Collects a series of applicable rules for a given element.\n\nuse crate::applicable_declarations::{ApplicableDeclarationBlock, ApplicableDeclarationList};\nuse crate::dom::{TElement, TNode, TShadowRoot};\nuse crate::properties::{AnimationDeclarations, PropertyDeclarationBlock};\nuse crate::rule_tree::{CascadeLevel, CascadeOrigin, ShadowCascadeOrder};\nuse crate::selector_map::SelectorMap;\nuse crate::selector_parser::PseudoElement;\nuse crate::shared_lock::Locked;\nuse crate::stylesheets::{layer_rule::LayerOrder, Origin};\nuse crate::stylist::{AuthorStylesEnabled, CascadeData, Rule, RuleInclusion, Stylist};\nuse selectors::matching::MatchingContext;\nuse servo_arc::ArcBorrow;\nuse smallvec::SmallVec;\n\n/// This is a bit of a hack so <svg:use> matches the rules of the enclosing\n/// tree.\n///\n/// This function returns the containing shadow host ignoring <svg:use> shadow\n/// trees, since those match the enclosing tree's rules.\n///\n/// Only a handful of places need to really care about this. This is not a\n/// problem for invalidation and that kind of stuff because they still don't\n/// match rules based on elements outside of the shadow tree, and because the\n/// <svg:use> subtrees are immutable and recreated each time the source tree\n/// changes.\n///\n/// We historically allow cross-document <svg:use> to have these rules applied,\n/// but I think that's not great. Gecko is the only engine supporting that.\n///\n/// See https://github.com/w3c/svgwg/issues/504 for the relevant spec\n/// discussion.\n#[inline]\npub fn containing_shadow_ignoring_svg_use<E: TElement>(\n    element: E,\n) -> Option<<E::ConcreteNode as TNode>::ConcreteShadowRoot> {\n    let mut shadow = element.containing_shadow()?;\n    loop {\n        let host = shadow.host();\n        let host_is_svg_use_element =\n            host.is_svg_element() && host.local_name() == &**local_name!(\"use\");\n        if !host_is_svg_use_element {\n            return Some(shadow);\n        }\n        debug_assert!(\n            shadow.style_data().is_none(),\n            \"We allow no stylesheets in <svg:use> subtrees\"\n        );\n        shadow = host.containing_shadow()?;\n    }\n}\n\n/// An object that we use with all the intermediate state needed for the\n/// cascade.\n///\n/// This is done basically to be able to organize the cascade in smaller\n/// functions, and be able to reason about it easily.\npub struct RuleCollector<'a, 'b: 'a, E>\nwhere\n    E: TElement,\n{\n    element: E,\n    rule_hash_target: E,\n    stylist: &'a Stylist,\n    pseudo_elements: SmallVec<[PseudoElement; 1]>,\n    style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,\n    smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,\n    animation_declarations: AnimationDeclarations,\n    rule_inclusion: RuleInclusion,\n    rules: &'a mut ApplicableDeclarationList,\n    context: &'a mut MatchingContext<'b, E::Impl>,\n    matches_user_and_content_rules: bool,\n    matches_document_author_rules: bool,\n    in_sort_scope: bool,\n}\n\nimpl<'a, 'b: 'a, E> RuleCollector<'a, 'b, E>\nwhere\n    E: TElement,\n{\n    /// Trivially construct a new collector.\n    pub fn new(\n        stylist: &'a Stylist,\n        element: E,\n        pseudo_elements: SmallVec<[PseudoElement; 1]>,\n        style_attribute: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,\n        smil_override: Option<ArcBorrow<'a, Locked<PropertyDeclarationBlock>>>,\n        animation_declarations: AnimationDeclarations,\n        rule_inclusion: RuleInclusion,\n        rules: &'a mut ApplicableDeclarationList,\n        context: &'a mut MatchingContext<'b, E::Impl>,\n    ) -> Self {\n        let rule_hash_target = element.rule_hash_target();\n        let matches_user_and_content_rules = rule_hash_target.matches_user_and_content_rules();\n\n        debug_assert!(pseudo_elements.iter().all(|p| !p.is_precomputed()));\n\n        Self {\n            element,\n            rule_hash_target,\n            stylist,\n            pseudo_elements,\n            style_attribute,\n            smil_override,\n            animation_declarations,\n            rule_inclusion,\n            context,\n            rules,\n            matches_user_and_content_rules,\n            matches_document_author_rules: matches_user_and_content_rules,\n            in_sort_scope: false,\n        }\n    }\n\n    /// Sets up the state necessary to collect rules from a given DOM tree\n    /// (either the document tree, or a shadow tree).\n    ///\n    /// All rules in the same tree need to be matched together, and this\n    /// function takes care of sorting them by specificity and source order.\n    #[inline]\n    fn in_tree(&mut self, host: Option<E>, f: impl FnOnce(&mut Self)) {\n        debug_assert!(!self.in_sort_scope, \"Nested sorting makes no sense\");\n        let start = self.rules.len();\n        self.in_sort_scope = true;\n        let old_host = self.context.current_host.take();\n        self.context.current_host = host.map(|e| e.opaque());\n        f(self);\n        if start != self.rules.len() {\n            self.rules[start..].sort_unstable_by_key(|block| block.sort_key());\n        }\n        self.context.current_host = old_host;\n        self.in_sort_scope = false;\n    }\n\n    #[inline]\n    fn in_shadow_tree(&mut self, host: E, f: impl FnOnce(&mut Self)) {\n        self.in_tree(Some(host), f);\n    }\n\n    fn collect_stylist_rules(&mut self, origin: Origin) {\n        let cascade_level = match origin {\n            Origin::UserAgent => CascadeLevel::new(CascadeOrigin::UA),\n            Origin::User => CascadeLevel::new(CascadeOrigin::User),\n            Origin::Author => CascadeLevel::same_tree_author_normal(),\n        };\n\n        let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);\n        let map = match cascade_data.normal_rules(&self.pseudo_elements) {\n            Some(m) => m,\n            None => return,\n        };\n\n        self.in_tree(None, |collector| {\n            collector.collect_rules_in_map(map, cascade_level, cascade_data);\n        });\n    }\n\n    fn collect_user_agent_rules(&mut self) {\n        self.collect_stylist_rules(Origin::UserAgent);\n        #[cfg(feature = \"gecko\")]\n        self.collect_view_transition_dynamic_rules();\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn collect_view_transition_dynamic_rules(&mut self) {\n        if !self\n            .pseudo_elements\n            .first()\n            .is_some_and(|p| p.is_named_view_transition())\n        {\n            return;\n        }\n        let len_before_vt_rules = self.rules.len();\n        self.element\n            .synthesize_view_transition_dynamic_rules(self.rules);\n        if cfg!(debug_assertions) && self.rules.len() != len_before_vt_rules {\n            for declaration in &self.rules[len_before_vt_rules..] {\n                assert_eq!(declaration.level(), CascadeLevel::new(CascadeOrigin::UA));\n            }\n        }\n    }\n\n    fn collect_user_rules(&mut self) {\n        if !self.matches_user_and_content_rules {\n            return;\n        }\n\n        self.collect_stylist_rules(Origin::User);\n    }\n\n    /// Presentational hints.\n    ///\n    /// These go before author rules, but after user rules, see:\n    /// https://drafts.csswg.org/css-cascade/#preshint\n    fn collect_presentational_hints(&mut self) {\n        if !self.pseudo_elements.is_empty() {\n            return;\n        }\n\n        let length_before_preshints = self.rules.len();\n        self.element\n            .synthesize_presentational_hints_for_legacy_attributes(\n                self.context.visited_handling(),\n                self.rules,\n            );\n        if cfg!(debug_assertions) && self.rules.len() != length_before_preshints {\n            for declaration in &self.rules[length_before_preshints..] {\n                assert_eq!(\n                    declaration.level(),\n                    CascadeLevel::new(CascadeOrigin::PresHints)\n                );\n            }\n        }\n    }\n\n    #[inline]\n    fn collect_rules_in_list(\n        &mut self,\n        part_rules: &[Rule],\n        cascade_level: CascadeLevel,\n        cascade_data: &CascadeData,\n    ) {\n        debug_assert!(self.in_sort_scope, \"Rules gotta be sorted\");\n        SelectorMap::get_matching_rules(\n            self.element,\n            part_rules,\n            &mut self.rules,\n            &mut self.context,\n            cascade_level,\n            cascade_data,\n            &self.stylist,\n        );\n    }\n\n    #[inline]\n    fn collect_rules_in_map(\n        &mut self,\n        map: &SelectorMap<Rule>,\n        cascade_level: CascadeLevel,\n        cascade_data: &CascadeData,\n    ) {\n        debug_assert!(self.in_sort_scope, \"Rules gotta be sorted\");\n        map.get_all_matching_rules(\n            self.element,\n            self.rule_hash_target,\n            &mut self.rules,\n            &mut self.context,\n            cascade_level,\n            cascade_data,\n            &self.stylist,\n        );\n    }\n\n    /// Collects the rules for the ::slotted pseudo-element and the :host\n    /// pseudo-class.\n    fn collect_host_and_slotted_rules(&mut self) {\n        let mut slots = SmallVec::<[_; 3]>::new();\n        let mut current = self.rule_hash_target.assigned_slot();\n        let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();\n\n        while let Some(slot) = current {\n            debug_assert!(\n                self.matches_user_and_content_rules,\n                \"We should not slot NAC anywhere\"\n            );\n            slots.push(slot);\n            current = slot.assigned_slot();\n            shadow_cascade_order.dec();\n        }\n\n        self.collect_host_rules(shadow_cascade_order);\n\n        // Match slotted rules in reverse order, so that the outer slotted rules\n        // come before the inner rules (and thus have less priority).\n        for slot in slots.iter().rev() {\n            shadow_cascade_order.inc();\n\n            let shadow = slot.containing_shadow().unwrap();\n            let data = match shadow.style_data() {\n                Some(d) => d,\n                None => continue,\n            };\n            let slotted_rules = match data.slotted_rules(&self.pseudo_elements) {\n                Some(r) => r,\n                None => continue,\n            };\n\n            self.in_shadow_tree(shadow.host(), |collector| {\n                let cascade_level = CascadeLevel::author_normal(shadow_cascade_order);\n                collector.collect_rules_in_map(slotted_rules, cascade_level, data);\n            });\n        }\n    }\n\n    fn collect_rules_from_containing_shadow_tree(&mut self) {\n        if !self.matches_user_and_content_rules {\n            return;\n        }\n\n        let containing_shadow = containing_shadow_ignoring_svg_use(self.rule_hash_target);\n        let containing_shadow = match containing_shadow {\n            Some(s) => s,\n            None => return,\n        };\n\n        self.matches_document_author_rules = false;\n\n        let cascade_data = match containing_shadow.style_data() {\n            Some(c) => c,\n            None => return,\n        };\n\n        let cascade_level = CascadeLevel::same_tree_author_normal();\n        self.in_shadow_tree(containing_shadow.host(), |collector| {\n            if let Some(map) = cascade_data.normal_rules(&collector.pseudo_elements) {\n                collector.collect_rules_in_map(map, cascade_level, cascade_data);\n            }\n\n            // Collect rules from :host::part() and such\n            let hash_target = collector.rule_hash_target;\n            if !hash_target.has_part_attr() {\n                return;\n            }\n\n            let part_rules = match cascade_data.part_rules(&collector.pseudo_elements) {\n                Some(p) => p,\n                None => return,\n            };\n\n            hash_target.each_part(|part| {\n                if let Some(part_rules) = part_rules.get(&part.0) {\n                    collector.collect_rules_in_list(part_rules, cascade_level, cascade_data);\n                }\n            });\n        });\n    }\n\n    /// Collects the rules for the :host pseudo-class.\n    fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {\n        let shadow = match self.rule_hash_target.shadow_root() {\n            Some(s) => s,\n            None => return,\n        };\n\n        let style_data = match shadow.style_data() {\n            Some(d) => d,\n            None => return,\n        };\n\n        let host_rules = match style_data.featureless_host_rules(&self.pseudo_elements) {\n            Some(rules) => rules,\n            None => return,\n        };\n\n        let rule_hash_target = self.rule_hash_target;\n        self.in_shadow_tree(rule_hash_target, |collector| {\n            let cascade_level = CascadeLevel::author_normal(shadow_cascade_order);\n            debug_assert!(!collector.context.featureless(), \"How?\");\n            collector.context.featureless = true;\n            collector.collect_rules_in_map(host_rules, cascade_level, style_data);\n            collector.context.featureless = false;\n        });\n    }\n\n    fn collect_document_author_rules(&mut self) {\n        if !self.matches_document_author_rules {\n            return;\n        }\n\n        self.collect_stylist_rules(Origin::Author);\n    }\n\n    fn collect_part_rules_from_outer_trees(&mut self) {\n        if !self.rule_hash_target.has_part_attr() {\n            return;\n        }\n\n        let mut inner_shadow = match self.rule_hash_target.containing_shadow() {\n            Some(s) => s,\n            None => return,\n        };\n\n        let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();\n\n        let mut parts = SmallVec::<[_; 3]>::new();\n        self.rule_hash_target.each_part(|p| parts.push(p.clone()));\n\n        loop {\n            if parts.is_empty() {\n                return;\n            }\n\n            let inner_shadow_host = inner_shadow.host();\n            let outer_shadow = inner_shadow_host.containing_shadow();\n            let cascade_data = match outer_shadow {\n                Some(shadow) => shadow.style_data(),\n                None => Some(\n                    self.stylist\n                        .cascade_data()\n                        .borrow_for_origin(Origin::Author),\n                ),\n            };\n\n            if let Some(cascade_data) = cascade_data {\n                if let Some(part_rules) = cascade_data.part_rules(&self.pseudo_elements) {\n                    let containing_host = outer_shadow.map(|s| s.host());\n                    let cascade_level = CascadeLevel::author_normal(shadow_cascade_order);\n                    self.in_tree(containing_host, |collector| {\n                        for p in &parts {\n                            if let Some(part_rules) = part_rules.get(&p.0) {\n                                collector.collect_rules_in_list(\n                                    part_rules,\n                                    cascade_level,\n                                    cascade_data,\n                                );\n                            }\n                        }\n                    });\n                    shadow_cascade_order.inc();\n                }\n            }\n\n            inner_shadow = match outer_shadow {\n                Some(s) => s,\n                None => break, // Nowhere to export to.\n            };\n\n            let mut new_parts = SmallVec::new();\n            for part in &parts {\n                inner_shadow_host.each_exported_part(part, |exported_part| {\n                    new_parts.push(exported_part.clone());\n                });\n            }\n            parts = new_parts;\n        }\n    }\n\n    fn collect_style_attribute(&mut self) {\n        if let Some(sa) = self.style_attribute {\n            self.rules\n                .push(ApplicableDeclarationBlock::from_declarations(\n                    sa.clone_arc(),\n                    CascadeLevel::same_tree_author_normal(),\n                    LayerOrder::style_attribute(),\n                ));\n        }\n    }\n\n    fn collect_animation_rules(&mut self) {\n        if let Some(so) = self.smil_override {\n            self.rules\n                .push(ApplicableDeclarationBlock::from_declarations(\n                    so.clone_arc(),\n                    CascadeLevel::new(CascadeOrigin::SMILOverride),\n                    LayerOrder::root(),\n                ));\n        }\n\n        // The animations sheet (CSS animations, script-generated\n        // animations, and CSS transitions that are no longer tied to CSS\n        // markup).\n        if let Some(anim) = self.animation_declarations.animations.take() {\n            self.rules\n                .push(ApplicableDeclarationBlock::from_declarations(\n                    anim,\n                    CascadeLevel::new(CascadeOrigin::Animations),\n                    LayerOrder::root(),\n                ));\n        }\n\n        // The transitions sheet (CSS transitions that are tied to CSS\n        // markup).\n        if let Some(anim) = self.animation_declarations.transitions.take() {\n            self.rules\n                .push(ApplicableDeclarationBlock::from_declarations(\n                    anim,\n                    CascadeLevel::new(CascadeOrigin::Transitions),\n                    LayerOrder::root(),\n                ));\n        }\n    }\n\n    /// Collects all the rules, leaving the result in `self.rules`.\n    ///\n    /// Note that `!important` rules are handled during rule tree insertion.\n    pub fn collect_all(mut self) {\n        self.collect_user_agent_rules();\n        self.collect_user_rules();\n        if self.rule_inclusion == RuleInclusion::DefaultOnly {\n            return;\n        }\n        self.collect_presentational_hints();\n        // FIXME(emilio): Should the author styles enabled stuff avoid the\n        // presentational hints from getting pushed? See bug 1505770.\n        if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {\n            return;\n        }\n        self.collect_host_and_slotted_rules();\n        self.collect_rules_from_containing_shadow_tree();\n        self.collect_document_author_rules();\n        self.collect_style_attribute();\n        self.collect_part_rules_from_outer_trees();\n        self.collect_animation_rules();\n    }\n}\n"
  },
  {
    "path": "style/rule_tree/core.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n\nuse crate::applicable_declarations::CascadePriority;\nuse crate::shared_lock::StylesheetGuards;\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};\nuse parking_lot::RwLock;\nuse smallvec::SmallVec;\nuse std::fmt;\nuse std::hash;\nuse std::io::Write;\nuse std::mem;\nuse std::ptr;\nuse std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering};\n\nuse super::map::{Entry, Map};\nuse super::unsafe_box::UnsafeBox;\nuse super::{CascadeLevel, CascadeOrigin, RuleCascadeFlags, StyleSource};\n\n/// The rule tree, the structure servo uses to preserve the results of selector\n/// matching.\n///\n/// This is organized as a tree of rules. When a node matches a set of rules,\n/// they're inserted in order in the tree, starting with the less specific one.\n///\n/// When a rule is inserted in the tree, other elements may share the path up to\n/// a given rule. If that's the case, we don't duplicate child nodes, but share\n/// them.\n///\n/// When the rule node refcount drops to zero, it doesn't get freed. It gets\n/// instead put into a free list, and it is potentially GC'd after a while.\n///\n/// That way, a rule node that represents a likely-to-match-again rule (like a\n/// :hover rule) can be reused if we haven't GC'd it yet.\n#[derive(Debug)]\npub struct RuleTree {\n    root: StrongRuleNode,\n}\n\nimpl Drop for RuleTree {\n    fn drop(&mut self) {\n        unsafe { self.swap_free_list_and_gc(ptr::null_mut()) }\n    }\n}\n\nimpl MallocSizeOf for RuleTree {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n        let mut stack = SmallVec::<[_; 32]>::new();\n        stack.push(self.root.clone());\n\n        while let Some(node) = stack.pop() {\n            n += unsafe { ops.malloc_size_of(&*node.p) };\n            let children = node.p.children.read();\n            children.shallow_size_of(ops);\n            for c in &*children {\n                stack.push(unsafe { c.upgrade() });\n            }\n        }\n\n        n\n    }\n}\n\n#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]\nstruct ChildKey(CascadePriority, ptr::NonNull<()>);\nunsafe impl Send for ChildKey {}\nunsafe impl Sync for ChildKey {}\n\nimpl RuleTree {\n    /// Construct a new rule tree.\n    pub fn new() -> Self {\n        RuleTree {\n            root: StrongRuleNode::new(Box::new(RuleNode::root())),\n        }\n    }\n\n    /// Get the root rule node.\n    pub fn root(&self) -> &StrongRuleNode {\n        &self.root\n    }\n\n    /// This can only be called when no other threads is accessing this tree.\n    pub fn gc(&self) {\n        unsafe { self.swap_free_list_and_gc(RuleNode::DANGLING_PTR) }\n    }\n\n    /// This can only be called when no other threads is accessing this tree.\n    pub fn maybe_gc(&self) {\n        #[cfg(debug_assertions)]\n        self.maybe_dump_stats();\n\n        if self.root.p.approximate_free_count.load(Ordering::Relaxed) > RULE_TREE_GC_INTERVAL {\n            self.gc();\n        }\n    }\n\n    #[cfg(debug_assertions)]\n    fn maybe_dump_stats(&self) {\n        use itertools::Itertools;\n        use std::cell::Cell;\n        use std::time::{Duration, Instant};\n\n        if !log_enabled!(log::Level::Trace) {\n            return;\n        }\n\n        const RULE_TREE_STATS_INTERVAL: Duration = Duration::from_secs(2);\n\n        thread_local! {\n            pub static LAST_STATS: Cell<Instant> = Cell::new(Instant::now());\n        };\n\n        let should_dump = LAST_STATS.with(|s| {\n            let now = Instant::now();\n            if now.duration_since(s.get()) < RULE_TREE_STATS_INTERVAL {\n                return false;\n            }\n            s.set(now);\n            true\n        });\n\n        if !should_dump {\n            return;\n        }\n\n        let mut children_count = rustc_hash::FxHashMap::default();\n\n        let mut stack = SmallVec::<[_; 32]>::new();\n        stack.push(self.root.clone());\n        while let Some(node) = stack.pop() {\n            let children = node.p.children.read();\n            *children_count.entry(children.len()).or_insert(0) += 1;\n            for c in &*children {\n                stack.push(unsafe { c.upgrade() });\n            }\n        }\n\n        trace!(\"Rule tree stats:\");\n        let counts = children_count.keys().sorted();\n        for count in counts {\n            trace!(\" {} - {}\", count, children_count[count]);\n        }\n    }\n\n    /// Steals the free list and drops its contents.\n    unsafe fn swap_free_list_and_gc(&self, ptr: *mut RuleNode) {\n        let root = &self.root.p;\n\n        debug_assert!(!root.next_free.load(Ordering::Relaxed).is_null());\n\n        // Reset the approximate free count to zero, as we are going to steal\n        // the free list.\n        root.approximate_free_count.store(0, Ordering::Relaxed);\n\n        // Steal the free list head. Memory loads on nodes while iterating it\n        // must observe any prior changes that occured so this requires\n        // acquire ordering, but there are no writes that need to be kept\n        // before this swap so there is no need for release.\n        let mut head = root.next_free.swap(ptr, Ordering::Acquire);\n\n        while head != RuleNode::DANGLING_PTR {\n            debug_assert!(!head.is_null());\n\n            let mut node = UnsafeBox::from_raw(head);\n\n            // The root node cannot go on the free list.\n            debug_assert!(node.root.is_some());\n\n            // The refcount of nodes on the free list never goes below 1.\n            debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);\n\n            // No one else is currently writing to that field. Get the address\n            // of the next node in the free list and replace it with null,\n            // other threads will now consider that this node is not on the\n            // free list.\n            head = node.next_free.swap(ptr::null_mut(), Ordering::Relaxed);\n\n            // This release write synchronises with the acquire fence in\n            // `WeakRuleNode::upgrade`, making sure that if `upgrade` observes\n            // decrements the refcount to 0, it will also observe the\n            // `node.next_free` swap to null above.\n            if node.refcount.fetch_sub(1, Ordering::Release) == 1 {\n                // And given it observed the null swap above, it will need\n                // `pretend_to_be_on_free_list` to finish its job, writing\n                // `RuleNode::DANGLING_PTR` in `node.next_free`.\n                RuleNode::pretend_to_be_on_free_list(&node);\n\n                // Drop this node now that we just observed its refcount going\n                // down to zero.\n                RuleNode::drop_without_free_list(&mut node);\n            }\n        }\n    }\n}\n\n/// The number of RuleNodes added to the free list before we will consider\n/// doing a GC when calling maybe_gc().  (The value is copied from Gecko,\n/// where it likely did not result from a rigorous performance analysis.)\nconst RULE_TREE_GC_INTERVAL: usize = 300;\n\n/// A node in the rule tree.\nstruct RuleNode {\n    /// The root node. Only the root has no root pointer, for obvious reasons.\n    root: Option<WeakRuleNode>,\n\n    /// The parent rule node. Only the root has no parent.\n    parent: Option<StrongRuleNode>,\n\n    /// The actual style source, either coming from a selector in a StyleRule,\n    /// or a raw property declaration block (like the style attribute).\n    ///\n    /// None for the root node.\n    source: Option<StyleSource>,\n\n    /// The cascade level + layer order this rule is positioned at.\n    cascade_priority: CascadePriority,\n\n    /// The refcount of this node.\n    ///\n    /// Starts at one. Incremented in `StrongRuleNode::clone` and\n    /// `WeakRuleNode::upgrade`. Decremented in `StrongRuleNode::drop`\n    /// and `RuleTree::swap_free_list_and_gc`.\n    ///\n    /// If a non-root node's refcount reaches zero, it is incremented back to at\n    /// least one in `RuleNode::pretend_to_be_on_free_list` until the caller who\n    /// observed it dropping to zero had a chance to try to remove it from its\n    /// parent's children list.\n    ///\n    /// The refcount should never be decremented to zero if the value in\n    /// `next_free` is not null.\n    refcount: AtomicUsize,\n\n    /// Only used for the root, stores the number of free rule nodes that are\n    /// around.\n    approximate_free_count: AtomicUsize,\n\n    /// The children of a given rule node. Children remove themselves from here\n    /// when they go away.\n    children: RwLock<Map<ChildKey, WeakRuleNode>>,\n\n    /// This field has two different meanings depending on whether this is the\n    /// root node or not.\n    ///\n    /// If it is the root, it represents the head of the free list. It may be\n    /// null, which means the free list is gone because the tree was dropped,\n    /// and it may be `RuleNode::DANGLING_PTR`, which means the free list is\n    /// empty.\n    ///\n    /// If it is not the root node, this field is either null if the node is\n    /// not on the free list, `RuleNode::DANGLING_PTR` if it is the last item\n    /// on the free list or the node is pretending to be on the free list, or\n    /// any valid non-null pointer representing the next item on the free list\n    /// after this one.\n    ///\n    /// See `RuleNode::push_on_free_list`, `swap_free_list_and_gc`, and\n    /// `WeakRuleNode::upgrade`.\n    ///\n    /// Two threads should never attempt to put the same node on the free list\n    /// both at the same time.\n    next_free: AtomicPtr<RuleNode>,\n}\n\n// On Gecko builds, hook into the leak checking machinery.\n#[cfg(feature = \"gecko_refcount_logging\")]\nmod gecko_leak_checking {\n    use super::RuleNode;\n    use std::mem::size_of;\n    use std::os::raw::{c_char, c_void};\n\n    extern \"C\" {\n        fn NS_LogCtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);\n        fn NS_LogDtor(aPtr: *mut c_void, aTypeName: *const c_char, aSize: u32);\n    }\n    static NAME: &'static [u8] = b\"RuleNode\\0\";\n\n    /// Logs the creation of a heap-allocated object to Gecko's leak-checking machinery.\n    pub(super) fn log_ctor(ptr: *const RuleNode) {\n        let s = NAME as *const [u8] as *const u8 as *const c_char;\n        unsafe {\n            NS_LogCtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);\n        }\n    }\n\n    /// Logs the destruction of a heap-allocated object to Gecko's leak-checking machinery.\n    pub(super) fn log_dtor(ptr: *const RuleNode) {\n        let s = NAME as *const [u8] as *const u8 as *const c_char;\n        unsafe {\n            NS_LogDtor(ptr as *mut c_void, s, size_of::<RuleNode>() as u32);\n        }\n    }\n}\n\n#[inline(always)]\nfn log_new(_ptr: *const RuleNode) {\n    #[cfg(feature = \"gecko_refcount_logging\")]\n    gecko_leak_checking::log_ctor(_ptr);\n}\n\n#[inline(always)]\nfn log_drop(_ptr: *const RuleNode) {\n    #[cfg(feature = \"gecko_refcount_logging\")]\n    gecko_leak_checking::log_dtor(_ptr);\n}\n\nimpl RuleNode {\n    const DANGLING_PTR: *mut Self = ptr::NonNull::dangling().as_ptr();\n\n    unsafe fn new(\n        root: WeakRuleNode,\n        parent: StrongRuleNode,\n        source: StyleSource,\n        cascade_priority: CascadePriority,\n    ) -> Self {\n        debug_assert!(root.p.parent.is_none());\n        RuleNode {\n            root: Some(root),\n            parent: Some(parent),\n            source: Some(source),\n            cascade_priority,\n            refcount: AtomicUsize::new(1),\n            children: Default::default(),\n            approximate_free_count: AtomicUsize::new(0),\n            next_free: AtomicPtr::new(ptr::null_mut()),\n        }\n    }\n\n    fn root() -> Self {\n        RuleNode {\n            root: None,\n            parent: None,\n            source: None,\n            cascade_priority: CascadePriority::new(\n                CascadeLevel::new(CascadeOrigin::UA),\n                LayerOrder::root(),\n                RuleCascadeFlags::empty(),\n            ),\n            refcount: AtomicUsize::new(1),\n            approximate_free_count: AtomicUsize::new(0),\n            children: Default::default(),\n            next_free: AtomicPtr::new(RuleNode::DANGLING_PTR),\n        }\n    }\n\n    fn key(&self) -> ChildKey {\n        ChildKey(\n            self.cascade_priority,\n            self.source\n                .as_ref()\n                .expect(\"Called key() on the root node\")\n                .key(),\n        )\n    }\n\n    /// Drops a node without ever putting it on the free list.\n    ///\n    /// Note that the node may not be dropped if we observe that its refcount\n    /// isn't zero anymore when we write-lock its parent's children map to\n    /// remove it.\n    ///\n    /// This loops over parents of dropped nodes if their own refcount reaches\n    /// zero to avoid recursion when dropping deep hierarchies of nodes.\n    ///\n    /// For non-root nodes, this should always be preceded by a call of\n    /// `RuleNode::pretend_to_be_on_free_list`.\n    unsafe fn drop_without_free_list(this: &mut UnsafeBox<Self>) {\n        // We clone the box and shadow the original one to be able to loop\n        // over its ancestors if they also need to be dropped.\n        let mut this = UnsafeBox::clone(this);\n        loop {\n            // If the node has a parent, we need to remove it from its parent's\n            // children list.\n            if let Some(parent) = this.parent.as_ref() {\n                debug_assert!(!this.next_free.load(Ordering::Relaxed).is_null());\n\n                // We lock the parent's children list, which means no other\n                // thread will have any more opportunity to resurrect the node\n                // anymore.\n                let mut children = parent.p.children.write();\n\n                this.next_free.store(ptr::null_mut(), Ordering::Relaxed);\n\n                // We decrement the counter to remove the \"pretend to be\n                // on the free list\" reference.\n                let old_refcount = this.refcount.fetch_sub(1, Ordering::Release);\n                debug_assert!(old_refcount != 0);\n                if old_refcount != 1 {\n                    // Other threads resurrected this node and those references\n                    // are still alive, we have nothing to do anymore.\n                    return;\n                }\n\n                // We finally remove the node from its parent's children list,\n                // there are now no other references to it and it cannot\n                // be resurrected anymore even after we unlock the list.\n                debug!(\n                    \"Remove from child list: {:?}, parent: {:?}\",\n                    this.as_mut_ptr(),\n                    this.parent.as_ref().map(|p| p.p.as_mut_ptr())\n                );\n                let weak = children.remove(&this.key(), |node| node.p.key()).unwrap();\n                assert_eq!(weak.p.as_mut_ptr(), this.as_mut_ptr());\n            } else {\n                debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());\n                debug_assert_eq!(this.refcount.load(Ordering::Relaxed), 0);\n            }\n\n            // We are going to drop this node for good this time, as per the\n            // usual refcounting protocol we need an acquire fence here before\n            // we run the destructor.\n            //\n            // See https://github.com/rust-lang/rust/pull/41714#issuecomment-298996916\n            // for why it doesn't matter whether this is a load or a fence.\n            atomic::fence(Ordering::Acquire);\n\n            // Remove the parent reference from the child to avoid\n            // recursively dropping it and putting it on the free list.\n            let parent = UnsafeBox::deref_mut(&mut this).parent.take();\n\n            // We now drop the actual box and its contents, no one should\n            // access the current value in `this` anymore.\n            log_drop(&*this);\n            UnsafeBox::drop(&mut this);\n\n            if let Some(parent) = parent {\n                // We will attempt to drop the node's parent without the free\n                // list, so we clone the inner unsafe box and forget the\n                // original parent to avoid running its `StrongRuleNode`\n                // destructor which would attempt to use the free list if it\n                // still exists.\n                this = UnsafeBox::clone(&parent.p);\n                mem::forget(parent);\n                if this.refcount.fetch_sub(1, Ordering::Release) == 1 {\n                    debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());\n                    if this.root.is_some() {\n                        RuleNode::pretend_to_be_on_free_list(&this);\n                    }\n                    // Parent also reached refcount zero, we loop to drop it.\n                    continue;\n                }\n            }\n\n            return;\n        }\n    }\n\n    /// Pushes this node on the tree's free list. Returns false if the free list\n    /// is gone. Should only be called after we decremented a node's refcount\n    /// to zero and pretended to be on the free list.\n    unsafe fn push_on_free_list(this: &UnsafeBox<Self>) -> bool {\n        let root = &this.root.as_ref().unwrap().p;\n\n        debug_assert!(this.refcount.load(Ordering::Relaxed) > 0);\n        debug_assert_eq!(this.next_free.load(Ordering::Relaxed), Self::DANGLING_PTR);\n\n        // Increment the approximate free count by one.\n        root.approximate_free_count.fetch_add(1, Ordering::Relaxed);\n\n        // If the compare-exchange operation fails in the loop, we will retry\n        // with the new head value, so this can be a relaxed load.\n        let mut head = root.next_free.load(Ordering::Relaxed);\n\n        while !head.is_null() {\n            // Two threads can never attempt to push the same node on the free\n            // list both at the same time, so whoever else pushed a node on the\n            // free list cannot have done so with this node.\n            debug_assert_ne!(head, this.as_mut_ptr());\n\n            // Store the current head of the free list in this node.\n            this.next_free.store(head, Ordering::Relaxed);\n\n            // Any thread acquiring the free list must observe the previous\n            // next_free changes that occured, hence the release ordering\n            // on success.\n            match root.next_free.compare_exchange_weak(\n                head,\n                this.as_mut_ptr(),\n                Ordering::Release,\n                Ordering::Relaxed,\n            ) {\n                Ok(_) => {\n                    // This node is now on the free list, caller should not use\n                    // the node anymore.\n                    return true;\n                },\n                Err(new_head) => head = new_head,\n            }\n        }\n\n        // Tree was dropped and free list has been destroyed. We did not push\n        // this node on the free list but we still pretend to be on the free\n        // list to be ready to call `drop_without_free_list`.\n        false\n    }\n\n    /// Makes the node pretend to be on the free list. This will increment the\n    /// refcount by 1 and store `Self::DANGLING_PTR` in `next_free`. This\n    /// method should only be called after caller decremented the refcount to\n    /// zero, with the null pointer stored in `next_free`.\n    unsafe fn pretend_to_be_on_free_list(this: &UnsafeBox<Self>) {\n        debug_assert_eq!(this.next_free.load(Ordering::Relaxed), ptr::null_mut());\n        this.refcount.fetch_add(1, Ordering::Relaxed);\n        this.next_free.store(Self::DANGLING_PTR, Ordering::Release);\n    }\n\n    fn as_mut_ptr(&self) -> *mut RuleNode {\n        self as *const RuleNode as *mut RuleNode\n    }\n}\n\npub(crate) struct WeakRuleNode {\n    p: UnsafeBox<RuleNode>,\n}\n\n/// A strong reference to a rule node.\npub struct StrongRuleNode {\n    p: UnsafeBox<RuleNode>,\n}\n\n#[cfg(feature = \"servo\")]\nmalloc_size_of::malloc_size_of_is_0!(StrongRuleNode);\n\nimpl StrongRuleNode {\n    fn new(n: Box<RuleNode>) -> Self {\n        debug_assert_eq!(n.parent.is_none(), !n.source.is_some());\n\n        log_new(&*n);\n\n        debug!(\"Creating rule node: {:p}\", &*n);\n\n        Self {\n            p: UnsafeBox::from_box(n),\n        }\n    }\n\n    unsafe fn from_unsafe_box(p: UnsafeBox<RuleNode>) -> Self {\n        Self { p }\n    }\n\n    unsafe fn downgrade(&self) -> WeakRuleNode {\n        WeakRuleNode {\n            p: UnsafeBox::clone(&self.p),\n        }\n    }\n\n    /// Get the parent rule node of this rule node.\n    pub fn parent(&self) -> Option<&StrongRuleNode> {\n        self.p.parent.as_ref()\n    }\n\n    pub(super) fn ensure_child(\n        &self,\n        root: &StrongRuleNode,\n        source: StyleSource,\n        cascade_priority: CascadePriority,\n    ) -> StrongRuleNode {\n        use parking_lot::RwLockUpgradableReadGuard;\n\n        debug_assert!(\n            self.p.cascade_priority <= cascade_priority,\n            \"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}\",\n            self.p.cascade_priority,\n            cascade_priority,\n            self.p.source,\n            source,\n        );\n\n        let key = ChildKey(cascade_priority, source.key());\n        let children = self.p.children.upgradable_read();\n        if let Some(child) = children.get(&key, |node| node.p.key()) {\n            // Sound to call because we read-locked the parent's children.\n            return unsafe { child.upgrade() };\n        }\n        let mut children = RwLockUpgradableReadGuard::upgrade(children);\n        match children.entry(key, |node| node.p.key()) {\n            Entry::Occupied(child) => {\n                // Sound to call because we write-locked the parent's children.\n                unsafe { child.upgrade() }\n            },\n            Entry::Vacant(entry) => unsafe {\n                let node = StrongRuleNode::new(Box::new(RuleNode::new(\n                    root.downgrade(),\n                    self.clone(),\n                    source,\n                    cascade_priority,\n                )));\n                // Sound to call because we still own a strong reference to\n                // this node, through the `node` variable itself that we are\n                // going to return to the caller.\n                entry.insert(node.downgrade());\n                node\n            },\n        }\n    }\n\n    /// Get the style source corresponding to this rule node. May return `None`\n    /// if it's the root node, which means that the node hasn't matched any\n    /// rules.\n    pub fn style_source(&self) -> Option<&StyleSource> {\n        self.p.source.as_ref()\n    }\n\n    /// The cascade priority.\n    #[inline]\n    pub fn cascade_priority(&self) -> CascadePriority {\n        self.p.cascade_priority\n    }\n\n    /// The cascade level.\n    #[inline]\n    pub fn cascade_level(&self) -> CascadeLevel {\n        self.cascade_priority().cascade_level()\n    }\n\n    /// The importance.\n    #[inline]\n    pub fn importance(&self) -> crate::properties::Importance {\n        self.cascade_level().importance()\n    }\n\n    /// Returns whether this node has any child, only intended for testing\n    /// purposes.\n    pub unsafe fn has_children_for_testing(&self) -> bool {\n        !self.p.children.read().is_empty()\n    }\n\n    pub(super) fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W, indent: usize) {\n        const INDENT_INCREMENT: usize = 4;\n\n        for _ in 0..indent {\n            let _ = write!(writer, \" \");\n        }\n\n        let _ = writeln!(\n            writer,\n            \" - {:p} (ref: {:?}, parent: {:?})\",\n            &*self.p,\n            self.p.refcount.load(Ordering::Relaxed),\n            self.parent().map(|p| &*p.p as *const RuleNode)\n        );\n\n        for _ in 0..indent {\n            let _ = write!(writer, \" \");\n        }\n\n        if let Some(source) = self.style_source() {\n            source.dump(self.cascade_level().guard(guards), writer);\n        } else {\n            if indent != 0 {\n                warn!(\"How has this happened?\");\n            }\n            let _ = write!(writer, \"(root)\");\n        }\n\n        let _ = write!(writer, \"\\n\");\n        for child in &*self.p.children.read() {\n            unsafe {\n                child\n                    .upgrade()\n                    .dump(guards, writer, indent + INDENT_INCREMENT);\n            }\n        }\n    }\n}\n\nimpl Clone for StrongRuleNode {\n    fn clone(&self) -> Self {\n        debug!(\n            \"{:p}: {:?}+\",\n            &*self.p,\n            self.p.refcount.load(Ordering::Relaxed)\n        );\n        debug_assert!(self.p.refcount.load(Ordering::Relaxed) > 0);\n        self.p.refcount.fetch_add(1, Ordering::Relaxed);\n        unsafe { StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p)) }\n    }\n}\n\nimpl Drop for StrongRuleNode {\n    #[cfg_attr(feature = \"servo\", allow(unused_mut))]\n    fn drop(&mut self) {\n        let node = &*self.p;\n        debug!(\"{:p}: {:?}-\", node, node.refcount.load(Ordering::Relaxed));\n        debug!(\n            \"Dropping node: {:p}, root: {:?}, parent: {:?}\",\n            node,\n            node.root.as_ref().map(|r| &*r.p as *const RuleNode),\n            node.parent.as_ref().map(|p| &*p.p as *const RuleNode)\n        );\n\n        let should_drop = {\n            debug_assert!(node.refcount.load(Ordering::Relaxed) > 0);\n            node.refcount.fetch_sub(1, Ordering::Release) == 1\n        };\n\n        if !should_drop {\n            // The refcount didn't even drop zero yet, there is nothing for us\n            // to do anymore.\n            return;\n        }\n\n        unsafe {\n            if node.root.is_some() {\n                // This is a non-root node and we just observed the refcount\n                // dropping to zero, we need to pretend to be on the free list\n                // to unstuck any thread who tried to resurrect this node first\n                // through `WeakRuleNode::upgrade`.\n                RuleNode::pretend_to_be_on_free_list(&self.p);\n\n                // Attempt to push the node on the free list. This may fail\n                // if the free list is gone.\n                if RuleNode::push_on_free_list(&self.p) {\n                    return;\n                }\n            }\n\n            // Either this was the last reference of the root node, or the\n            // tree rule is gone and there is no free list anymore. Drop the\n            // node.\n            RuleNode::drop_without_free_list(&mut self.p);\n        }\n    }\n}\n\nimpl WeakRuleNode {\n    /// Upgrades this weak node reference, returning a strong one.\n    ///\n    /// Must be called with items stored in a node's children list. The children\n    /// list must at least be read-locked when this is called.\n    unsafe fn upgrade(&self) -> StrongRuleNode {\n        debug!(\"Upgrading weak node: {:p}\", &*self.p);\n\n        if self.p.refcount.fetch_add(1, Ordering::Relaxed) == 0 {\n            // We observed a refcount of 0, we need to wait for this node to\n            // be put on the free list. Resetting the `next_free` pointer to\n            // null is only done in `RuleNode::drop_without_free_list`, just\n            // before a release refcount decrement, so this acquire fence here\n            // makes sure that we observed the write to null before we loop\n            // until there is a non-null value.\n            atomic::fence(Ordering::Acquire);\n            while self.p.next_free.load(Ordering::Relaxed).is_null() {}\n        }\n        StrongRuleNode::from_unsafe_box(UnsafeBox::clone(&self.p))\n    }\n}\n\nimpl fmt::Debug for StrongRuleNode {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        (&*self.p as *const RuleNode).fmt(f)\n    }\n}\n\nimpl Eq for StrongRuleNode {}\nimpl PartialEq for StrongRuleNode {\n    fn eq(&self, other: &Self) -> bool {\n        &*self.p as *const RuleNode == &*other.p\n    }\n}\n\nimpl hash::Hash for StrongRuleNode {\n    fn hash<H>(&self, state: &mut H)\n    where\n        H: hash::Hasher,\n    {\n        (&*self.p as *const RuleNode).hash(state)\n    }\n}\n\n// Large pages generate thousands of RuleNode objects.\nsize_of_test!(RuleNode, 80);\n// StrongRuleNode should be pointer-sized even inside an option.\nsize_of_test!(Option<StrongRuleNode>, 8);\n"
  },
  {
    "path": "style/rule_tree/level.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![forbid(unsafe_code)]\n\n//! The cascade level and shadow cascade order for tracking shadow tree rules.\n\nuse crate::derives::*;\nuse crate::properties::Importance;\nuse crate::shared_lock::{SharedRwLockReadGuard, StylesheetGuards};\nuse crate::stylesheets::Origin;\n\nuse std::cmp::{Ord, Ordering, PartialOrd};\n\n/// The cascade level these rules are relevant at, as per[1][2][3].\n///\n/// We store them as a bitfield with:\n///\n///  * Shadow cascade order (4 bits). See ShadowCascadeOrder for details.\n///  * Importance bit. Whether the declaration was !important or not.\n///  * The CascadeOrigin of the declaration as per [1].\n///\n/// Presentational hints for SVG and HTML are in the \"author-level zero-specificity\" level, that\n/// is, right after user rules, and before author rules.\n///\n/// See also [4] for the Shadow DOM bits. We rely on the invariant that rules\n/// from outside the tree the element is in can't affect the element.\n///\n/// The opposite is not true (i.e., :host and ::slotted) from an \"inner\" shadow tree may affect an\n/// element connected to the document or an \"outer\" shadow tree.\n///\n/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin\n/// [2]: https://drafts.csswg.org/css-cascade/#preshint\n/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints\n/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading\n#[repr(C)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    PartialEq,\n    Serialize,\n    Deserialize,\n    ToAnimatedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct CascadeLevel(u8);\nbitflags! {\n    impl CascadeLevel: u8 {\n        /// The three bits that represent the CascadeOrigin.\n        const ORIGIN_BITS = 0b00000111;\n        /// Whether the declarations are `!important` or not.\n        const IMPORTANT = 1 << 3;\n        /// The three bits for cascade order absolute value. If you change this, please change\n        /// CASCADE_ORDER_SHIFT accordingly.\n        const CASCADE_ORDER_BITS = 0b01110000;\n        /// The bit for the sign.\n        const CASCADE_ORDER_SIGN = 1 << 7;\n    }\n}\n\nmalloc_size_of::malloc_size_of_is_0!(CascadeLevel);\n\n/// The cascade origin of rules.\n///\n/// Presentational hints for SVG and HTML are in the \"author-level zero-specificity\" level, that\n/// is, right after user rules, and before author rules.\n///\n/// The order of variants declared here is significant, and must be in _ascending_ order of\n/// precedence.\n///\n/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin\n/// [2]: https://drafts.csswg.org/css-cascade/#cascade-origin\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Ord,\n    PartialEq,\n    PartialOrd,\n    Serialize,\n    ToAnimatedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum CascadeOrigin {\n    /// Normal User-Agent rules.\n    UA = 0,\n    /// User normal rules.\n    User,\n    /// Presentational hints.\n    PresHints,\n    /// Styles from author styles.\n    Author,\n    /// https://drafts.csswg.org/css-anchor-position-1/#position-fallback-origin\n    PositionFallback,\n    /// SVG SMIL animations.\n    SMILOverride,\n    /// CSS animations and script-generated animations.\n    Animations,\n    /// CSS Transitions\n    Transitions,\n}\n\nimpl CascadeOrigin {\n    /// Returns the \"simplified\" origin.\n    #[inline]\n    pub fn origin(self) -> Origin {\n        match self {\n            Self::UA => Origin::UserAgent,\n            Self::User => Origin::User,\n            _ => Origin::Author,\n        }\n    }\n\n    /// Returns whether this is an \"author\" origin (in the \"simplified\" sense of the word).\n    #[inline]\n    pub fn is_author_origin(self) -> bool {\n        self > Self::User\n    }\n\n    /// Select a lock guard for this origin.\n    #[inline]\n    pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {\n        match *self {\n            Self::UA | Self::User => guards.ua_or_user,\n            _ => guards.author,\n        }\n    }\n}\n\nimpl Ord for CascadeLevel {\n    fn cmp(&self, other: &Self) -> Ordering {\n        let self_important = self.is_important();\n        if self_important != other.is_important() {\n            return if self_important {\n                if other.origin() == CascadeOrigin::Transitions {\n                    // Transitions override all important rules.\n                    return Ordering::Less;\n                }\n                Ordering::Greater\n            } else {\n                if self.origin() == CascadeOrigin::Transitions {\n                    return Ordering::Greater;\n                }\n                Ordering::Less\n            };\n        }\n        let origin_cmp = self\n            .origin()\n            .cmp(&other.origin())\n            .then_with(|| self.shadow_order().cmp(&other.shadow_order()));\n        if self_important {\n            origin_cmp.reverse()\n        } else {\n            origin_cmp\n        }\n    }\n}\n\nimpl PartialOrd for CascadeLevel {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        Some(self.cmp(other))\n    }\n}\n\nimpl CascadeLevel {\n    /// The shift that we apply to the cascade order.\n    pub const CASCADE_ORDER_SHIFT: usize = 4;\n    /// The author level at the same tree. Here for convenience to avoid duplicating it from C++.\n    pub const SAME_TREE_AUTHOR_NORMAL: Self = Self(CascadeOrigin::Author as u8);\n\n    /// Returns a cascade level with a given origin.\n    pub fn new(origin: CascadeOrigin) -> Self {\n        let bits = origin as u8;\n        debug_assert_eq!(bits & Self::ORIGIN_BITS.bits(), bits);\n        Self(bits)\n    }\n\n    /// Returns the cascade origin.\n    #[inline]\n    pub fn origin(self) -> CascadeOrigin {\n        use num_traits::FromPrimitive;\n        let origin = (self & Self::ORIGIN_BITS).bits();\n        CascadeOrigin::from_u8(origin).unwrap()\n    }\n\n    /// Convert this level from \"unimportant\" to \"important\".\n    pub fn important(self) -> Self {\n        debug_assert!(\n            matches!(\n                self.origin(),\n                CascadeOrigin::UA | CascadeOrigin::User | CascadeOrigin::Author\n            ),\n            \"{self:?}\"\n        );\n        let mut result = self;\n        result.insert(Self::IMPORTANT);\n        result\n    }\n\n    /// Convert this level from \"important\" to \"non-important\".\n    pub fn unimportant(self) -> Self {\n        let mut result = self;\n        result.remove(Self::IMPORTANT);\n        result\n    }\n\n    /// Select a lock guard for this level\n    #[inline]\n    pub fn guard<'a>(&self, guards: &'a StylesheetGuards<'a>) -> &'a SharedRwLockReadGuard<'a> {\n        self.origin().guard(guards)\n    }\n\n    /// Returns the cascade level for author important declarations from the\n    /// same tree as the element.\n    #[inline]\n    pub fn same_tree_author_important() -> Self {\n        Self::new(CascadeOrigin::Author).important()\n    }\n\n    /// Returns the cascade level for author normal declarations from the same\n    /// tree as the element.\n    #[inline]\n    pub fn same_tree_author_normal() -> Self {\n        Self::new(CascadeOrigin::Author)\n    }\n\n    /// Returns whether this cascade level represents important rules of some\n    /// sort.\n    #[inline]\n    pub fn is_important(&self) -> bool {\n        self.intersects(Self::IMPORTANT)\n    }\n\n    /// Returns the importance relevant for this rule. Pretty similar to\n    /// `is_important`.\n    #[inline]\n    pub fn importance(&self) -> Importance {\n        if self.is_important() {\n            Importance::Important\n        } else {\n            Importance::Normal\n        }\n    }\n\n    /// Returns whether this cascade level represents an animation rules.\n    #[inline]\n    pub fn is_animation(&self) -> bool {\n        match self.origin() {\n            CascadeOrigin::SMILOverride\n            | CascadeOrigin::Animations\n            | CascadeOrigin::Transitions => true,\n            _ => false,\n        }\n    }\n\n    /// Returns whether this cascade level is tree.\n    #[inline]\n    pub fn is_tree(self) -> bool {\n        self.origin() == CascadeOrigin::Author\n    }\n\n    #[inline]\n    fn shadow_order(self) -> ShadowCascadeOrder {\n        let neg = self.intersects(Self::CASCADE_ORDER_SIGN);\n        let abs = (self & Self::CASCADE_ORDER_BITS).bits() >> Self::CASCADE_ORDER_SHIFT;\n        ShadowCascadeOrder(if neg { -(abs as i8) } else { abs as i8 })\n    }\n\n    /// Returns an author normal cascade level with the given shadow cascade order.\n    #[inline]\n    pub fn author_normal(shadow_cascade_order: ShadowCascadeOrder) -> Self {\n        let abs = (shadow_cascade_order.0.abs() as u8) << Self::CASCADE_ORDER_SHIFT;\n        let mut result = Self::new(CascadeOrigin::Author);\n        result |= Self::from_bits_truncate(abs);\n        result.set(Self::CASCADE_ORDER_SIGN, shadow_cascade_order.0 < 0);\n        result\n    }\n}\n\n/// A counter to track how many shadow root rules deep we are. This is used to\n/// handle:\n///\n/// https://drafts.csswg.org/css-scoping/#shadow-cascading\n///\n/// See the static functions for the meaning of different values.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]\n#[repr(transparent)]\npub struct ShadowCascadeOrder(i8);\n\nimpl ShadowCascadeOrder {\n    /// We keep a maximum of 3 bits of order as a limit so that we can pack CascadeLevel in 1 byte.\n    const MAX: i8 = 0b111;\n    const MIN: i8 = -Self::MAX;\n\n    /// A level for the outermost shadow tree (the shadow tree we own, and the\n    /// ones from the slots we're slotted in).\n    #[inline]\n    pub fn for_outermost_shadow_tree() -> Self {\n        Self(-1)\n    }\n\n    /// A level for the element's tree.\n    #[inline]\n    pub fn for_same_tree() -> Self {\n        Self(0)\n    }\n\n    /// A level for the innermost containing tree (the one closest to the\n    /// element).\n    #[inline]\n    pub fn for_innermost_containing_tree() -> Self {\n        Self(1)\n    }\n\n    /// Decrement the level, moving inwards. We should only move inwards if\n    /// we're traversing slots.\n    #[inline]\n    pub fn dec(&mut self) {\n        debug_assert!(self.0 < 0);\n        if self.0 != Self::MIN {\n            self.0 -= 1;\n        }\n    }\n\n    /// The level, moving inwards. We should only move inwards if we're\n    /// traversing slots.\n    #[inline]\n    pub fn inc(&mut self) {\n        debug_assert_ne!(self.0, -1);\n        if self.0 != Self::MAX {\n            self.0 += 1;\n        }\n    }\n}\n\nimpl std::ops::Neg for ShadowCascadeOrder {\n    type Output = Self;\n    #[inline]\n    fn neg(self) -> Self {\n        Self(self.0.neg())\n    }\n}\n"
  },
  {
    "path": "style/rule_tree/map.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![forbid(unsafe_code)]\n\nuse malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};\nuse rustc_hash::FxHashMap;\nuse std::collections::hash_map;\nuse std::hash::Hash;\nuse std::mem;\n\npub(super) struct Map<K, V> {\n    inner: MapInner<K, V>,\n}\n\nenum MapInner<K, V> {\n    Empty,\n    One(V),\n    Map(Box<FxHashMap<K, V>>),\n}\n\npub(super) struct MapIter<'a, K, V> {\n    inner: MapIterInner<'a, K, V>,\n}\n\nenum MapIterInner<'a, K, V> {\n    One(std::option::IntoIter<&'a V>),\n    Map(std::collections::hash_map::Values<'a, K, V>),\n}\n\npub(super) enum Entry<'a, K, V> {\n    Occupied(&'a mut V),\n    Vacant(VacantEntry<'a, K, V>),\n}\n\npub(super) struct VacantEntry<'a, K, V> {\n    inner: VacantEntryInner<'a, K, V>,\n}\n\nenum VacantEntryInner<'a, K, V> {\n    One(&'a mut MapInner<K, V>),\n    Map(hash_map::VacantEntry<'a, K, V>),\n}\n\nimpl<K, V> Default for Map<K, V> {\n    fn default() -> Self {\n        Map {\n            inner: MapInner::Empty,\n        }\n    }\n}\n\nimpl<'a, K, V> IntoIterator for &'a Map<K, V> {\n    type Item = &'a V;\n    type IntoIter = MapIter<'a, K, V>;\n\n    fn into_iter(self) -> Self::IntoIter {\n        MapIter {\n            inner: match &self.inner {\n                MapInner::Empty => MapIterInner::One(None.into_iter()),\n                MapInner::One(one) => MapIterInner::One(Some(one).into_iter()),\n                MapInner::Map(map) => MapIterInner::Map(map.values()),\n            },\n        }\n    }\n}\n\nimpl<'a, K, V> Iterator for MapIter<'a, K, V> {\n    type Item = &'a V;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        match &mut self.inner {\n            MapIterInner::One(one_iter) => one_iter.next(),\n            MapIterInner::Map(map_iter) => map_iter.next(),\n        }\n    }\n}\n\nimpl<K, V> Map<K, V>\nwhere\n    K: Eq + Hash,\n{\n    pub(super) fn is_empty(&self) -> bool {\n        match &self.inner {\n            MapInner::Empty => true,\n            MapInner::One(_) => false,\n            MapInner::Map(map) => map.is_empty(),\n        }\n    }\n\n    #[cfg(debug_assertions)]\n    pub(super) fn len(&self) -> usize {\n        match &self.inner {\n            MapInner::Empty => 0,\n            MapInner::One(_) => 1,\n            MapInner::Map(map) => map.len(),\n        }\n    }\n\n    pub(super) fn get(&self, key: &K, key_from_value: impl FnOnce(&V) -> K) -> Option<&V> {\n        match &self.inner {\n            MapInner::One(one) if *key == key_from_value(one) => Some(one),\n            MapInner::Map(map) => map.get(key),\n            MapInner::Empty | MapInner::One(_) => None,\n        }\n    }\n\n    pub(super) fn entry(\n        &mut self,\n        key: K,\n        key_from_value: impl FnOnce(&V) -> K,\n    ) -> Entry<'_, K, V> {\n        match self.inner {\n            ref mut inner @ MapInner::Empty => Entry::Vacant(VacantEntry {\n                inner: VacantEntryInner::One(inner),\n            }),\n            MapInner::One(_) => {\n                let one = match mem::replace(&mut self.inner, MapInner::Empty) {\n                    MapInner::One(one) => one,\n                    _ => unreachable!(),\n                };\n                // If this panics, the child `one` will be lost.\n                let one_key = key_from_value(&one);\n                // Same for the equality test.\n                if key == one_key {\n                    self.inner = MapInner::One(one);\n                    let one = match &mut self.inner {\n                        MapInner::One(one) => one,\n                        _ => unreachable!(),\n                    };\n                    return Entry::Occupied(one);\n                }\n                self.inner = MapInner::Map(Box::new(FxHashMap::with_capacity_and_hasher(\n                    2,\n                    Default::default(),\n                )));\n                let map = match &mut self.inner {\n                    MapInner::Map(map) => map,\n                    _ => unreachable!(),\n                };\n                map.insert(one_key, one);\n                match map.entry(key) {\n                    hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {\n                        inner: VacantEntryInner::Map(entry),\n                    }),\n                    _ => unreachable!(),\n                }\n            },\n            MapInner::Map(ref mut map) => match map.entry(key) {\n                hash_map::Entry::Occupied(entry) => Entry::Occupied(entry.into_mut()),\n                hash_map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry {\n                    inner: VacantEntryInner::Map(entry),\n                }),\n            },\n        }\n    }\n\n    pub(super) fn remove(&mut self, key: &K, key_from_value: impl FnOnce(&V) -> K) -> Option<V> {\n        match &mut self.inner {\n            MapInner::One(one) if *key == key_from_value(one) => {\n                match mem::replace(&mut self.inner, MapInner::Empty) {\n                    MapInner::One(one) => Some(one),\n                    _ => unreachable!(),\n                }\n            },\n            MapInner::Map(map) => map.remove(key),\n            MapInner::Empty | MapInner::One(_) => None,\n        }\n    }\n}\n\nimpl<'a, K, V> VacantEntry<'a, K, V> {\n    pub(super) fn insert(self, value: V) -> &'a mut V {\n        match self.inner {\n            VacantEntryInner::One(map) => {\n                *map = MapInner::One(value);\n                match map {\n                    MapInner::One(one) => one,\n                    _ => unreachable!(),\n                }\n            },\n            VacantEntryInner::Map(entry) => entry.insert(value),\n        }\n    }\n}\n\nimpl<K, V> MallocShallowSizeOf for Map<K, V>\nwhere\n    K: Eq + Hash,\n{\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        match &self.inner {\n            MapInner::Map(m) => {\n                // We want to account for both the box and the hashmap.\n                m.shallow_size_of(ops) + (**m).shallow_size_of(ops)\n            },\n            MapInner::One(_) | MapInner::Empty => 0,\n        }\n    }\n}\n"
  },
  {
    "path": "style/rule_tree/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![deny(unsafe_code)]\n\n//! The rule tree.\n\nuse crate::applicable_declarations::{ApplicableDeclarationList, CascadePriority};\nuse crate::properties::{LonghandIdSet, PropertyDeclarationBlock};\nuse crate::shared_lock::{Locked, StylesheetGuards};\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse servo_arc::ArcBorrow;\nuse smallvec::SmallVec;\nuse std::io::{self, Write};\n\nmod core;\npub mod level;\nmod map;\nmod source;\nmod unsafe_box;\n\npub use self::core::{RuleTree, StrongRuleNode};\npub use self::level::{CascadeLevel, CascadeOrigin, ShadowCascadeOrder};\npub use self::source::StyleSource;\n\nbitflags! {\n    /// Flags that are part of the cascade priority, and that we use to track\n    /// information about where the rule came from.\n    #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]\n    pub struct RuleCascadeFlags: u8 {\n        /// Whether the rule is inside a @starting-style block.\n        const STARTING_STYLE = 1 << 0;\n        /// Whether the rule is inside an @appearance-base block.\n        const APPEARANCE_BASE = 1 << 1;\n    }\n}\n\nmalloc_size_of::malloc_size_of_is_0!(RuleCascadeFlags);\n\nimpl RuleTree {\n    fn dump<W: Write>(&self, guards: &StylesheetGuards, writer: &mut W) {\n        let _ = writeln!(writer, \" + RuleTree\");\n        self.root().dump(guards, writer, 0);\n    }\n\n    /// Dump the rule tree to stdout.\n    pub fn dump_stdout(&self, guards: &StylesheetGuards) {\n        let mut stdout = io::stdout();\n        self.dump(guards, &mut stdout);\n    }\n\n    /// Inserts the given rules, that must be in proper order by specifity, and\n    /// returns the corresponding rule node representing the last inserted one.\n    ///\n    /// !important rules are detected and inserted into the appropriate position\n    /// in the rule tree. This allows selector matching to ignore importance,\n    /// while still maintaining the appropriate cascade order in the rule tree.\n    pub fn insert_ordered_rules_with_important<'a, I>(\n        &self,\n        iter: I,\n        guards: &StylesheetGuards,\n    ) -> StrongRuleNode\n    where\n        I: Iterator<Item = (StyleSource, CascadePriority)>,\n    {\n        let mut current = self.root().clone();\n\n        let mut found_important = false;\n\n        let mut important_author = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();\n        let mut important_user = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();\n        let mut important_ua = SmallVec::<[(StyleSource, CascadePriority); 4]>::new();\n        let mut transition = None;\n\n        for (source, priority) in iter {\n            let level = priority.cascade_level();\n            debug_assert!(!level.is_important(), \"Important levels handled internally\");\n\n            let any_important = {\n                let pdb = source.read(level.guard(guards));\n                pdb.any_important()\n            };\n\n            if any_important {\n                found_important = true;\n                match level.origin() {\n                    CascadeOrigin::Author => {\n                        important_author.push((source.clone(), priority.important()))\n                    },\n                    CascadeOrigin::UA => important_ua.push((source.clone(), priority.important())),\n                    CascadeOrigin::User => {\n                        important_user.push((source.clone(), priority.important()))\n                    },\n                    _ => {},\n                };\n            }\n\n            // We don't optimize out empty rules, even though we could.\n            //\n            // Inspector relies on every rule being inserted in the normal level\n            // at least once, in order to return the rules with the correct\n            // specificity order.\n            //\n            // TODO(emilio): If we want to apply these optimizations without\n            // breaking inspector's expectations, we'd need to run\n            // selector-matching again at the inspector's request. That may or\n            // may not be a better trade-off.\n            if level.origin() == CascadeOrigin::Transitions && found_important {\n                // There can be at most one transition, and it will come at\n                // the end of the iterator. Stash it and apply it after\n                // !important rules.\n                debug_assert!(transition.is_none());\n                transition = Some(source);\n            } else {\n                current = current.ensure_child(self.root(), source, priority);\n            }\n        }\n\n        // Early-return in the common case of no !important declarations.\n        if !found_important {\n            return current;\n        }\n\n        // Insert important declarations, in order of increasing importance,\n        // followed by any transition rule.\n        //\n        // Important rules are sorted differently from unimportant ones by\n        // shadow order and cascade order.\n        if !important_author.is_empty()\n            && important_author.first().unwrap().1 != important_author.last().unwrap().1\n        {\n            // We only need to sort if the important rules come from\n            // different trees, but we need this sort to be stable.\n            //\n            // FIXME(emilio): This could maybe be smarter, probably by chunking\n            // the important rules while inserting, and iterating the outer\n            // chunks in reverse order.\n            //\n            // That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,\n            // we're really only sorting the chunks, while keeping elements\n            // inside the same chunk already sorted. Seems like we could try to\n            // keep a SmallVec-of-SmallVecs with the chunks and just iterate the\n            // outer in reverse.\n            important_author.sort_by_key(|&(_, priority)| priority);\n        }\n\n        for (source, priority) in important_author.drain(..) {\n            current = current.ensure_child(self.root(), source, priority);\n        }\n\n        for (source, priority) in important_user.drain(..) {\n            current = current.ensure_child(self.root(), source, priority);\n        }\n\n        for (source, priority) in important_ua.drain(..) {\n            current = current.ensure_child(self.root(), source, priority);\n        }\n\n        if let Some(source) = transition {\n            current = current.ensure_child(\n                self.root(),\n                source,\n                CascadePriority::new(\n                    CascadeLevel::new(CascadeOrigin::Transitions),\n                    LayerOrder::root(),\n                    RuleCascadeFlags::empty(),\n                ),\n            );\n        }\n\n        current\n    }\n\n    /// Given a list of applicable declarations, insert the rules and return the\n    /// corresponding rule node.\n    pub fn compute_rule_node(\n        &self,\n        applicable_declarations: &mut ApplicableDeclarationList,\n        guards: &StylesheetGuards,\n    ) -> StrongRuleNode {\n        self.insert_ordered_rules_with_important(\n            applicable_declarations.drain(..).map(|d| d.for_rule_tree()),\n            guards,\n        )\n    }\n\n    /// Insert the given rules, that must be in proper order by specifity, and\n    /// return the corresponding rule node representing the last inserted one.\n    pub fn insert_ordered_rules<'a, I>(&self, iter: I) -> StrongRuleNode\n    where\n        I: Iterator<Item = (StyleSource, CascadePriority)>,\n    {\n        self.insert_ordered_rules_from(self.root().clone(), iter)\n    }\n\n    fn insert_ordered_rules_from<'a, I>(&self, from: StrongRuleNode, iter: I) -> StrongRuleNode\n    where\n        I: Iterator<Item = (StyleSource, CascadePriority)>,\n    {\n        let mut current = from;\n        for (source, priority) in iter {\n            current = current.ensure_child(self.root(), source, priority);\n        }\n        current\n    }\n\n    /// Replaces a rule in a given level (if present) for another rule.\n    ///\n    /// Returns the resulting node that represents the new path, or None if\n    /// the old path is still valid.\n    pub fn update_rule_at_level(\n        &self,\n        level: CascadeLevel,\n        layer_order: LayerOrder,\n        pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,\n        path: &StrongRuleNode,\n        guards: &StylesheetGuards,\n        important_rules_changed: &mut bool,\n    ) -> Option<StrongRuleNode> {\n        // TODO(emilio): Being smarter with lifetimes we could avoid a bit of\n        // the refcount churn.\n        let mut current = path.clone();\n        *important_rules_changed = false;\n\n        // First walk up until the first less-or-equally specific rule.\n        let mut children = SmallVec::<[_; 10]>::new();\n        while current.cascade_priority().cascade_level() > level {\n            children.push((\n                current.style_source().unwrap().clone(),\n                current.cascade_priority(),\n            ));\n            current = current.parent().unwrap().clone();\n        }\n\n        let cascade_priority = CascadePriority::new(level, layer_order, RuleCascadeFlags::empty());\n\n        // Then remove the one at the level we want to replace, if any.\n        //\n        // NOTE: Here we assume that only one rule can be at the level + priority we're replacing,\n        // which holds because we only use this for HTML style attribute, animations and transition\n        // rules.\n        //\n        // This is certainly true for HTML style attribute rules, animations, transitions, and\n        // SMIL.\n        if current.cascade_priority() == cascade_priority {\n            *important_rules_changed |= level.is_important();\n\n            let current_decls = current.style_source().unwrap().get();\n\n            // If the only rule at the level we're replacing is exactly the\n            // same as `pdb`, we're done, and `path` is still valid.\n            if let Some(ref pdb) = pdb {\n                // If the only rule at the level we're replacing is exactly the\n                // same as `pdb`, we're done, and `path` is still valid.\n                //\n                // TODO(emilio): Another potential optimization is the one where\n                // we can just replace the rule at that level for `pdb`, and\n                // then we don't need to re-create the children, and `path` is\n                // also equally valid. This is less likely, and would require an\n                // in-place mutation of the source, which is, at best, fiddly,\n                // so let's skip it for now.\n                let is_here_already = ArcBorrow::ptr_eq(pdb, &current_decls.borrow_arc());\n                if is_here_already {\n                    debug!(\"Picking the fast path in rule replacement\");\n                    return None;\n                }\n            }\n\n            current = current.parent().unwrap().clone();\n        }\n\n        // Insert the rule if it's relevant at this level in the cascade.\n        //\n        // These optimizations are likely to be important, because the levels where replacements\n        // apply (style and animations) tend to trigger pretty bad styling cases already.\n        if let Some(pdb) = pdb {\n            if level.is_important() {\n                if pdb.read_with(level.guard(guards)).any_important() {\n                    current = current.ensure_child(\n                        self.root(),\n                        StyleSource::from_declarations(pdb.clone_arc()),\n                        cascade_priority,\n                    );\n                    *important_rules_changed = true;\n                }\n            } else {\n                if pdb.read_with(level.guard(guards)).any_normal() {\n                    current = current.ensure_child(\n                        self.root(),\n                        StyleSource::from_declarations(pdb.clone_arc()),\n                        cascade_priority,\n                    );\n                }\n            }\n        }\n\n        // Now the rule is in the relevant place, push the children as\n        // necessary.\n        let rule = self.insert_ordered_rules_from(current, children.drain(..).rev());\n        Some(rule)\n    }\n\n    /// Returns whether this rule node has any @starting-style rule.\n    pub fn has_starting_style(path: &StrongRuleNode) -> bool {\n        path.self_and_ancestors().any(|node| {\n            node.cascade_priority()\n                .flags()\n                .intersects(RuleCascadeFlags::STARTING_STYLE)\n        })\n    }\n\n    /// Returns new rule nodes without Transitions level rule.\n    pub fn remove_transition_rule_if_applicable(path: &StrongRuleNode) -> StrongRuleNode {\n        // Return a clone if there is no transition level.\n        if path.cascade_level().origin() != CascadeOrigin::Transitions {\n            return path.clone();\n        }\n\n        path.parent().unwrap().clone()\n    }\n\n    /// Returns new rule node without rules from declarative animations.\n    pub fn remove_animation_rules(&self, path: &StrongRuleNode) -> StrongRuleNode {\n        // Return a clone if there are no animation rules.\n        if !path.has_animation_or_transition_rules() {\n            return path.clone();\n        }\n\n        let iter = path.self_and_ancestors().take_while(|node| {\n            node.cascade_level() >= CascadeLevel::new(CascadeOrigin::SMILOverride)\n        });\n        let mut last = path;\n        let mut children = SmallVec::<[_; 10]>::new();\n        for node in iter {\n            if !node.cascade_level().is_animation() {\n                children.push((\n                    node.style_source().unwrap().clone(),\n                    node.cascade_priority(),\n                ));\n            }\n            last = node;\n        }\n\n        let rule = self\n            .insert_ordered_rules_from(last.parent().unwrap().clone(), children.drain(..).rev());\n        rule\n    }\n}\n\nimpl StrongRuleNode {\n    /// Get an iterator for this rule node and its ancestors.\n    pub fn self_and_ancestors(&self) -> SelfAndAncestors<'_> {\n        SelfAndAncestors {\n            current: Some(self),\n        }\n    }\n\n    /// Returns true if there is either animation or transition level rule.\n    pub fn has_animation_or_transition_rules(&self) -> bool {\n        self.self_and_ancestors()\n            .take_while(|node| {\n                node.cascade_level() >= CascadeLevel::new(CascadeOrigin::SMILOverride)\n            })\n            .any(|node| node.cascade_level().is_animation())\n    }\n\n    /// Get a set of properties whose CascadeLevel are higher than Animations\n    /// but not equal to Transitions.\n    ///\n    /// If there are any custom properties, we set the boolean value of the\n    /// returned tuple to true.\n    pub fn get_properties_overriding_animations(\n        &self,\n        guards: &StylesheetGuards,\n    ) -> (LonghandIdSet, bool) {\n        use crate::properties::PropertyDeclarationId;\n\n        // We want to iterate over cascade levels that override the animations\n        // level, i.e.  !important levels and the transitions level.\n        //\n        // However, we actually want to skip the transitions level because\n        // although it is higher in the cascade than animations, when both\n        // transitions and animations are present for a given element and\n        // property, transitions are suppressed so that they don't actually\n        // override animations.\n        let iter = self\n            .self_and_ancestors()\n            .skip_while(|node| node.cascade_level().origin() == CascadeOrigin::Transitions)\n            .take_while(|node| node.cascade_level() > CascadeLevel::new(CascadeOrigin::Animations));\n        let mut result = (LonghandIdSet::new(), false);\n        for node in iter {\n            let style = node.style_source().unwrap();\n            for (decl, important) in style\n                .read(node.cascade_level().guard(guards))\n                .declaration_importance_iter()\n            {\n                // Although we are only iterating over cascade levels that\n                // override animations, in a given property declaration block we\n                // can have a mixture of !important and non-!important\n                // declarations but only the !important declarations actually\n                // override animations.\n                if important.important() {\n                    match decl.id() {\n                        PropertyDeclarationId::Longhand(id) => result.0.insert(id),\n                        PropertyDeclarationId::Custom(_) => result.1 = true,\n                    }\n                }\n            }\n        }\n        result\n    }\n}\n\n/// An iterator over a rule node and its ancestors.\n#[derive(Clone)]\npub struct SelfAndAncestors<'a> {\n    current: Option<&'a StrongRuleNode>,\n}\n\nimpl<'a> Iterator for SelfAndAncestors<'a> {\n    type Item = &'a StrongRuleNode;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.current.map(|node| {\n            self.current = node.parent();\n            node\n        })\n    }\n}\n"
  },
  {
    "path": "style/rule_tree/source.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![forbid(unsafe_code)]\n\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::{Locked, SharedRwLockReadGuard};\nuse servo_arc::Arc;\nuse std::io::Write;\nuse std::ptr;\n\n/// A style source for the rule node. It is a declaration block that may come from either a style\n/// rule or a standalone block like animations / transitions / smil / preshints / style attr...\n///\n/// Keeping the style rule around would provide more debugability, but also causes more\n/// pointer-chasing in the common code-path, which is undesired. If needed, we could keep it around\n/// in debug builds or something along those lines.\n#[derive(Clone, Debug)]\npub struct StyleSource(Arc<Locked<PropertyDeclarationBlock>>);\n\nimpl PartialEq for StyleSource {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl StyleSource {\n    #[inline]\n    pub(super) fn key(&self) -> ptr::NonNull<()> {\n        self.0.raw_ptr()\n    }\n\n    /// Creates a StyleSource from a PropertyDeclarationBlock.\n    #[inline]\n    pub fn from_declarations(decls: Arc<Locked<PropertyDeclarationBlock>>) -> Self {\n        Self(decls)\n    }\n\n    pub(super) fn dump<W: Write>(&self, guard: &SharedRwLockReadGuard, writer: &mut W) {\n        let _ = write!(writer, \"  -> {:?}\", self.read(guard).declarations());\n    }\n\n    /// Read the style source guard, and obtain thus read access to the\n    /// underlying property declaration block.\n    #[inline]\n    pub fn read<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a PropertyDeclarationBlock {\n        self.0.read_with(guard)\n    }\n\n    /// Returns the declaration block if applicable, otherwise None.\n    #[inline]\n    pub fn get(&self) -> &Arc<Locked<PropertyDeclarationBlock>> {\n        &self.0\n    }\n}\n"
  },
  {
    "path": "style/rule_tree/unsafe_box.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n\nuse std::mem::ManuallyDrop;\nuse std::ops::Deref;\nuse std::ptr;\n\n/// An unsafe box, derefs to `T`.\npub(super) struct UnsafeBox<T> {\n    inner: ManuallyDrop<Box<T>>,\n}\n\nimpl<T> UnsafeBox<T> {\n    /// Creates a new unsafe box.\n    pub(super) fn from_box(value: Box<T>) -> Self {\n        Self {\n            inner: ManuallyDrop::new(value),\n        }\n    }\n\n    /// Creates a new box from a pointer.\n    ///\n    /// # Safety\n    ///\n    /// The input should point to a valid `T`.\n    pub(super) unsafe fn from_raw(ptr: *mut T) -> Self {\n        Self {\n            inner: ManuallyDrop::new(Box::from_raw(ptr)),\n        }\n    }\n\n    /// Creates a new unsafe box from an existing one.\n    ///\n    /// # Safety\n    ///\n    /// There is no refcounting or whatever else in an unsafe box, so this\n    /// operation can lead to double frees.\n    pub(super) unsafe fn clone(this: &Self) -> Self {\n        Self {\n            inner: ptr::read(&this.inner),\n        }\n    }\n\n    /// Returns a mutable reference to the inner value of this unsafe box.\n    ///\n    /// # Safety\n    ///\n    /// Given `Self::clone`, nothing prevents anyone from creating\n    /// multiple mutable references to the inner value, which is completely UB.\n    pub(crate) unsafe fn deref_mut(this: &mut Self) -> &mut T {\n        &mut this.inner\n    }\n\n    /// Drops the inner value of this unsafe box.\n    ///\n    /// # Safety\n    ///\n    /// Given this doesn't consume the unsafe box itself, this has the same\n    /// safety caveats as `ManuallyDrop::drop`.\n    pub(super) unsafe fn drop(this: &mut Self) {\n        ManuallyDrop::drop(&mut this.inner)\n    }\n}\n\nimpl<T> Deref for UnsafeBox<T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        &self.inner\n    }\n}\n"
  },
  {
    "path": "style/scoped_tls.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Stack-scoped thread-local storage for rayon thread pools.\n\n#![allow(unsafe_code)]\n#![deny(missing_docs)]\n\nuse crate::global_style_data::STYLO_MAX_THREADS;\nuse std::cell::{Ref, RefCell, RefMut};\nuse std::ops::DerefMut;\n\n/// A scoped TLS set, that is alive during the `'scope` lifetime.\n///\n/// We use this on Servo to construct thread-local contexts, but clear them once\n/// we're done with restyling.\n///\n/// Note that the cleanup is done on the thread that owns the scoped TLS, thus\n/// the Send bound.\npub struct ScopedTLS<'scope, T: Send> {\n    pool: Option<&'scope rayon::ThreadPool>,\n    slots: [RefCell<Option<T>>; STYLO_MAX_THREADS],\n}\n\n/// The scoped TLS is `Sync` because no more than one worker thread can access a\n/// given slot.\nunsafe impl<'scope, T: Send> Sync for ScopedTLS<'scope, T> {}\n\nimpl<'scope, T: Send> ScopedTLS<'scope, T> {\n    /// Create a new scoped TLS that will last as long as this rayon threadpool\n    /// reference.\n    pub fn new(pool: Option<&'scope rayon::ThreadPool>) -> Self {\n        debug_assert!(pool.map_or(true, |p| p.current_num_threads() <= STYLO_MAX_THREADS));\n        ScopedTLS {\n            pool,\n            slots: Default::default(),\n        }\n    }\n\n    /// Returns the index corresponding to the calling thread in the thread pool.\n    #[inline]\n    pub fn current_thread_index(&self) -> usize {\n        self.pool.map_or(0, |p| p.current_thread_index().unwrap())\n    }\n\n    /// Return an immutable reference to the `Option<T>` that this thread owns.\n    pub fn borrow(&self) -> Ref<'_, Option<T>> {\n        let idx = self.current_thread_index();\n        self.slots[idx].borrow()\n    }\n\n    /// Return a mutable reference to the `Option<T>` that this thread owns.\n    pub fn borrow_mut(&self) -> RefMut<'_, Option<T>> {\n        let idx = self.current_thread_index();\n        self.slots[idx].borrow_mut()\n    }\n\n    /// Ensure that the current data this thread owns is initialized, or\n    /// initialize it using `f`.  We want ensure() to be fast and inline, and we\n    /// want to inline the memmove that initializes the Option<T>.  But we don't\n    /// want to inline space for the entire large T struct in our stack frame.\n    /// That's why we hand `f` a mutable borrow to write to instead of just\n    /// having it return a T.\n    #[inline(always)]\n    pub fn ensure<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<'_, T> {\n        let mut opt = self.borrow_mut();\n        if opt.is_none() {\n            f(opt.deref_mut());\n        }\n\n        RefMut::map(opt, |x| x.as_mut().unwrap())\n    }\n\n    /// Returns the slots. Safe because if we have a mut reference the tls can't be referenced by\n    /// any other thread.\n    pub fn slots(&mut self) -> &mut [RefCell<Option<T>>] {\n        &mut self.slots\n    }\n}\n"
  },
  {
    "path": "style/selector_map.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A data structure to efficiently index structs containing selectors by local\n//! name, ids and hash.\n\nuse crate::applicable_declarations::{ApplicableDeclarationList, ScopeProximity};\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::dom::TElement;\nuse crate::rule_tree::CascadeLevel;\nuse crate::selector_parser::SelectorImpl;\nuse crate::stylist::{CascadeData, ContainerConditionId, Rule, ScopeConditionId, Stylist};\nuse crate::AllocErr;\nuse crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};\nuse dom::ElementState;\nuse precomputed_hash::PrecomputedHash;\nuse selectors::matching::{matches_selector, MatchingContext};\nuse selectors::parser::{Combinator, Component, SelectorIter};\nuse smallvec::SmallVec;\nuse std::collections::hash_map;\nuse std::collections::{HashMap, HashSet};\nuse std::hash::{BuildHasherDefault, Hash, Hasher};\n\n/// A hasher implementation that doesn't hash anything, because it expects its\n/// input to be a suitable u32 hash.\npub struct PrecomputedHasher {\n    hash: Option<u32>,\n}\n\nimpl Default for PrecomputedHasher {\n    fn default() -> Self {\n        Self { hash: None }\n    }\n}\n\n/// A vector of relevant attributes, that can be useful for revalidation.\npub type RelevantAttributes = thin_vec::ThinVec<LocalName>;\n\n/// This is a set of pseudo-classes that are both relatively-rare (they don't\n/// affect most elements by default) and likely or known to have global rules\n/// (in e.g., the UA sheets).\n///\n/// We can avoid selector-matching those global rules for all elements without\n/// these pseudo-class states.\nconst RARE_PSEUDO_CLASS_STATES: ElementState = ElementState::from_bits_retain(\n    ElementState::FULLSCREEN.bits()\n        | ElementState::VISITED_OR_UNVISITED.bits()\n        | ElementState::URLTARGET.bits()\n        | ElementState::INERT.bits()\n        | ElementState::FOCUS.bits()\n        | ElementState::FOCUSRING.bits()\n        | ElementState::TOPMOST_MODAL.bits()\n        | ElementState::SUPPRESS_FOR_PRINT_SELECTION.bits()\n        | ElementState::ACTIVE_VIEW_TRANSITION.bits()\n        | ElementState::HEADING_LEVEL_BITS.bits(),\n);\n\n/// A simple alias for a hashmap using PrecomputedHasher.\npub type PrecomputedHashMap<K, V> = HashMap<K, V, BuildHasherDefault<PrecomputedHasher>>;\n\n/// A simple alias for a hashset using PrecomputedHasher.\npub type PrecomputedHashSet<K> = HashSet<K, BuildHasherDefault<PrecomputedHasher>>;\n\nimpl Hasher for PrecomputedHasher {\n    #[inline]\n    fn write(&mut self, _: &[u8]) {\n        unreachable!(\n            \"Called into PrecomputedHasher with something that isn't \\\n             a u32\"\n        )\n    }\n\n    #[inline]\n    fn write_u32(&mut self, i: u32) {\n        debug_assert!(self.hash.is_none());\n        self.hash = Some(i);\n    }\n\n    #[inline]\n    fn finish(&self) -> u64 {\n        self.hash.expect(\"PrecomputedHasher wasn't fed?\") as u64\n    }\n}\n\n/// A trait to abstract over a given selector map entry.\npub trait SelectorMapEntry: Sized + Clone {\n    /// Gets the selector we should use to index in the selector map.\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl>;\n}\n\n/// Map element data to selector-providing objects for which the last simple\n/// selector starts with them.\n///\n/// e.g.,\n/// \"p > img\" would go into the set of selectors corresponding to the\n/// element \"img\"\n/// \"a .foo .bar.baz\" would go into the set of selectors corresponding to\n/// the class \"bar\"\n///\n/// Because we match selectors right-to-left (i.e., moving up the tree\n/// from an element), we need to compare the last simple selector in the\n/// selector with the element.\n///\n/// So, if an element has ID \"id1\" and classes \"foo\" and \"bar\", then all\n/// the rules it matches will have their last simple selector starting\n/// either with \"#id1\" or with \".foo\" or with \".bar\".\n///\n/// Hence, the union of the rules keyed on each of element's classes, ID,\n/// element name, etc. will contain the Selectors that actually match that\n/// element.\n///\n/// We use a 1-entry SmallVec to avoid a separate heap allocation in the case\n/// where we only have one entry, which is quite common. See measurements in:\n/// * https://bugzilla.mozilla.org/show_bug.cgi?id=1363789#c5\n/// * https://bugzilla.mozilla.org/show_bug.cgi?id=681755\n///\n/// TODO: Tune the initial capacity of the HashMap\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct SelectorMap<T: 'static> {\n    /// Rules that have `:root` selectors.\n    pub root: SmallVec<[T; 1]>,\n    /// A hash from an ID to rules which contain that ID selector.\n    pub id_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,\n    /// A hash from a class name to rules which contain that class selector.\n    pub class_hash: MaybeCaseInsensitiveHashMap<Atom, SmallVec<[T; 1]>>,\n    /// A hash from local name to rules which contain that local name selector.\n    pub local_name_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,\n    /// A hash from attributes to rules which contain that attribute selector.\n    pub attribute_hash: PrecomputedHashMap<LocalName, SmallVec<[T; 1]>>,\n    /// A hash from namespace to rules which contain that namespace selector.\n    pub namespace_hash: PrecomputedHashMap<Namespace, SmallVec<[T; 1]>>,\n    /// Rules for pseudo-states that are rare but have global selectors.\n    pub rare_pseudo_classes: SmallVec<[T; 1]>,\n    /// All other rules.\n    pub other: SmallVec<[T; 1]>,\n    /// The number of entries in this map.\n    pub count: usize,\n}\n\nimpl<T: 'static> Default for SelectorMap<T> {\n    #[inline]\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl<T> SelectorMap<T> {\n    /// Trivially constructs an empty `SelectorMap`.\n    pub fn new() -> Self {\n        SelectorMap {\n            root: SmallVec::new(),\n            id_hash: MaybeCaseInsensitiveHashMap::new(),\n            class_hash: MaybeCaseInsensitiveHashMap::new(),\n            attribute_hash: HashMap::default(),\n            local_name_hash: HashMap::default(),\n            namespace_hash: HashMap::default(),\n            rare_pseudo_classes: SmallVec::new(),\n            other: SmallVec::new(),\n            count: 0,\n        }\n    }\n\n    /// Shrink the capacity of the map if needed.\n    pub fn shrink_if_needed(&mut self) {\n        self.id_hash.shrink_if_needed();\n        self.class_hash.shrink_if_needed();\n        self.attribute_hash.shrink_if_needed();\n        self.local_name_hash.shrink_if_needed();\n        self.namespace_hash.shrink_if_needed();\n    }\n\n    /// Clears the hashmap retaining storage.\n    pub fn clear(&mut self) {\n        self.root.clear();\n        self.id_hash.clear();\n        self.class_hash.clear();\n        self.attribute_hash.clear();\n        self.local_name_hash.clear();\n        self.namespace_hash.clear();\n        self.rare_pseudo_classes.clear();\n        self.other.clear();\n        self.count = 0;\n    }\n\n    /// Returns whether there are any entries in the map.\n    pub fn is_empty(&self) -> bool {\n        self.count == 0\n    }\n\n    /// Returns the number of entries.\n    pub fn len(&self) -> usize {\n        self.count\n    }\n}\n\nimpl SelectorMap<Rule> {\n    /// Append to `rule_list` all Rules in `self` that match element.\n    ///\n    /// Extract matching rules as per element's ID, classes, tag name, etc..\n    /// Sort the Rules at the end to maintain cascading order.\n    pub fn get_all_matching_rules<E>(\n        &self,\n        element: E,\n        rule_hash_target: E,\n        matching_rules_list: &mut ApplicableDeclarationList,\n        matching_context: &mut MatchingContext<E::Impl>,\n        cascade_level: CascadeLevel,\n        cascade_data: &CascadeData,\n        stylist: &Stylist,\n    ) where\n        E: TElement,\n    {\n        if self.is_empty() {\n            return;\n        }\n\n        let quirks_mode = matching_context.quirks_mode();\n\n        if rule_hash_target.is_root() {\n            SelectorMap::get_matching_rules(\n                element,\n                &self.root,\n                matching_rules_list,\n                matching_context,\n                cascade_level,\n                cascade_data,\n                stylist,\n            );\n        }\n\n        if let Some(id) = rule_hash_target.id() {\n            if let Some(rules) = self.id_hash.get(id, quirks_mode) {\n                SelectorMap::get_matching_rules(\n                    element,\n                    rules,\n                    matching_rules_list,\n                    matching_context,\n                    cascade_level,\n                    cascade_data,\n                    stylist,\n                )\n            }\n        }\n\n        rule_hash_target.each_class(|class| {\n            if let Some(rules) = self.class_hash.get(&class, quirks_mode) {\n                SelectorMap::get_matching_rules(\n                    element,\n                    rules,\n                    matching_rules_list,\n                    matching_context,\n                    cascade_level,\n                    cascade_data,\n                    stylist,\n                )\n            }\n        });\n\n        rule_hash_target.each_attr_name(|name| {\n            if let Some(rules) = self.attribute_hash.get(name) {\n                SelectorMap::get_matching_rules(\n                    element,\n                    rules,\n                    matching_rules_list,\n                    matching_context,\n                    cascade_level,\n                    cascade_data,\n                    stylist,\n                )\n            }\n        });\n\n        if let Some(rules) = self.local_name_hash.get(rule_hash_target.local_name()) {\n            SelectorMap::get_matching_rules(\n                element,\n                rules,\n                matching_rules_list,\n                matching_context,\n                cascade_level,\n                cascade_data,\n                stylist,\n            )\n        }\n\n        if rule_hash_target\n            .state()\n            .intersects(RARE_PSEUDO_CLASS_STATES)\n        {\n            SelectorMap::get_matching_rules(\n                element,\n                &self.rare_pseudo_classes,\n                matching_rules_list,\n                matching_context,\n                cascade_level,\n                cascade_data,\n                stylist,\n            );\n        }\n\n        if let Some(rules) = self.namespace_hash.get(rule_hash_target.namespace()) {\n            SelectorMap::get_matching_rules(\n                element,\n                rules,\n                matching_rules_list,\n                matching_context,\n                cascade_level,\n                cascade_data,\n                stylist,\n            )\n        }\n\n        SelectorMap::get_matching_rules(\n            element,\n            &self.other,\n            matching_rules_list,\n            matching_context,\n            cascade_level,\n            cascade_data,\n            stylist,\n        );\n    }\n\n    /// Adds rules in `rules` that match `element` to the `matching_rules` list.\n    pub(crate) fn get_matching_rules<E>(\n        element: E,\n        rules: &[Rule],\n        matching_rules: &mut ApplicableDeclarationList,\n        matching_context: &mut MatchingContext<E::Impl>,\n        cascade_level: CascadeLevel,\n        cascade_data: &CascadeData,\n        stylist: &Stylist,\n    ) where\n        E: TElement,\n    {\n        for rule in rules {\n            let scope_proximity = if rule.scope_condition_id == ScopeConditionId::none() {\n                if !matches_selector(\n                    &rule.selector,\n                    0,\n                    Some(&rule.hashes),\n                    &element,\n                    matching_context,\n                ) {\n                    continue;\n                }\n                ScopeProximity::infinity()\n            } else {\n                let result =\n                    cascade_data.find_scope_proximity_if_matching(rule, element, matching_context);\n                if result == ScopeProximity::infinity() {\n                    continue;\n                }\n                result\n            };\n\n            if rule.container_condition_id != ContainerConditionId::none() {\n                if !cascade_data.container_condition_matches(\n                    rule.container_condition_id,\n                    stylist,\n                    element,\n                    matching_context,\n                ) {\n                    continue;\n                }\n            }\n            matching_rules.push(rule.to_applicable_declaration_block(\n                cascade_level,\n                cascade_data,\n                scope_proximity,\n            ));\n        }\n    }\n}\n\nimpl<T: SelectorMapEntry> SelectorMap<T> {\n    /// Inserts an entry into the correct bucket(s).\n    pub fn insert(&mut self, entry: T, quirks_mode: QuirksMode) -> Result<(), AllocErr> {\n        self.count += 1;\n\n        // NOTE(emilio): It'd be nice for this to be a separate function, but\n        // then the compiler can't reason about the lifetime dependency between\n        // `entry` and `bucket`, and would force us to clone the rule in the\n        // common path.\n        macro_rules! insert_into_bucket {\n            ($entry:ident, $bucket:expr) => {{\n                let vec = match $bucket {\n                    Bucket::Root => &mut self.root,\n                    Bucket::ID(id) => self\n                        .id_hash\n                        .try_entry(id.clone(), quirks_mode)?\n                        .or_default(),\n                    Bucket::Class(class) => self\n                        .class_hash\n                        .try_entry(class.clone(), quirks_mode)?\n                        .or_default(),\n                    Bucket::Attribute { name, lower_name }\n                    | Bucket::LocalName { name, lower_name } => {\n                        // If the local name in the selector isn't lowercase,\n                        // insert it into the rule hash twice. This means that,\n                        // during lookup, we can always find the rules based on\n                        // the local name of the element, regardless of whether\n                        // it's an html element in an html document (in which\n                        // case we match against lower_name) or not (in which\n                        // case we match against name).\n                        //\n                        // In the case of a non-html-element-in-html-document\n                        // with a lowercase localname and a non-lowercase\n                        // selector, the rulehash lookup may produce superfluous\n                        // selectors, but the subsequent selector matching work\n                        // will filter them out.\n                        let is_attribute = matches!($bucket, Bucket::Attribute { .. });\n                        let hash = if is_attribute {\n                            &mut self.attribute_hash\n                        } else {\n                            &mut self.local_name_hash\n                        };\n                        if name != lower_name {\n                            hash.try_reserve(1)?;\n                            let vec = hash.entry(lower_name.clone()).or_default();\n                            vec.try_reserve(1)?;\n                            vec.push($entry.clone());\n                        }\n                        hash.try_reserve(1)?;\n                        hash.entry(name.clone()).or_default()\n                    },\n                    Bucket::Namespace(url) => {\n                        self.namespace_hash.try_reserve(1)?;\n                        self.namespace_hash.entry(url.clone()).or_default()\n                    },\n                    Bucket::RarePseudoClasses => &mut self.rare_pseudo_classes,\n                    Bucket::Universal => &mut self.other,\n                };\n                vec.try_reserve(1)?;\n                vec.push($entry);\n            }};\n        }\n\n        let bucket = {\n            let mut disjoint_buckets = SmallVec::new();\n            let bucket = find_bucket(entry.selector(), &mut disjoint_buckets);\n\n            // See if inserting this selector in multiple entries in the\n            // selector map would be worth it. Consider a case like:\n            //\n            //   .foo:where(div, #bar)\n            //\n            // There, `bucket` would be `Class(foo)`, and disjoint_buckets would\n            // be `[LocalName { div }, ID(bar)]`.\n            //\n            // Here we choose to insert the selector in the `.foo` bucket in\n            // such a case, as it's likely more worth it than inserting it in\n            // both `div` and `#bar`.\n            //\n            // This is specially true if there's any universal selector in the\n            // `disjoint_selectors` set, at which point we'd just be doing\n            // wasted work.\n            if !disjoint_buckets.is_empty()\n                && disjoint_buckets\n                    .iter()\n                    .all(|b| b.more_specific_than(&bucket))\n            {\n                for bucket in &disjoint_buckets {\n                    let entry = entry.clone();\n                    insert_into_bucket!(entry, *bucket);\n                }\n                return Ok(());\n            }\n            bucket\n        };\n\n        insert_into_bucket!(entry, bucket);\n        Ok(())\n    }\n\n    /// Looks up entries by id, class, local name, namespace, and other (in\n    /// order).\n    ///\n    /// Each entry is passed to the callback, which returns true to continue\n    /// iterating entries, or false to terminate the lookup.\n    ///\n    /// Returns false if the callback ever returns false.\n    ///\n    /// FIXME(bholley) This overlaps with SelectorMap<Rule>::get_all_matching_rules,\n    /// but that function is extremely hot and I'd rather not rearrange it.\n    pub fn lookup<'a, E, F>(\n        &'a self,\n        element: E,\n        quirks_mode: QuirksMode,\n        relevant_attributes: Option<&mut RelevantAttributes>,\n        f: F,\n    ) -> bool\n    where\n        E: TElement,\n        F: FnMut(&'a T) -> bool,\n    {\n        self.lookup_with_state(\n            element,\n            element.state(),\n            quirks_mode,\n            relevant_attributes,\n            f,\n        )\n    }\n\n    #[inline]\n    fn lookup_with_state<'a, E, F>(\n        &'a self,\n        element: E,\n        element_state: ElementState,\n        quirks_mode: QuirksMode,\n        mut relevant_attributes: Option<&mut RelevantAttributes>,\n        mut f: F,\n    ) -> bool\n    where\n        E: TElement,\n        F: FnMut(&'a T) -> bool,\n    {\n        if element.is_root() {\n            for entry in self.root.iter() {\n                if !f(&entry) {\n                    return false;\n                }\n            }\n        }\n\n        if let Some(id) = element.id() {\n            if let Some(v) = self.id_hash.get(id, quirks_mode) {\n                for entry in v.iter() {\n                    if !f(&entry) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        let mut done = false;\n        element.each_class(|class| {\n            if done {\n                return;\n            }\n            if let Some(v) = self.class_hash.get(class, quirks_mode) {\n                for entry in v.iter() {\n                    if !f(&entry) {\n                        done = true;\n                        return;\n                    }\n                }\n            }\n        });\n\n        if done {\n            return false;\n        }\n\n        element.each_attr_name(|name| {\n            if done {\n                return;\n            }\n            if let Some(v) = self.attribute_hash.get(name) {\n                if let Some(ref mut relevant_attributes) = relevant_attributes {\n                    relevant_attributes.push(name.clone());\n                }\n                for entry in v.iter() {\n                    if !f(&entry) {\n                        done = true;\n                        return;\n                    }\n                }\n            }\n        });\n\n        if done {\n            return false;\n        }\n\n        if let Some(v) = self.local_name_hash.get(element.local_name()) {\n            for entry in v.iter() {\n                if !f(&entry) {\n                    return false;\n                }\n            }\n        }\n\n        if let Some(v) = self.namespace_hash.get(element.namespace()) {\n            for entry in v.iter() {\n                if !f(&entry) {\n                    return false;\n                }\n            }\n        }\n\n        if element_state.intersects(RARE_PSEUDO_CLASS_STATES) {\n            for entry in self.rare_pseudo_classes.iter() {\n                if !f(&entry) {\n                    return false;\n                }\n            }\n        }\n\n        for entry in self.other.iter() {\n            if !f(&entry) {\n                return false;\n            }\n        }\n\n        true\n    }\n\n    /// Performs a normal lookup, and also looks up entries for the passed-in\n    /// id and classes.\n    ///\n    /// Each entry is passed to the callback, which returns true to continue\n    /// iterating entries, or false to terminate the lookup.\n    ///\n    /// Returns false if the callback ever returns false.\n    #[inline]\n    pub fn lookup_with_additional<'a, E, F>(\n        &'a self,\n        element: E,\n        quirks_mode: QuirksMode,\n        additional_id: Option<&WeakAtom>,\n        additional_classes: &[Atom],\n        additional_states: ElementState,\n        mut f: F,\n    ) -> bool\n    where\n        E: TElement,\n        F: FnMut(&'a T) -> bool,\n    {\n        // Do the normal lookup.\n        if !self.lookup_with_state(\n            element,\n            element.state() | additional_states,\n            quirks_mode,\n            /* relevant_attributes = */ None,\n            |entry| f(entry),\n        ) {\n            return false;\n        }\n\n        // Check the additional id.\n        if let Some(id) = additional_id {\n            if let Some(v) = self.id_hash.get(id, quirks_mode) {\n                for entry in v.iter() {\n                    if !f(&entry) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        // Check the additional classes.\n        for class in additional_classes {\n            if let Some(v) = self.class_hash.get(class, quirks_mode) {\n                for entry in v.iter() {\n                    if !f(&entry) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        true\n    }\n}\n\n#[derive(PartialEq)]\nenum Bucket<'a> {\n    Universal,\n    Namespace(&'a Namespace),\n    RarePseudoClasses,\n    LocalName {\n        name: &'a LocalName,\n        lower_name: &'a LocalName,\n    },\n    Attribute {\n        name: &'a LocalName,\n        lower_name: &'a LocalName,\n    },\n    Class(&'a Atom),\n    ID(&'a Atom),\n    Root,\n}\n\nimpl<'a> Bucket<'a> {\n    /// root > id > class > local name > namespace > pseudo-classes > universal.\n    #[inline]\n    fn specificity(&self) -> usize {\n        match *self {\n            Bucket::Universal => 0,\n            Bucket::Namespace(..) => 1,\n            Bucket::RarePseudoClasses => 2,\n            Bucket::LocalName { .. } => 3,\n            Bucket::Attribute { .. } => 4,\n            Bucket::Class(..) => 5,\n            Bucket::ID(..) => 6,\n            Bucket::Root => 7,\n        }\n    }\n\n    #[inline]\n    fn more_or_equally_specific_than(&self, other: &Self) -> bool {\n        self.specificity() >= other.specificity()\n    }\n\n    #[inline]\n    fn more_specific_than(&self, other: &Self) -> bool {\n        self.specificity() > other.specificity()\n    }\n}\n\ntype DisjointBuckets<'a> = SmallVec<[Bucket<'a>; 5]>;\n\nfn specific_bucket_for<'a>(\n    component: &'a Component<SelectorImpl>,\n    disjoint_buckets: &mut DisjointBuckets<'a>,\n) -> Bucket<'a> {\n    match *component {\n        Component::Root => Bucket::Root,\n        Component::ID(ref id) => Bucket::ID(id),\n        Component::Class(ref class) => Bucket::Class(class),\n        Component::AttributeInNoNamespace { ref local_name, .. } => Bucket::Attribute {\n            name: local_name,\n            lower_name: local_name,\n        },\n        Component::AttributeInNoNamespaceExists {\n            ref local_name,\n            ref local_name_lower,\n        } => Bucket::Attribute {\n            name: local_name,\n            lower_name: local_name_lower,\n        },\n        Component::AttributeOther(ref selector) => Bucket::Attribute {\n            name: &selector.local_name,\n            lower_name: &selector.local_name_lower,\n        },\n        Component::LocalName(ref selector) => Bucket::LocalName {\n            name: &selector.name,\n            lower_name: &selector.lower_name,\n        },\n        Component::Namespace(_, ref url) | Component::DefaultNamespace(ref url) => {\n            Bucket::Namespace(url)\n        },\n        // ::slotted(..) isn't a normal pseudo-element, so we can insert it on\n        // the rule hash normally without much problem. For example, in a\n        // selector like:\n        //\n        //   div::slotted(span)::before\n        //\n        // It looks like:\n        //\n        //  [\n        //    LocalName(div),\n        //    Combinator(SlotAssignment),\n        //    Slotted(span),\n        //    Combinator::PseudoElement,\n        //    PseudoElement(::before),\n        //  ]\n        //\n        // So inserting `span` in the rule hash makes sense since we want to\n        // match the slotted <span>.\n        Component::Slotted(ref selector) => find_bucket(selector.iter(), disjoint_buckets),\n        Component::Host(Some(ref selector)) => find_bucket(selector.iter(), disjoint_buckets),\n        Component::Is(ref list) | Component::Where(ref list) => {\n            if list.len() == 1 {\n                find_bucket(list.slice()[0].iter(), disjoint_buckets)\n            } else {\n                for selector in list.slice() {\n                    let bucket = find_bucket(selector.iter(), disjoint_buckets);\n                    if disjoint_buckets.last() == Some(&bucket) {\n                        // It's pretty common to have selectors like:\n                        //   input:is([type=foo], [type=bar], ...)\n                        // Try to prevent trivial duplicate entries for the same bucket.\n                        continue;\n                    }\n                    disjoint_buckets.push(bucket);\n                }\n                Bucket::Universal\n            }\n        },\n        Component::NonTSPseudoClass(ref pseudo_class)\n            if pseudo_class\n                .state_flag()\n                .intersects(RARE_PSEUDO_CLASS_STATES) =>\n        {\n            Bucket::RarePseudoClasses\n        },\n        _ => Bucket::Universal,\n    }\n}\n\n/// Searches a compound selector from left to right, and returns the appropriate\n/// bucket for it.\n///\n/// It also populates disjoint_buckets with dependencies from nested selectors\n/// with any semantics like :is() and :where().\n#[inline(always)]\nfn find_bucket<'a>(\n    mut iter: SelectorIter<'a, SelectorImpl>,\n    disjoint_buckets: &mut DisjointBuckets<'a>,\n) -> Bucket<'a> {\n    let mut current_bucket = Bucket::Universal;\n\n    loop {\n        for ss in &mut iter {\n            let new_bucket = specific_bucket_for(ss, disjoint_buckets);\n            // NOTE: When presented with the choice of multiple specific selectors, use the\n            // rightmost, on the assumption that that's less common, see bug 1829540.\n            if new_bucket.more_or_equally_specific_than(&current_bucket) {\n                current_bucket = new_bucket;\n            }\n        }\n\n        // Effectively, pseudo-elements are ignored, given only state\n        // pseudo-classes may appear before them.\n        if iter.next_sequence() != Some(Combinator::PseudoElement) {\n            break;\n        }\n    }\n\n    current_bucket\n}\n\n/// Wrapper for PrecomputedHashMap that does ASCII-case-insensitive lookup in quirks mode.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct MaybeCaseInsensitiveHashMap<K: PrecomputedHash + Hash + Eq, V>(PrecomputedHashMap<K, V>);\n\nimpl<V> Default for MaybeCaseInsensitiveHashMap<Atom, V> {\n    #[inline]\n    fn default() -> Self {\n        MaybeCaseInsensitiveHashMap(PrecomputedHashMap::default())\n    }\n}\n\nimpl<V> MaybeCaseInsensitiveHashMap<Atom, V> {\n    /// Empty map\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Shrink the capacity of the map if needed.\n    pub fn shrink_if_needed(&mut self) {\n        self.0.shrink_if_needed()\n    }\n\n    /// HashMap::try_entry\n    pub fn try_entry(\n        &mut self,\n        mut key: Atom,\n        quirks_mode: QuirksMode,\n    ) -> Result<hash_map::Entry<'_, Atom, V>, AllocErr> {\n        if quirks_mode == QuirksMode::Quirks {\n            key = key.to_ascii_lowercase()\n        }\n        self.0.try_reserve(1)?;\n        Ok(self.0.entry(key))\n    }\n\n    /// HashMap::is_empty\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// HashMap::iter\n    pub fn iter(&self) -> hash_map::Iter<'_, Atom, V> {\n        self.0.iter()\n    }\n\n    /// HashMap::clear\n    pub fn clear(&mut self) {\n        self.0.clear()\n    }\n\n    /// HashMap::get\n    pub fn get(&self, key: &WeakAtom, quirks_mode: QuirksMode) -> Option<&V> {\n        if quirks_mode == QuirksMode::Quirks {\n            self.0.get(&key.to_ascii_lowercase())\n        } else {\n            self.0.get(key)\n        }\n    }\n}\n"
  },
  {
    "path": "style/selector_parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The pseudo-classes and pseudo-elements supported by the style system.\n\n#![deny(missing_docs)]\n\nuse crate::derives::*;\nuse crate::stylesheets::{Namespaces, Origin, UrlExtraData};\nuse crate::values::serialize_atom_identifier;\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, Parser as CssParser, ParserInput};\nuse dom::ElementState;\nuse selectors::parser::{ParseRelative, SelectorList};\nuse std::fmt::{self, Debug, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// A convenient alias for the type that represents an attribute value used for\n/// selector parser implementation.\npub type AttrValue = <SelectorImpl as ::selectors::SelectorImpl>::AttrValue;\n\n#[cfg(feature = \"servo\")]\npub use crate::servo::selector_parser::*;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::selector_parser::*;\n\n#[cfg(feature = \"servo\")]\npub use crate::servo::selector_parser::ServoElementSnapshot as Snapshot;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::snapshot::GeckoElementSnapshot as Snapshot;\n\n#[cfg(feature = \"servo\")]\npub use crate::servo::restyle_damage::ServoRestyleDamage as RestyleDamage;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::restyle_damage::GeckoRestyleDamage as RestyleDamage;\n\n/// Servo's selector parser.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct SelectorParser<'a> {\n    /// The origin of the stylesheet we're parsing.\n    pub stylesheet_origin: Origin,\n    /// The namespace set of the stylesheet.\n    pub namespaces: &'a Namespaces,\n    /// The extra URL data of the stylesheet, which is used to look up\n    /// whether we are parsing a chrome:// URL style sheet.\n    pub url_data: &'a UrlExtraData,\n    /// Whether we're parsing selectors for `@supports`\n    pub for_supports_rule: bool,\n}\n\nimpl<'a> SelectorParser<'a> {\n    /// Parse a selector list with an author origin and without taking into\n    /// account namespaces.\n    ///\n    /// This is used for some DOM APIs like `querySelector`.\n    pub fn parse_author_origin_no_namespace<'i>(\n        input: &'i str,\n        url_data: &UrlExtraData,\n    ) -> Result<SelectorList<SelectorImpl>, ParseError<'i>> {\n        let namespaces = Namespaces::default();\n        let parser = SelectorParser {\n            stylesheet_origin: Origin::Author,\n            namespaces: &namespaces,\n            url_data,\n            for_supports_rule: false,\n        };\n        let mut input = ParserInput::new(input);\n        SelectorList::parse(&parser, &mut CssParser::new(&mut input), ParseRelative::No)\n    }\n\n    /// Whether we're parsing selectors in a user-agent stylesheet.\n    pub fn in_user_agent_stylesheet(&self) -> bool {\n        matches!(self.stylesheet_origin, Origin::UserAgent)\n    }\n\n    /// Whether we're parsing selectors in a stylesheet that has chrome\n    /// privilege.\n    pub fn chrome_rules_enabled(&self) -> bool {\n        self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User\n    }\n}\n\n/// This enumeration determines how a pseudo-element cascades.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum PseudoElementCascadeType {\n    /// Eagerly cascaded pseudo-elements are \"normal\" pseudo-elements (i.e.\n    /// `::before` and `::after`). They inherit styles normally as another\n    /// selector would do, and they're computed as part of the cascade.\n    ///\n    /// These kind of pseudo-elements require more up-front computation and\n    /// storage and thus should used for public pseudo-elements that can be used\n    /// on many element types (such as `::before` and `::after`).\n    Eager,\n    /// Lazy pseudo-elements are affected by selector matching, but they're only\n    /// computed when needed, and not before. They're useful for general\n    /// pseudo-elements that are not very common or that do not apply to many\n    /// elements. For instance in Servo this is used for `::backdrop` and\n    /// `::marker`.\n    Lazy,\n    /// Precomputed pseudo-elements skip the cascade process entirely, mostly as\n    /// an optimisation since they are private pseudo-elements (like\n    /// `::-servo-details-content`).\n    ///\n    /// This pseudo-elements are resolved on the fly using *only* global rules\n    /// (rules of the form `*|*`), and applying them to the parent style so are\n    /// mainly useful for user-agent stylesheets.\n    Precomputed,\n}\n\n/// A per-pseudo map, from a given pseudo to a `T`.\n#[derive(Clone, MallocSizeOf)]\npub struct PerPseudoElementMap<T> {\n    sparse: [i8; PSEUDO_COUNT],\n    dense: Vec<T>,\n}\n\nimpl<T> Default for PerPseudoElementMap<T> {\n    fn default() -> Self {\n        Self {\n            dense: Vec::new(),\n            sparse: [const { -1 }; PSEUDO_COUNT],\n        }\n    }\n}\n\nimpl<T> Debug for PerPseudoElementMap<T>\nwhere\n    T: Debug,\n{\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_char('[')?;\n        for idx in 0..=PSEUDO_COUNT {\n            if idx > 0 {\n                f.write_str(\", \")?;\n            }\n            debug_assert!(self.sparse.get(idx).is_some());\n            let i = self.sparse[idx];\n            if i < 0 {\n                None::<T>.fmt(f)?;\n            } else {\n                Some(&self.dense[i as usize]).fmt(f)?;\n            }\n        }\n        f.write_char(']')\n    }\n}\n\nimpl<T> PerPseudoElementMap<T> {\n    /// Get an entry in the map.\n    pub fn get(&self, pseudo: &PseudoElement) -> Option<&T> {\n        let idx = pseudo.index();\n        debug_assert!(self.sparse.get(idx).is_some());\n        let i = self.sparse[idx];\n        if i < 0 {\n            None\n        } else {\n            Some(&self.dense[i as usize])\n        }\n    }\n\n    /// Clear this enumerated array.\n    pub fn clear(&mut self) {\n        self.dense.clear();\n        self.sparse.fill(-1);\n    }\n\n    /// Set an entry value.\n    ///\n    /// Returns an error if the element is not a simple pseudo.\n    pub fn set(&mut self, pseudo: &PseudoElement, value: T) {\n        let idx = pseudo.index();\n        let i = self.sparse[idx];\n        if i < 0 {\n            let i = self.dense.len() as i8;\n            self.dense.push(value);\n            self.sparse[idx] = i\n        } else {\n            self.dense[i as usize] = value\n        }\n    }\n\n    /// Get an entry for `pseudo`, or create it with calling `f`.\n    pub fn get_or_insert_with<F>(&mut self, pseudo: &PseudoElement, f: F) -> &mut T\n    where\n        F: FnOnce() -> T,\n    {\n        let idx = pseudo.index();\n        let mut i = self.sparse[idx];\n        if i < 0 {\n            i = self.dense.len() as i8;\n            debug_assert!(self.dense.len() < PSEUDO_COUNT);\n            self.dense.push(f());\n            self.sparse[idx] = i;\n        }\n        debug_assert!(self.dense.get(i as usize).is_some());\n        &mut self.dense[i as usize]\n    }\n\n    /// Get an iterator for the entries.\n    pub fn iter(&self) -> impl Iterator<Item = &T> {\n        self.dense.iter()\n    }\n\n    /// Get a mutable iterator for the entries.\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {\n        self.dense.iter_mut()\n    }\n}\n\n/// Values for the :dir() pseudo class\n///\n/// \"ltr\" and \"rtl\" values are normalized to lowercase.\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub struct Direction(pub Atom);\n\n/// Horizontal values for the :dir() pseudo class\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum HorizontalDirection {\n    /// :dir(ltr)\n    Ltr,\n    /// :dir(rtl)\n    Rtl,\n}\n\nimpl Direction {\n    /// Parse a direction value.\n    pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        let ident = parser.expect_ident()?;\n        Ok(Direction(match_ignore_ascii_case! { &ident,\n            \"rtl\" => atom!(\"rtl\"),\n            \"ltr\" => atom!(\"ltr\"),\n            _ => Atom::from(ident.as_ref()),\n        }))\n    }\n\n    /// Convert this Direction into a HorizontalDirection, if applicable\n    pub fn as_horizontal_direction(&self) -> Option<HorizontalDirection> {\n        if self.0 == atom!(\"ltr\") {\n            Some(HorizontalDirection::Ltr)\n        } else if self.0 == atom!(\"rtl\") {\n            Some(HorizontalDirection::Rtl)\n        } else {\n            None\n        }\n    }\n\n    /// Gets the element state relevant to this :dir() selector.\n    pub fn element_state(&self) -> ElementState {\n        match self.as_horizontal_direction() {\n            Some(HorizontalDirection::Ltr) => ElementState::LTR,\n            Some(HorizontalDirection::Rtl) => ElementState::RTL,\n            None => ElementState::empty(),\n        }\n    }\n}\n\nimpl ToCss for Direction {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn can_build_and_set_arbitrary_index() {\n        let mut map = <PerPseudoElementMap<i32>>::default();\n        assert_eq!(map.get(&PseudoElement::After), None);\n        map.set(&PseudoElement::After, 3);\n        assert_eq!(map.get(&PseudoElement::After), Some(3).as_ref());\n\n        assert_eq!(map.get(&PseudoElement::MozRubyText), None);\n        map.set(&PseudoElement::MozRubyText, 8);\n        assert_eq!(map.get(&PseudoElement::MozRubyText), Some(8).as_ref());\n\n        assert_eq!(\n            map.get_or_insert_with(&PseudoElement::MozRubyText, || { 10 }),\n            &8\n        );\n        map.set(&PseudoElement::MozRubyText, 9);\n        assert_eq!(map.get(&PseudoElement::MozRubyText), Some(9).as_ref());\n\n        assert_eq!(\n            map.get_or_insert_with(&PseudoElement::FirstLine, || { 10 }),\n            &10\n        );\n        assert_eq!(map.get(&PseudoElement::FirstLine), Some(10).as_ref());\n    }\n\n    #[test]\n    fn can_iter() {\n        let mut map = <PerPseudoElementMap<i32>>::default();\n        map.set(&PseudoElement::After, 3);\n        map.set(&PseudoElement::MozRubyText, 8);\n        assert_eq!(map.iter().cloned().collect::<Vec<_>>(), vec![3, 8]);\n    }\n}\n"
  },
  {
    "path": "style/servo/animation.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS transitions and animations.\n\n// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to\n// compile it out so that people remember it exists.\n\nuse crate::context::{CascadeInputs, SharedStyleContext};\nuse crate::derives::*;\nuse crate::dom::{OpaqueNode, TDocument, TElement, TNode};\nuse crate::properties::animated_properties::{AnimationValue, AnimationValueMap};\nuse crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;\nuse crate::properties::longhands::animation_fill_mode::computed_value::single_value::T as AnimationFillMode;\nuse crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;\nuse crate::properties::AnimationDeclarations;\nuse crate::properties::{\n    ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, PropertyDeclarationId,\n    PropertyDeclarationIdSet,\n};\nuse crate::rule_tree::{CascadeLevel, CascadeOrigin, RuleCascadeFlags};\nuse crate::selector_parser::PseudoElement;\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::style_resolver::StyleResolverForElement;\nuse crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};\nuse crate::stylesheets::layer_rule::LayerOrder;\nuse crate::values::animated::{Animate, Procedure};\nuse crate::values::computed::TimingFunction;\nuse crate::values::generics::easing::BeforeFlag;\nuse crate::values::specified::TransitionBehavior;\nuse crate::Atom;\nuse parking_lot::RwLock;\nuse rustc_hash::FxHashMap;\nuse servo_arc::Arc;\nuse std::fmt;\n\n/// Represents an animation for a given property.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct PropertyAnimation {\n    /// The value we are animating from.\n    from: AnimationValue,\n\n    /// The value we are animating to.\n    to: AnimationValue,\n\n    /// The timing function of this `PropertyAnimation`.\n    timing_function: TimingFunction,\n\n    /// The duration of this `PropertyAnimation` in seconds.\n    pub duration: f64,\n}\n\nimpl PropertyAnimation {\n    /// Returns the given property longhand id.\n    pub fn property_id(&self) -> PropertyDeclarationId<'_> {\n        debug_assert_eq!(self.from.id(), self.to.id());\n        self.from.id()\n    }\n\n    /// The output of the timing function given the progress ration of this animation.\n    fn timing_function_output(&self, progress: f64) -> f64 {\n        let epsilon = 1. / (200. * self.duration);\n        // FIXME: Need to set the before flag correctly.\n        // In order to get the before flag, we have to know the current animation phase\n        // and whether the iteration is reversed. For now, we skip this calculation\n        // by treating as if the flag is unset at all times.\n        // https://drafts.csswg.org/css-easing/#step-timing-function-algo\n        self.timing_function\n            .calculate_output(progress, BeforeFlag::Unset, epsilon)\n    }\n\n    /// Update the given animation at a given point of progress.\n    fn calculate_value(&self, progress: f64) -> AnimationValue {\n        let progress = self.timing_function_output(progress);\n        let procedure = Procedure::Interpolate { progress };\n        self.from.animate(&self.to, procedure).unwrap_or_else(|()| {\n            // Fall back to discrete interpolation\n            if progress < 0.5 {\n                self.from.clone()\n            } else {\n                self.to.clone()\n            }\n        })\n    }\n}\n\n/// This structure represents the state of an animation.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq)]\npub enum AnimationState {\n    /// The animation has been created, but is not running yet. This state\n    /// is also used when an animation is still in the first delay phase.\n    Pending,\n    /// This animation is currently running.\n    Running,\n    /// This animation is paused. The inner field is the percentage of progress\n    /// when it was paused, from 0 to 1.\n    Paused(f64),\n    /// This animation has finished.\n    Finished,\n    /// This animation has been canceled.\n    Canceled,\n}\n\nimpl AnimationState {\n    /// Whether or not this state requires its owning animation to be ticked.\n    fn needs_to_be_ticked(&self) -> bool {\n        *self == AnimationState::Running || *self == AnimationState::Pending\n    }\n}\n\nenum IgnoreTransitions {\n    Canceled,\n    CanceledAndFinished,\n}\n\n/// This structure represents a keyframes animation current iteration state.\n///\n/// If the iteration count is infinite, there's no other state, otherwise we\n/// have to keep track the current iteration and the max iteration count.\n#[derive(Clone, Debug, MallocSizeOf)]\npub enum KeyframesIterationState {\n    /// Infinite iterations with the current iteration count.\n    Infinite(f64),\n    /// Current and max iterations.\n    Finite(f64, f64),\n}\n\n/// A temporary data structure used when calculating ComputedKeyframes for an\n/// animation. This data structure is used to collapse information for steps\n/// which may be spread across multiple keyframe declarations into a single\n/// instance per `start_percentage`.\n#[derive(Debug)]\nstruct IntermediateComputedKeyframe {\n    declarations: PropertyDeclarationBlock,\n    timing_function: Option<TimingFunction>,\n    start_percentage: f32,\n}\n\nimpl IntermediateComputedKeyframe {\n    fn new(start_percentage: f32) -> Self {\n        IntermediateComputedKeyframe {\n            declarations: PropertyDeclarationBlock::new(),\n            timing_function: None,\n            start_percentage,\n        }\n    }\n\n    /// Walk through all keyframe declarations and combine all declarations with the\n    /// same `start_percentage` into individual `IntermediateComputedKeyframe`s.\n    fn generate_for_keyframes(\n        animation: &KeyframesAnimation,\n        context: &SharedStyleContext,\n        base_style: &ComputedValues,\n    ) -> Vec<Self> {\n        if animation.steps.is_empty() {\n            return vec![];\n        }\n\n        let mut intermediate_steps: Vec<Self> = Vec::with_capacity(animation.steps.len());\n        let mut current_step = IntermediateComputedKeyframe::new(0.);\n        for step in animation.steps.iter() {\n            let start_percentage = step.start_percentage.0;\n            if start_percentage != current_step.start_percentage {\n                let new_step = IntermediateComputedKeyframe::new(start_percentage);\n                intermediate_steps.push(std::mem::replace(&mut current_step, new_step));\n            }\n\n            current_step.update_from_step(step, context, base_style);\n        }\n        intermediate_steps.push(current_step);\n\n        // We should always have a first and a last step, even if these are just\n        // generated by KeyframesStepValue::ComputedValues.\n        debug_assert!(intermediate_steps.first().unwrap().start_percentage == 0.);\n        debug_assert!(intermediate_steps.last().unwrap().start_percentage == 1.);\n\n        intermediate_steps\n    }\n\n    fn update_from_step(\n        &mut self,\n        step: &KeyframesStep,\n        context: &SharedStyleContext,\n        base_style: &ComputedValues,\n    ) {\n        // Each keyframe declaration may optionally specify a timing function, falling\n        // back to the one defined global for the animation.\n        let guard = &context.guards.author;\n        if let Some(timing_function) = step.get_animation_timing_function(&guard) {\n            self.timing_function = Some(timing_function.to_computed_value_without_context());\n        }\n\n        let block = match step.value {\n            KeyframesStepValue::ComputedValues => return,\n            KeyframesStepValue::Declarations { ref block } => block,\n        };\n\n        // Filter out !important, non-animatable properties, and the\n        // 'display' property (which is only animatable from SMIL).\n        let guard = block.read_with(&guard);\n        for declaration in guard.normal_declaration_iter() {\n            if let PropertyDeclarationId::Longhand(id) = declaration.id() {\n                if id == LonghandId::Display {\n                    continue;\n                }\n\n                if !id.is_animatable() {\n                    continue;\n                }\n            }\n\n            self.declarations.push(\n                declaration.to_physical(base_style.writing_mode),\n                Importance::Normal,\n            );\n        }\n    }\n\n    fn resolve_style<E>(\n        self,\n        element: E,\n        context: &SharedStyleContext,\n        base_style: &Arc<ComputedValues>,\n        resolver: &mut StyleResolverForElement<E>,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        if !self.declarations.any_normal() {\n            return base_style.clone();\n        }\n\n        let document = element.as_node().owner_doc();\n        let locked_block = Arc::new(document.shared_lock().wrap(self.declarations));\n        let mut important_rules_changed = false;\n        let rule_node = base_style.rules().clone();\n        let new_node = context.stylist.rule_tree().update_rule_at_level(\n            CascadeLevel::new(CascadeOrigin::Animations),\n            LayerOrder::root(),\n            Some(locked_block.borrow_arc()),\n            &rule_node,\n            &context.guards,\n            &mut important_rules_changed,\n        );\n\n        if new_node.is_none() {\n            return base_style.clone();\n        }\n\n        let inputs = CascadeInputs {\n            rules: new_node,\n            visited_rules: base_style.visited_rules().cloned(),\n            flags: base_style.flags.for_cascade_inputs(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n        };\n        resolver\n            .cascade_style_and_visited_with_default_parents(inputs)\n            .0\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf)]\nstruct PropertyDeclarationOffsets {\n    /// The absolute index of the most recent preceding keyframe that declared\n    /// the given property.\n    preceding_declaration: usize,\n    /// The absolute index of the next keyframe that will declare the given\n    /// property.\n    following_declaration: usize,\n}\n\n#[derive(Clone, Debug, MallocSizeOf)]\nenum AnimationValueOrReference {\n    /// This keyframe declares the property with the given value.\n    AnimationValue(AnimationValue),\n    /// This keyframe does not declare the property.\n    NotDefinedHere(PropertyDeclarationOffsets),\n}\n\n/// A single computed keyframe for a CSS Animation.\n#[derive(Clone, Debug, MallocSizeOf)]\nstruct ComputedKeyframe {\n    /// The timing function to use for transitions between this step\n    /// and the next one.\n    timing_function: TimingFunction,\n\n    /// The starting percentage (a number between 0 and 1) which represents\n    /// at what point in an animation iteration this step is.\n    start_percentage: f32,\n\n    /// The animation values to transition to and from when processing this\n    /// keyframe animation step.\n    values: Box<[AnimationValueOrReference]>,\n}\n\n/// Caches the indices of keyframes that declare a specific property.\n///\n/// While traversing the list of keyframes, this is used to avoid repeatedly\n/// searching for the next or last keyframe that declares the property. That\n/// would result in quadratic runtime with respect to the number of keyframes.\n#[derive(Clone, Copy, Debug, Default)]\nstruct KeyframeOffsetCacheForProperty {\n    /// The index of a previous keyframe that declares the property.\n    ///\n    /// Note that if the first keyframe does not declare a property, then it implicitly\n    /// uses the computed value of that property. That's why there's always a preceding keyframe\n    /// with the property.\n    last_keyframe_that_defined_property: usize,\n\n    /// The index of a future keyframe or `None` if we have not yet walked the list of keyframes\n    /// to find the next index.\n    ///\n    /// There will always be a next keyframe because the last keyframe (like the first keyframe)\n    /// declares *all* animating properties.\n    next_keyframe_that_defines_property: Option<usize>,\n}\n\nstruct KeyframeDataForProperty<'a> {\n    /// The timing function to use for transitions between this step\n    /// and the next one.\n    timing_function: &'a TimingFunction,\n\n    /// The starting percentage (a number between 0 and 1) which represents\n    /// at what point in an animation iteration this step is.\n    start_percentage: f32,\n\n    value: &'a AnimationValue,\n}\n\n#[derive(Clone, Copy, Debug)]\nenum Direction {\n    Forward,\n    Backward,\n}\n\nimpl Direction {\n    fn relative_to_animation_direction(&self, reverse: bool) -> Self {\n        match self {\n            Self::Forward if reverse => Self::Backward,\n            Self::Backward if reverse => Self::Forward,\n            _ => *self,\n        }\n    }\n}\n\nimpl Animation {\n    /// Starting from the keyframe at `keyframe_index`, returns the contents of the next keyframe in `direction`\n    /// that sets the property at `property_index`.\n    ///\n    /// Returns `None` if there is no keyframe in the specified direction that sets the property.\n    fn next_relevant_keyframe_for_property_in_direction(\n        &self,\n        property_index: usize,\n        keyframe_index: usize,\n        direction: Direction,\n    ) -> Option<KeyframeDataForProperty<'_>> {\n        let relevant_keyframe = &self.computed_steps[keyframe_index];\n        let parameters = match &relevant_keyframe.values[property_index] {\n            AnimationValueOrReference::AnimationValue(animation_value) => KeyframeDataForProperty {\n                timing_function: &relevant_keyframe.timing_function,\n                start_percentage: relevant_keyframe.start_percentage,\n                value: animation_value,\n            },\n            AnimationValueOrReference::NotDefinedHere(offsets) => {\n                let next_relevant_keyframe_index = match direction {\n                    Direction::Forward => offsets.following_declaration,\n                    Direction::Backward => offsets.preceding_declaration,\n                };\n                let next_relevant_keyframe = &self.computed_steps[next_relevant_keyframe_index];\n                let AnimationValueOrReference::AnimationValue(animation_value) =\n                    &next_relevant_keyframe.values[property_index]\n                else {\n                    panic!(\"Referenced keyframe does not set property\");\n                };\n\n                KeyframeDataForProperty {\n                    timing_function: &next_relevant_keyframe.timing_function,\n                    start_percentage: next_relevant_keyframe.start_percentage,\n                    value: &animation_value,\n                }\n            },\n        };\n\n        Some(parameters)\n    }\n}\nimpl ComputedKeyframe {\n    fn generate_for_keyframes<E>(\n        element: E,\n        animation: &KeyframesAnimation,\n        context: &SharedStyleContext,\n        base_style: &Arc<ComputedValues>,\n        default_timing_function: TimingFunction,\n        resolver: &mut StyleResolverForElement<E>,\n        animating_properties: PropertyDeclarationIdSet,\n        number_of_animating_properties: usize,\n    ) -> Box<[Self]>\n    where\n        E: TElement,\n    {\n        let animation_values_from_style: Vec<AnimationValue> = animating_properties\n            .iter()\n            .map(|property| {\n                AnimationValue::from_computed_values(property, &**base_style)\n                    .expect(\"Unexpected non-animatable property.\")\n            })\n            .collect();\n\n        let intermediate_steps =\n            IntermediateComputedKeyframe::generate_for_keyframes(animation, context, base_style);\n\n        // Used while iterating over the keyframes to, for each property, remember the most recent and\n        // next keyframe that declares the property. That avoids a quadratic number of traversals per\n        // property.\n        let mut keyframe_offset_caches: Vec<KeyframeOffsetCacheForProperty> =\n            vec![Default::default(); number_of_animating_properties];\n\n        let mut computed_steps: Vec<Self> = Vec::with_capacity(intermediate_steps.len());\n        let mut remaining_steps = intermediate_steps.into_iter();\n        let mut step_index = 0;\n        while let Some(step) = remaining_steps.next() {\n            let start_percentage = step.start_percentage;\n            let properties_changed_in_step = step.declarations.property_ids().clone();\n            let timing_function = step\n                .timing_function\n                .clone()\n                .unwrap_or_else(|| default_timing_function.clone());\n            let step_style = step.resolve_style(element, context, base_style, resolver);\n\n            let values: Box<[_]> = {\n                // For each property that is animating, pull the value from the resolved\n                // style for this step if it's in one of the declarations.\n                animating_properties\n                    .iter()\n                    .enumerate()\n                    .map(|(property_index, property_declaration)| {\n                        let keyframe_offset_cache = &mut keyframe_offset_caches[property_index];\n                        if properties_changed_in_step.contains(property_declaration) {\n                            keyframe_offset_cache.last_keyframe_that_defined_property = step_index;\n                            let animation_value = AnimationValue::from_computed_values(\n                                property_declaration,\n                                &step_style,\n                            )\n                            .unwrap();\n                            return AnimationValueOrReference::AnimationValue(animation_value);\n                        }\n\n                        // https://drafts.csswg.org/css-animations/#keyframes\n                        // > If a 0% or from keyframe is not specified, then the user agent constructs a 0% keyframe\n                        // > using the computed values of the properties being animated. If a 100% or to keyframe is\n                        // > not specified, then the user agent constructs a 100% keyframe using the computed values\n                        // > of the properties being animated.\n                        if step_index == 0 || remaining_steps.as_slice().is_empty() {\n                            return AnimationValueOrReference::AnimationValue(\n                                animation_values_from_style[property_index].clone(),\n                            );\n                        }\n\n                        // This animating property is not defined on this keyframe - we should act as if this keyframe\n                        // didn't exist for this property, so we calculate an interpolated value.\n                        // (https://drafts.csswg.org/css-animations/#keyframes)\n                        //\n                        // If the property was not defined on any previous keyframe then we use the value from style.\n                        // and if it's not defined on any following keyframe then we've already finished animating it.\n                        let preceding_declaration =\n                            keyframe_offset_cache.last_keyframe_that_defined_property;\n                        let following_declaration = keyframe_offset_cache\n                            .next_keyframe_that_defines_property\n                            .filter(|offset| *offset > step_index)\n                            .unwrap_or_else(|| {\n                                let relative_offset = remaining_steps\n                                    .as_slice()\n                                    .iter()\n                                    .position(|step| {\n                                        step.declarations.contains(property_declaration)\n                                    })\n                                    .unwrap_or(remaining_steps.as_slice().len() - 1);\n                                let absolute_offset = step_index + 1 + relative_offset;\n\n                                keyframe_offset_cache.next_keyframe_that_defines_property =\n                                    Some(absolute_offset);\n                                absolute_offset\n                            });\n\n                        AnimationValueOrReference::NotDefinedHere(PropertyDeclarationOffsets {\n                            preceding_declaration,\n                            following_declaration,\n                        })\n                    })\n                    .collect()\n            };\n            debug_assert_eq!(values.len(), number_of_animating_properties);\n\n            computed_steps.push(ComputedKeyframe {\n                timing_function,\n                start_percentage,\n                values,\n            });\n\n            step_index += 1;\n        }\n\n        // The first and last steps (at 0% and 100% respectively) should declare all animating properties.\n        // If they don't then we should have filled the missing properties with the computed values.\n        debug_assert!(computed_steps.first().is_none_or(|first_step| {\n            first_step\n                .values\n                .iter()\n                .all(|value| matches!(value, AnimationValueOrReference::AnimationValue(_)))\n        }));\n        debug_assert!(computed_steps.last().is_none_or(|first_step| {\n            first_step\n                .values\n                .iter()\n                .all(|value| matches!(value, AnimationValueOrReference::AnimationValue(_)))\n        }));\n\n        computed_steps.into_boxed_slice()\n    }\n}\n\n/// A CSS Animation\n#[derive(Clone, MallocSizeOf)]\npub struct Animation {\n    /// The name of this animation as defined by the style.\n    pub name: Atom,\n\n    /// The properties that change in this animation.\n    properties_changed: PropertyDeclarationIdSet,\n\n    /// The computed style for each keyframe of this animation.\n    computed_steps: Box<[ComputedKeyframe]>,\n\n    /// The time this animation started at, which is the current value of the animation\n    /// timeline when this animation was created plus any animation delay.\n    pub started_at: f64,\n\n    /// The duration of this animation.\n    pub duration: f64,\n\n    /// The delay of the animation.\n    pub delay: f64,\n\n    /// The `animation-fill-mode` property of this animation.\n    pub fill_mode: AnimationFillMode,\n\n    /// The current iteration state for the animation.\n    pub iteration_state: KeyframesIterationState,\n\n    /// Whether this animation is paused.\n    pub state: AnimationState,\n\n    /// The declared animation direction of this animation.\n    pub direction: AnimationDirection,\n\n    /// The current animation direction. This can only be `normal` or `reverse`.\n    pub current_direction: AnimationDirection,\n\n    /// The number of properties that are affected by this animation.\n    pub number_of_animating_properties: usize,\n\n    /// Whether or not this animation is new and or has already been tracked\n    /// by the script thread.\n    pub is_new: bool,\n}\n\nimpl Animation {\n    /// Whether or not this animation is cancelled by changes from a new style.\n    fn is_cancelled_in_new_style(&self, new_style: &Arc<ComputedValues>) -> bool {\n        let new_ui = new_style.get_ui();\n        let index = new_ui\n            .animation_name_iter()\n            .position(|animation_name| Some(&self.name) == animation_name.as_atom());\n        let index = match index {\n            Some(index) => index,\n            None => return true,\n        };\n\n        new_ui.animation_duration_mod(index).seconds() == 0.\n    }\n\n    /// Given the current time, advances this animation to the next iteration,\n    /// updates times, and then toggles the direction if appropriate. Otherwise\n    /// does nothing. Returns true if this animation has iterated.\n    pub fn iterate_if_necessary(&mut self, time: f64) -> bool {\n        if !self.iteration_over(time) {\n            return false;\n        }\n\n        // Only iterate animations that are currently running.\n        if self.state != AnimationState::Running {\n            return false;\n        }\n\n        if self.on_last_iteration() {\n            return false;\n        }\n\n        self.iterate();\n        true\n    }\n\n    fn iterate(&mut self) {\n        debug_assert!(!self.on_last_iteration());\n\n        if let KeyframesIterationState::Finite(ref mut current, max) = self.iteration_state {\n            *current = (*current + 1.).min(max);\n        }\n\n        if let AnimationState::Paused(ref mut progress) = self.state {\n            debug_assert!(*progress > 1.);\n            *progress -= 1.;\n        }\n\n        // Update the next iteration direction if applicable.\n        self.started_at += self.duration;\n        match self.direction {\n            AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {\n                self.current_direction = match self.current_direction {\n                    AnimationDirection::Normal => AnimationDirection::Reverse,\n                    AnimationDirection::Reverse => AnimationDirection::Normal,\n                    _ => unreachable!(),\n                };\n            },\n            _ => {},\n        }\n    }\n\n    /// A number (> 0 and <= 1) which represents the fraction of a full iteration\n    /// that the current iteration of the animation lasts. This will be less than 1\n    /// if the current iteration is the fractional remainder of a non-integral\n    /// iteration count.\n    pub fn current_iteration_end_progress(&self) -> f64 {\n        match self.iteration_state {\n            KeyframesIterationState::Finite(current, max) => (max - current).min(1.),\n            KeyframesIterationState::Infinite(_) => 1.,\n        }\n    }\n\n    /// The duration of the current iteration of this animation which may be less\n    /// than the animation duration if it has a non-integral iteration count.\n    pub fn current_iteration_duration(&self) -> f64 {\n        self.current_iteration_end_progress() * self.duration\n    }\n\n    /// Whether or not the current iteration is over. Note that this method assumes that\n    /// the animation is still running.\n    fn iteration_over(&self, time: f64) -> bool {\n        time > (self.started_at + self.current_iteration_duration())\n    }\n\n    /// Assuming this animation is running, whether or not it is on the last iteration.\n    fn on_last_iteration(&self) -> bool {\n        match self.iteration_state {\n            KeyframesIterationState::Finite(current, max) => current >= (max - 1.),\n            KeyframesIterationState::Infinite(_) => false,\n        }\n    }\n\n    /// Whether or not this animation has finished at the provided time. This does\n    /// not take into account canceling i.e. when an animation or transition is\n    /// canceled due to changes in the style.\n    pub fn has_ended(&self, time: f64) -> bool {\n        if !self.on_last_iteration() {\n            return false;\n        }\n\n        let progress = match self.state {\n            AnimationState::Finished => return true,\n            AnimationState::Paused(progress) => progress,\n            AnimationState::Running => (time - self.started_at) / self.duration,\n            AnimationState::Pending | AnimationState::Canceled => return false,\n        };\n\n        progress >= self.current_iteration_end_progress()\n    }\n\n    /// Updates the appropiate state from other animation.\n    ///\n    /// This happens when an animation is re-submitted to layout, presumably\n    /// because of an state change.\n    ///\n    /// There are some bits of state we can't just replace, over all taking in\n    /// account times, so here's that logic.\n    pub fn update_from_other(&mut self, other: &Self, now: f64) {\n        use self::AnimationState::*;\n\n        debug!(\n            \"KeyframesAnimationState::update_from_other({:?}, {:?})\",\n            self, other\n        );\n\n        // NB: We shall not touch the started_at field, since we don't want to\n        // restart the animation.\n        let old_started_at = self.started_at;\n        let old_duration = self.duration;\n        let old_direction = self.current_direction;\n        let old_state = self.state.clone();\n        let old_iteration_state = self.iteration_state.clone();\n\n        *self = other.clone();\n\n        self.started_at = old_started_at;\n        self.current_direction = old_direction;\n\n        // Don't update the iteration count, just the iteration limit.\n        // TODO: see how changing the limit affects rendering in other browsers.\n        // We might need to keep the iteration count even when it's infinite.\n        match (&mut self.iteration_state, old_iteration_state) {\n            (\n                &mut KeyframesIterationState::Finite(ref mut iters, _),\n                KeyframesIterationState::Finite(old_iters, _),\n            ) => *iters = old_iters,\n            _ => {},\n        }\n\n        // Don't pause or restart animations that should remain finished.\n        // We call mem::replace because `has_ended(...)` looks at `Animation::state`.\n        let new_state = std::mem::replace(&mut self.state, Running);\n        if old_state == Finished && self.has_ended(now) {\n            self.state = Finished;\n        } else {\n            self.state = new_state;\n        }\n\n        // If we're unpausing the animation, fake the start time so we seem to\n        // restore it.\n        //\n        // If the animation keeps paused, keep the old value.\n        //\n        // If we're pausing the animation, compute the progress value.\n        match (&mut self.state, &old_state) {\n            (&mut Pending, &Paused(progress)) => {\n                self.started_at = now - (self.duration * progress);\n            },\n            (&mut Paused(ref mut new), &Paused(old)) => *new = old,\n            (&mut Paused(ref mut progress), &Running) => {\n                *progress = (now - old_started_at) / old_duration\n            },\n            _ => {},\n        }\n\n        // Try to detect when we should skip straight to the running phase to\n        // avoid sending multiple animationstart events.\n        if self.state == Pending && self.started_at <= now && old_state != Pending {\n            self.state = Running;\n        }\n    }\n\n    /// Fill in an `AnimationValueMap` with values calculated from this animation at\n    /// the given time value.\n    fn get_property_declaration_at_time(&self, now: f64, map: &mut AnimationValueMap) {\n        if self.computed_steps.is_empty() {\n            // Nothing to do.\n            return;\n        }\n\n        let total_progress = match self.state {\n            AnimationState::Running | AnimationState::Pending | AnimationState::Finished => {\n                (now - self.started_at) / self.duration\n            },\n            AnimationState::Paused(progress) => progress,\n            AnimationState::Canceled => return,\n        };\n\n        if total_progress < 0.\n            && self.fill_mode != AnimationFillMode::Backwards\n            && self.fill_mode != AnimationFillMode::Both\n        {\n            return;\n        }\n        if self.has_ended(now)\n            && self.fill_mode != AnimationFillMode::Forwards\n            && self.fill_mode != AnimationFillMode::Both\n        {\n            return;\n        }\n        let total_progress = total_progress\n            .min(self.current_iteration_end_progress())\n            .max(0.0);\n\n        // Get the indices of the previous (from) keyframe and the next (to) keyframe.\n        let next_keyframe_index;\n        let prev_keyframe_index;\n        let num_steps = self.computed_steps.len();\n        match self.current_direction {\n            AnimationDirection::Normal => {\n                next_keyframe_index = self\n                    .computed_steps\n                    .iter()\n                    .position(|step| total_progress as f32 <= step.start_percentage);\n                prev_keyframe_index = next_keyframe_index\n                    .and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })\n                    .unwrap_or(0);\n            },\n            AnimationDirection::Reverse => {\n                next_keyframe_index = self\n                    .computed_steps\n                    .iter()\n                    .rev()\n                    .position(|step| total_progress as f32 <= 1. - step.start_percentage)\n                    .map(|pos| num_steps - pos - 1);\n                prev_keyframe_index = next_keyframe_index\n                    .and_then(|pos| {\n                        if pos != num_steps - 1 {\n                            Some(pos + 1)\n                        } else {\n                            None\n                        }\n                    })\n                    .unwrap_or(num_steps - 1)\n            },\n            _ => unreachable!(),\n        }\n\n        debug!(\n            \"Animation::get_property_declaration_at_time: keyframe from {:?} to {:?}\",\n            prev_keyframe_index, next_keyframe_index\n        );\n\n        let Some(next_keyframe_index) = next_keyframe_index else {\n            return;\n        };\n\n        // If we only need to take into account one keyframe, then exit early\n        // in order to avoid doing more work.\n        let mut add_declarations_to_map = |keyframe_index: usize| {\n            for value_or_reference in &self.computed_steps[keyframe_index].values {\n                let AnimationValueOrReference::AnimationValue(value) = value_or_reference else {\n                    unreachable!(\"First or last keyframes define all properties\");\n                };\n\n                map.insert(value.id().to_owned(), value.clone());\n            }\n        };\n\n        let reversed = self.current_direction != AnimationDirection::Normal;\n        if total_progress <= 0.0 {\n            if reversed {\n                add_declarations_to_map(self.computed_steps.len() - 1);\n            } else {\n                add_declarations_to_map(0);\n            }\n            return;\n        }\n        if total_progress >= 1.0 {\n            if reversed {\n                add_declarations_to_map(0);\n            } else {\n                add_declarations_to_map(self.computed_steps.len() - 1);\n            }\n            return;\n        }\n\n        // Interpolate a new value for each animating property\n        for property_index in 0..self.number_of_animating_properties {\n            let Some(previous_keyframe) = self.next_relevant_keyframe_for_property_in_direction(\n                property_index,\n                prev_keyframe_index,\n                Direction::Backward.relative_to_animation_direction(reversed),\n            ) else {\n                // Animation of this property has not started yet\n                continue;\n            };\n\n            let Some(next_keyframe) = self.next_relevant_keyframe_for_property_in_direction(\n                property_index,\n                next_keyframe_index,\n                Direction::Forward.relative_to_animation_direction(reversed),\n            ) else {\n                // This property has finished animating, just use the previous data\n                map.insert(\n                    previous_keyframe.value.id().to_owned(),\n                    previous_keyframe.value.clone(),\n                );\n                continue;\n            };\n\n            let percentage_between_keyframes =\n                (next_keyframe.start_percentage - previous_keyframe.start_percentage).abs() as f64;\n            let duration_between_keyframes = percentage_between_keyframes * self.duration;\n            let direction_aware_prev_keyframe_start_percentage = match self.current_direction {\n                AnimationDirection::Normal => previous_keyframe.start_percentage as f64,\n                AnimationDirection::Reverse => 1. - previous_keyframe.start_percentage as f64,\n                _ => unreachable!(),\n            };\n            let progress_between_keyframes = (total_progress\n                - direction_aware_prev_keyframe_start_percentage)\n                / percentage_between_keyframes;\n            let animation = PropertyAnimation {\n                from: previous_keyframe.value.clone(),\n                to: next_keyframe.value.clone(),\n                timing_function: previous_keyframe.timing_function.clone(),\n                duration: duration_between_keyframes as f64,\n            };\n\n            let value = animation.calculate_value(progress_between_keyframes);\n            map.insert(value.id().to_owned(), value);\n        }\n    }\n}\n\nimpl fmt::Debug for Animation {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_struct(\"Animation\")\n            .field(\"name\", &self.name)\n            .field(\"started_at\", &self.started_at)\n            .field(\"duration\", &self.duration)\n            .field(\"delay\", &self.delay)\n            .field(\"iteration_state\", &self.iteration_state)\n            .field(\"state\", &self.state)\n            .field(\"direction\", &self.direction)\n            .field(\"current_direction\", &self.current_direction)\n            .field(\"cascade_style\", &())\n            .finish()\n    }\n}\n\n/// A CSS Transition\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct Transition {\n    /// The start time of this transition, which is the current value of the animation\n    /// timeline when this transition was created plus any animation delay.\n    pub start_time: f64,\n\n    /// The delay used for this transition.\n    pub delay: f64,\n\n    /// The internal style `PropertyAnimation` for this transition.\n    pub property_animation: PropertyAnimation,\n\n    /// The state of this transition.\n    pub state: AnimationState,\n\n    /// Whether or not this transition is new and or has already been tracked\n    /// by the script thread.\n    pub is_new: bool,\n\n    /// If this `Transition` has been replaced by a new one this field is\n    /// used to help produce better reversed transitions.\n    pub reversing_adjusted_start_value: AnimationValue,\n\n    /// If this `Transition` has been replaced by a new one this field is\n    /// used to help produce better reversed transitions.\n    pub reversing_shortening_factor: f64,\n}\n\nimpl Transition {\n    fn new(\n        start_time: f64,\n        delay: f64,\n        duration: f64,\n        from: AnimationValue,\n        to: AnimationValue,\n        timing_function: &TimingFunction,\n    ) -> Self {\n        let property_animation = PropertyAnimation {\n            from: from.clone(),\n            to,\n            timing_function: timing_function.clone(),\n            duration,\n        };\n        Self {\n            start_time,\n            delay,\n            property_animation,\n            state: AnimationState::Pending,\n            is_new: true,\n            reversing_adjusted_start_value: from,\n            reversing_shortening_factor: 1.0,\n        }\n    }\n\n    fn update_for_possibly_reversed_transition(\n        &mut self,\n        replaced_transition: &Transition,\n        delay: f64,\n        now: f64,\n    ) {\n        // If we reach here, we need to calculate a reversed transition according to\n        // https://drafts.csswg.org/css-transitions/#starting\n        //\n        //  \"...if the reversing-adjusted start value of the running transition\n        //  is the same as the value of the property in the after-change style (see\n        //  the section on reversing of transitions for why these case exists),\n        //  implementations must cancel the running transition and start\n        //  a new transition...\"\n        if replaced_transition.reversing_adjusted_start_value != self.property_animation.to {\n            return;\n        }\n\n        // \"* reversing-adjusted start value is the end value of the running transition\"\n        let replaced_animation = &replaced_transition.property_animation;\n        self.reversing_adjusted_start_value = replaced_animation.to.clone();\n\n        // \"* reversing shortening factor is the absolute value, clamped to the\n        //    range [0, 1], of the sum of:\n        //    1. the output of the timing function of the old transition at the\n        //      time of the style change event, times the reversing shortening\n        //      factor of the old transition\n        //    2.  1 minus the reversing shortening factor of the old transition.\"\n        let transition_progress = ((now - replaced_transition.start_time)\n            / (replaced_transition.property_animation.duration))\n            .min(1.0)\n            .max(0.0);\n        let timing_function_output = replaced_animation.timing_function_output(transition_progress);\n        let old_reversing_shortening_factor = replaced_transition.reversing_shortening_factor;\n        self.reversing_shortening_factor = ((timing_function_output\n            * old_reversing_shortening_factor)\n            + (1.0 - old_reversing_shortening_factor))\n            .abs()\n            .min(1.0)\n            .max(0.0);\n\n        // \"* start time is the time of the style change event plus:\n        //    1. if the matching transition delay is nonnegative, the matching\n        //       transition delay, or.\n        //    2. if the matching transition delay is negative, the product of the new\n        //       transition’s reversing shortening factor and the matching transition delay,\"\n        self.start_time = if delay >= 0. {\n            now + delay\n        } else {\n            now + (self.reversing_shortening_factor * delay)\n        };\n\n        // \"* end time is the start time plus the product of the matching transition\n        //    duration and the new transition’s reversing shortening factor,\"\n        self.property_animation.duration *= self.reversing_shortening_factor;\n\n        // \"* start value is the current value of the property in the running transition,\n        //  * end value is the value of the property in the after-change style,\"\n        let procedure = Procedure::Interpolate {\n            progress: timing_function_output,\n        };\n        match replaced_animation\n            .from\n            .animate(&replaced_animation.to, procedure)\n        {\n            Ok(new_start) => self.property_animation.from = new_start,\n            Err(..) => {},\n        }\n    }\n\n    /// Whether or not this animation has ended at the provided time. This does\n    /// not take into account canceling i.e. when an animation or transition is\n    /// canceled due to changes in the style.\n    pub fn has_ended(&self, time: f64) -> bool {\n        time >= self.start_time + (self.property_animation.duration)\n    }\n\n    /// Update the given animation at a given point of progress.\n    pub fn calculate_value(&self, time: f64) -> AnimationValue {\n        let progress = (time - self.start_time) / (self.property_animation.duration);\n        self.property_animation\n            .calculate_value(progress.clamp(0.0, 1.0))\n    }\n}\n\n/// Holds the animation state for a particular element.\n#[derive(Debug, Default, MallocSizeOf)]\npub struct ElementAnimationSet {\n    /// The animations for this element.\n    pub animations: Vec<Animation>,\n\n    /// The transitions for this element.\n    pub transitions: Vec<Transition>,\n\n    /// Whether or not this ElementAnimationSet has had animations or transitions\n    /// which have been added, removed, or had their state changed.\n    pub dirty: bool,\n}\n\nimpl ElementAnimationSet {\n    /// Cancel all animations in this `ElementAnimationSet`. This is typically called\n    /// when the element has been removed from the DOM.\n    pub fn cancel_all_animations(&mut self) {\n        self.dirty = !self.animations.is_empty();\n        for animation in self.animations.iter_mut() {\n            animation.state = AnimationState::Canceled;\n        }\n        self.cancel_active_transitions();\n    }\n\n    fn cancel_active_transitions(&mut self) {\n        for transition in self.transitions.iter_mut() {\n            if transition.state != AnimationState::Finished {\n                self.dirty = true;\n                transition.state = AnimationState::Canceled;\n            }\n        }\n    }\n\n    /// Apply all active animations.\n    pub fn apply_active_animations(\n        &self,\n        context: &SharedStyleContext,\n        style: &mut Arc<ComputedValues>,\n    ) {\n        let now = context.current_time_for_animations;\n        let mutable_style = Arc::make_mut(style);\n        if let Some(map) = self.get_value_map_for_active_animations(now) {\n            for value in map.values() {\n                value.set_in_style_for_servo(mutable_style, context);\n            }\n        }\n\n        if let Some(map) = self.get_value_map_for_transitions(now, IgnoreTransitions::Canceled) {\n            for value in map.values() {\n                value.set_in_style_for_servo(mutable_style, context);\n            }\n        }\n    }\n\n    /// Clear all canceled animations and transitions from this `ElementAnimationSet`.\n    pub fn clear_canceled_animations(&mut self) {\n        self.animations\n            .retain(|animation| animation.state != AnimationState::Canceled);\n        self.transitions\n            .retain(|animation| animation.state != AnimationState::Canceled);\n    }\n\n    /// Whether this `ElementAnimationSet` is empty, which means it doesn't\n    /// hold any animations in any state.\n    pub fn is_empty(&self) -> bool {\n        self.animations.is_empty() && self.transitions.is_empty()\n    }\n\n    /// Whether or not this state needs animation ticks for its transitions\n    /// or animations.\n    pub fn needs_animation_ticks(&self) -> bool {\n        self.animations\n            .iter()\n            .any(|animation| animation.state.needs_to_be_ticked())\n            || self\n                .transitions\n                .iter()\n                .any(|transition| transition.state.needs_to_be_ticked())\n    }\n\n    /// The number of running animations and transitions for this `ElementAnimationSet`.\n    pub fn running_animation_and_transition_count(&self) -> usize {\n        self.animations\n            .iter()\n            .filter(|animation| animation.state.needs_to_be_ticked())\n            .count()\n            + self\n                .transitions\n                .iter()\n                .filter(|transition| transition.state.needs_to_be_ticked())\n                .count()\n    }\n\n    /// If this `ElementAnimationSet` has any any active animations.\n    pub fn has_active_animation(&self) -> bool {\n        self.animations\n            .iter()\n            .any(|animation| animation.state != AnimationState::Canceled)\n    }\n\n    /// If this `ElementAnimationSet` has any any active transitions.\n    pub fn has_active_transition(&self) -> bool {\n        self.transitions\n            .iter()\n            .any(|transition| transition.state != AnimationState::Canceled)\n    }\n\n    /// Update our animations given a new style, canceling or starting new animations\n    /// when appropriate.\n    pub fn update_animations_for_new_style<E>(\n        &mut self,\n        element: E,\n        context: &SharedStyleContext,\n        new_style: &Arc<ComputedValues>,\n        resolver: &mut StyleResolverForElement<E>,\n    ) where\n        E: TElement,\n    {\n        for animation in self.animations.iter_mut() {\n            if animation.is_cancelled_in_new_style(new_style) {\n                animation.state = AnimationState::Canceled;\n            }\n        }\n\n        maybe_start_animations(element, &context, &new_style, self, resolver);\n    }\n\n    /// Update our transitions given a new style, canceling or starting new animations\n    /// when appropriate.\n    pub fn update_transitions_for_new_style(\n        &mut self,\n        might_need_transitions_update: bool,\n        context: &SharedStyleContext,\n        old_style: Option<&Arc<ComputedValues>>,\n        after_change_style: &Arc<ComputedValues>,\n    ) {\n        // If this is the first style, we don't trigger any transitions and we assume\n        // there were no previously triggered transitions.\n        let mut before_change_style = match old_style {\n            Some(old_style) => Arc::clone(old_style),\n            None => return,\n        };\n\n        // If the style of this element is display:none, then cancel all active transitions.\n        if after_change_style.get_box().clone_display().is_none() {\n            self.cancel_active_transitions();\n            return;\n        }\n\n        if !might_need_transitions_update {\n            return;\n        }\n\n        // We convert old values into `before-change-style` here.\n        if self.has_active_transition() || self.has_active_animation() {\n            self.apply_active_animations(context, &mut before_change_style);\n        }\n\n        let transitioning_properties = start_transitions_if_applicable(\n            context,\n            &before_change_style,\n            after_change_style,\n            self,\n        );\n\n        // Cancel any non-finished transitions that have properties which no\n        // longer transition.\n        //\n        // Step 3 in https://drafts.csswg.org/css-transitions/#starting:\n        // > If the element has a running transition or completed transition for\n        // > the property, and there is not a matching transition-property value,\n        // > then implementations must cancel the running transition or remove the\n        // > completed transition from the set of completed transitions.\n        //\n        // TODO: This is happening here as opposed to in\n        // `start_transition_if_applicable` as an optimization, but maybe this\n        // code should be reworked to be more like the specification.\n        for transition in self.transitions.iter_mut() {\n            if transition.state == AnimationState::Finished\n                || transition.state == AnimationState::Canceled\n            {\n                continue;\n            }\n            if transitioning_properties.contains(transition.property_animation.property_id()) {\n                continue;\n            }\n            transition.state = AnimationState::Canceled;\n            self.dirty = true;\n        }\n    }\n\n    fn start_transition_if_applicable(\n        &mut self,\n        context: &SharedStyleContext,\n        property_declaration_id: &PropertyDeclarationId,\n        index: usize,\n        old_style: &ComputedValues,\n        new_style: &Arc<ComputedValues>,\n    ) {\n        let style = new_style.get_ui();\n        let allow_discrete =\n            style.transition_behavior_mod(index) == TransitionBehavior::AllowDiscrete;\n\n        // FIXME(emilio): Handle the case where old_style and new_style's writing mode differ.\n        let Some(from) = AnimationValue::from_computed_values(*property_declaration_id, old_style)\n        else {\n            return;\n        };\n        let Some(to) = AnimationValue::from_computed_values(*property_declaration_id, new_style)\n        else {\n            return;\n        };\n\n        let timing_function = style.transition_timing_function_mod(index);\n        let duration = style.transition_duration_mod(index).seconds() as f64;\n        let delay = style.transition_delay_mod(index).seconds() as f64;\n        let now = context.current_time_for_animations;\n        let transitionable = property_declaration_id.is_animatable()\n            && (allow_discrete || !property_declaration_id.is_discrete_animatable())\n            && (allow_discrete || from.interpolable_with(&to));\n\n        let mut existing_transition = self.transitions.iter_mut().find(|transition| {\n            transition.property_animation.property_id() == *property_declaration_id\n        });\n\n        // Step 1:\n        // > If all of the following are true:\n        // >  - the element does not have a running transition for the property,\n        // >  - the before-change style is different from the after-change style\n        // >    for that property, and the values for the property are\n        // >    transitionable,\n        // >  - the element does not have a completed transition for the property\n        // >    or the end value of the completed transition is different from the\n        // >    after-change style for the property,\n        // >  - there is a matching transition-property value, and\n        // >  - the combined duration is greater than 0s,\n        //\n        // This function is only run if there is a matching transition-property\n        // value, so that check is skipped here.\n        let has_running_transition = existing_transition.as_ref().is_some_and(|transition| {\n            transition.state != AnimationState::Finished\n                && transition.state != AnimationState::Canceled\n        });\n        let no_completed_transition_or_end_values_differ =\n            existing_transition.as_ref().is_none_or(|transition| {\n                transition.state != AnimationState::Finished\n                    || transition.property_animation.to != to\n            });\n        if !has_running_transition\n            && from != to\n            && transitionable\n            && no_completed_transition_or_end_values_differ\n            && (duration + delay > 0.0)\n        {\n            // > then implementations must remove the completed transition (if\n            // > present) from the set of completed transitions and start a\n            // > transition whose:\n            // >\n            // > - start time is the time of the style change event plus the matching transition delay,\n            // > - end time is the start time plus the matching transition duration,\n            // > - start value is the value of the transitioning property in the before-change style,\n            // > - end value is the value of the transitioning property in the after-change style,\n            // > - reversing-adjusted start value is the same as the start value, and\n            // > - reversing shortening factor is 1.\n            self.transitions.push(Transition::new(\n                now + delay, /* start_time */\n                delay,\n                duration,\n                from,\n                to,\n                &timing_function,\n            ));\n            self.dirty = true;\n            return;\n        }\n\n        // > Step 2: Otherwise, if the element has a completed transition for the\n        // > property and the end value of the completed transition is different\n        // > from the after-change style for the property, then implementations\n        // > must remove the completed transition from the set of completed\n        // > transitions.\n        //\n        // All completed transitions will be cleared from the `AnimationSet` in\n        // `process_animations_for_style in `matching.rs`.\n\n        // > Step 3: If the element has a running transition or completed\n        // > transition for the property, and there is not a matching\n        // > transition-property value, then implementations must cancel the\n        // > running transition or remove the completed transition from the set\n        // > of completed transitions.\n        //\n        // - All completed transitions will be cleared cleared from the `AnimationSet` in\n        //   `process_animations_for_style in `matching.rs`.\n        // - Transitions for properties that don't have a matching transition-property\n        //   value will be canceled in `Self::update_transitions_for_new_style`. In addition,\n        //   this method is only called for properties that do ahave a matching\n        //   transition-property value.\n\n        let Some(existing_transition) = existing_transition.as_mut() else {\n            return;\n        };\n\n        // > Step 4: If the element has a running transition for the property,\n        // > there is a matching transition-property value, and the end value of\n        // > the running transition is not equal to the value of the property in\n        // > the after-change style, then:\n        if has_running_transition && existing_transition.property_animation.to != to {\n            // > Step 4.1: If the current value of the property in the running transition is\n            // > equal to the value of the property in the after-change style, or\n            // > if these two values are not transitionable, then implementations\n            // > must cancel the running transition.\n            let current_value = existing_transition.calculate_value(now);\n            let transitionable_from_current_value =\n                transitionable && (allow_discrete || current_value.interpolable_with(&to));\n            if current_value == to || !transitionable_from_current_value {\n                existing_transition.state = AnimationState::Canceled;\n                self.dirty = true;\n                return;\n            }\n\n            // > Step 4.2: Otherwise, if the combined duration is less than or\n            // > equal to 0s, or if the current value of the property in the\n            // > running transition is not transitionable with the value of the\n            // > property in the after-change style, then implementations must\n            // > cancel the running transition.\n            if duration + delay <= 0.0 {\n                existing_transition.state = AnimationState::Canceled;\n                self.dirty = true;\n                return;\n            }\n\n            // > Step 4.3: Otherwise, if the reversing-adjusted start value of the\n            // > running transition is the same as the value of the property in\n            // > the after-change style (see the section on reversing of\n            // > transitions for why these case exists), implementations must\n            // > cancel the running transition and start a new transition whose:\n            if existing_transition.reversing_adjusted_start_value == to {\n                existing_transition.state = AnimationState::Canceled;\n\n                let mut transition = Transition::new(\n                    now + delay, /* start_time */\n                    delay,\n                    duration,\n                    from,\n                    to,\n                    &timing_function,\n                );\n\n                // This function takes care of applying all of the modifications to the transition\n                // after \"whose:\" above.\n                transition.update_for_possibly_reversed_transition(\n                    &existing_transition,\n                    delay,\n                    now,\n                );\n\n                self.transitions.push(transition);\n                self.dirty = true;\n                return;\n            }\n\n            // > Step 4.4: Otherwise, implementations must cancel the running\n            // > transition and start a new transition whose:\n            // >  - start time is the time of the style change event plus the matching transition delay,\n            // >  - end time is the start time plus the matching transition duration,\n            // >  - start value is the current value of the property in the running transition,\n            // >  - end value is the value of the property in the after-change style,\n            // >  - reversing-adjusted start value is the same as the start value, and\n            // >  - reversing shortening factor is 1.\n            existing_transition.state = AnimationState::Canceled;\n            self.transitions.push(Transition::new(\n                now + delay, /* start_time */\n                delay,\n                duration,\n                current_value,\n                to,\n                &timing_function,\n            ));\n            self.dirty = true;\n        }\n    }\n\n    /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s\n    /// transitions, ignoring those specified by the `ignore_transitions`\n    /// argument.\n    fn get_value_map_for_transitions(\n        &self,\n        now: f64,\n        ignore_transitions: IgnoreTransitions,\n    ) -> Option<AnimationValueMap> {\n        if !self.has_active_transition() {\n            return None;\n        }\n\n        let mut map =\n            AnimationValueMap::with_capacity_and_hasher(self.transitions.len(), Default::default());\n        for transition in &self.transitions {\n            match ignore_transitions {\n                IgnoreTransitions::Canceled => {\n                    if transition.state == AnimationState::Canceled {\n                        continue;\n                    }\n                },\n                IgnoreTransitions::CanceledAndFinished => {\n                    if transition.state == AnimationState::Canceled\n                        || transition.state == AnimationState::Finished\n                    {\n                        continue;\n                    }\n                },\n            }\n\n            let value = transition.calculate_value(now);\n            map.insert(value.id().to_owned(), value);\n        }\n\n        Some(map)\n    }\n\n    /// Generate a `AnimationValueMap` for this `ElementAnimationSet`'s\n    /// active animations at the given time value.\n    pub fn get_value_map_for_active_animations(&self, now: f64) -> Option<AnimationValueMap> {\n        if !self.has_active_animation() {\n            return None;\n        }\n\n        let mut map = Default::default();\n        for animation in &self.animations {\n            animation.get_property_declaration_at_time(now, &mut map);\n        }\n\n        Some(map)\n    }\n}\n\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\n/// A key that is used to identify nodes in the `DocumentAnimationSet`.\npub struct AnimationSetKey {\n    /// The node for this `AnimationSetKey`.\n    pub node: OpaqueNode,\n    /// The pseudo element for this `AnimationSetKey`. If `None` this key will\n    /// refer to the main content for its node.\n    pub pseudo_element: Option<PseudoElement>,\n}\n\nimpl AnimationSetKey {\n    /// Create a new key given a node and optional pseudo element.\n    pub fn new(node: OpaqueNode, pseudo_element: Option<PseudoElement>) -> Self {\n        AnimationSetKey {\n            node,\n            pseudo_element,\n        }\n    }\n\n    /// Create a new key for the main content of this node.\n    pub fn new_for_non_pseudo(node: OpaqueNode) -> Self {\n        AnimationSetKey {\n            node,\n            pseudo_element: None,\n        }\n    }\n\n    /// Create a new key for given node and pseudo element.\n    pub fn new_for_pseudo(node: OpaqueNode, pseudo_element: PseudoElement) -> Self {\n        AnimationSetKey {\n            node,\n            pseudo_element: Some(pseudo_element),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Default, MallocSizeOf)]\n/// A set of animations for a document.\npub struct DocumentAnimationSet {\n    /// The `ElementAnimationSet`s that this set contains.\n    #[ignore_malloc_size_of = \"Arc is hard\"]\n    pub sets: Arc<RwLock<FxHashMap<AnimationSetKey, ElementAnimationSet>>>,\n}\n\nimpl DocumentAnimationSet {\n    /// Return whether or not the provided node has active CSS animations.\n    pub fn has_active_animations(&self, key: &AnimationSetKey) -> bool {\n        self.sets\n            .read()\n            .get(key)\n            .map_or(false, |set| set.has_active_animation())\n    }\n\n    /// Return whether or not the provided node has active CSS transitions.\n    pub fn has_active_transitions(&self, key: &AnimationSetKey) -> bool {\n        self.sets\n            .read()\n            .get(key)\n            .map_or(false, |set| set.has_active_transition())\n    }\n\n    /// Return a locked PropertyDeclarationBlock with animation values for the given\n    /// key and time.\n    pub fn get_animation_declarations(\n        &self,\n        key: &AnimationSetKey,\n        time: f64,\n        shared_lock: &SharedRwLock,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {\n        self.sets\n            .read()\n            .get(key)\n            .and_then(|set| set.get_value_map_for_active_animations(time))\n            .map(|map| {\n                let block = PropertyDeclarationBlock::from_animation_value_map(&map);\n                Arc::new(shared_lock.wrap(block))\n            })\n    }\n\n    /// Return a locked PropertyDeclarationBlock with transition values for the given\n    /// key and time.\n    pub fn get_transition_declarations(\n        &self,\n        key: &AnimationSetKey,\n        time: f64,\n        shared_lock: &SharedRwLock,\n    ) -> Option<Arc<Locked<PropertyDeclarationBlock>>> {\n        self.sets\n            .read()\n            .get(key)\n            .and_then(|set| {\n                set.get_value_map_for_transitions(time, IgnoreTransitions::CanceledAndFinished)\n            })\n            .map(|map| {\n                let block = PropertyDeclarationBlock::from_animation_value_map(&map);\n                Arc::new(shared_lock.wrap(block))\n            })\n    }\n\n    /// Get all the animation declarations for the given key, returning an empty\n    /// `AnimationDeclarations` if there are no animations.\n    pub fn get_all_declarations(\n        &self,\n        key: &AnimationSetKey,\n        time: f64,\n        shared_lock: &SharedRwLock,\n    ) -> AnimationDeclarations {\n        let sets = self.sets.read();\n        let set = match sets.get(key) {\n            Some(set) => set,\n            None => return Default::default(),\n        };\n\n        let animations = set.get_value_map_for_active_animations(time).map(|map| {\n            let block = PropertyDeclarationBlock::from_animation_value_map(&map);\n            Arc::new(shared_lock.wrap(block))\n        });\n        let transitions = set\n            .get_value_map_for_transitions(time, IgnoreTransitions::CanceledAndFinished)\n            .map(|map| {\n                let block = PropertyDeclarationBlock::from_animation_value_map(&map);\n                Arc::new(shared_lock.wrap(block))\n            });\n        AnimationDeclarations {\n            animations,\n            transitions,\n        }\n    }\n\n    /// Cancel all animations for set at the given key.\n    pub fn cancel_all_animations_for_key(&self, key: &AnimationSetKey) {\n        if let Some(set) = self.sets.write().get_mut(key) {\n            set.cancel_all_animations();\n        }\n    }\n}\n\n/// Kick off any new transitions for this node and return all of the properties that are\n/// transitioning. This is at the end of calculating style for a single node.\npub fn start_transitions_if_applicable(\n    context: &SharedStyleContext,\n    old_style: &ComputedValues,\n    new_style: &Arc<ComputedValues>,\n    animation_state: &mut ElementAnimationSet,\n) -> PropertyDeclarationIdSet {\n    // See <https://www.w3.org/TR/css-transitions-1/#transitions>\n    // \"If a property is specified multiple times in the value of transition-property\n    // (either on its own, via a shorthand that contains it, or via the all value),\n    // then the transition that starts uses the duration, delay, and timing function\n    // at the index corresponding to the last item in the value of transition-property\n    // that calls for animating that property.\"\n    // See Example 3 of <https://www.w3.org/TR/css-transitions-1/#transitions>\n    //\n    // Reversing the transition order here means that transitions defined later in the list\n    // have preference, in accordance with the specification.\n    //\n    // TODO: It would be better to be able to do this without having to allocate an array.\n    // We should restructure the code or make `transition_properties()` return a reversible\n    // iterator in order to avoid the allocation.\n    let mut transition_properties = new_style.transition_properties().collect::<Vec<_>>();\n    transition_properties.reverse();\n\n    let mut properties_that_transition = PropertyDeclarationIdSet::default();\n    for transition in transition_properties {\n        let physical_property = transition\n            .property\n            .as_borrowed()\n            .to_physical(new_style.writing_mode);\n        if properties_that_transition.contains(physical_property) {\n            continue;\n        }\n\n        properties_that_transition.insert(physical_property);\n        animation_state.start_transition_if_applicable(\n            context,\n            &physical_property,\n            transition.index,\n            old_style,\n            new_style,\n        );\n    }\n\n    properties_that_transition\n}\n\n/// Triggers animations for a given node looking at the animation property\n/// values.\npub fn maybe_start_animations<E>(\n    element: E,\n    context: &SharedStyleContext,\n    new_style: &Arc<ComputedValues>,\n    animation_state: &mut ElementAnimationSet,\n    resolver: &mut StyleResolverForElement<E>,\n) where\n    E: TElement,\n{\n    let style = new_style.get_ui();\n    for (i, name) in style.animation_name_iter().enumerate() {\n        let name = match name.as_atom() {\n            Some(atom) => atom,\n            None => continue,\n        };\n\n        debug!(\"maybe_start_animations: name={}\", name);\n        let duration = style.animation_duration_mod(i).seconds() as f64;\n        if duration == 0. {\n            continue;\n        }\n\n        let Some(keyframe_animation) = context.stylist.lookup_keyframes(name, element) else {\n            continue;\n        };\n\n        debug!(\"maybe_start_animations: animation {} found\", name);\n\n        // NB: This delay may be negative, meaning that the animation may be created\n        // in a state where we have advanced one or more iterations or even that the\n        // animation begins in a finished state.\n        let delay = style.animation_delay_mod(i).seconds();\n\n        let iteration_count = style.animation_iteration_count_mod(i);\n        let iteration_state = if iteration_count.0.is_infinite() {\n            KeyframesIterationState::Infinite(0.0)\n        } else {\n            KeyframesIterationState::Finite(0.0, iteration_count.0 as f64)\n        };\n\n        let animation_direction = style.animation_direction_mod(i);\n\n        let initial_direction = match animation_direction {\n            AnimationDirection::Normal | AnimationDirection::Alternate => {\n                AnimationDirection::Normal\n            },\n            AnimationDirection::Reverse | AnimationDirection::AlternateReverse => {\n                AnimationDirection::Reverse\n            },\n        };\n\n        let now = context.current_time_for_animations;\n        let started_at = now + delay as f64;\n        let mut starting_progress = (now - started_at) / duration;\n        let state = match style.animation_play_state_mod(i) {\n            AnimationPlayState::Paused => AnimationState::Paused(starting_progress),\n            AnimationPlayState::Running => AnimationState::Pending,\n        };\n\n        // Determine the set of animating properties. This is not equivalent to the set of changed properties\n        // when one changed property overrides another. (For example, \"block-size\" with writing-mode: initial\n        // is the same as \"height\")\n        let mut animating_properties = PropertyDeclarationIdSet::default();\n        let mut number_of_animating_properties = 0;\n        for property in keyframe_animation.properties_changed.iter() {\n            debug_assert!(property.is_animatable());\n\n            if animating_properties.insert(property.to_physical(new_style.writing_mode)) {\n                number_of_animating_properties += 1;\n            }\n        }\n\n        let computed_steps = ComputedKeyframe::generate_for_keyframes(\n            element,\n            &keyframe_animation,\n            context,\n            new_style,\n            style.animation_timing_function_mod(i),\n            resolver,\n            animating_properties,\n            number_of_animating_properties,\n        );\n\n        let mut new_animation = Animation {\n            name: name.clone(),\n            properties_changed: keyframe_animation.properties_changed.clone(),\n            computed_steps,\n            started_at,\n            duration,\n            fill_mode: style.animation_fill_mode_mod(i),\n            delay: delay as f64,\n            iteration_state,\n            state,\n            direction: animation_direction,\n            current_direction: initial_direction,\n            number_of_animating_properties,\n            is_new: true,\n        };\n\n        // If we started with a negative delay, make sure we iterate the animation if\n        // the delay moves us past the first iteration.\n        while starting_progress > 1. && !new_animation.on_last_iteration() {\n            new_animation.iterate();\n            starting_progress -= 1.;\n        }\n\n        animation_state.dirty = true;\n\n        // If the animation was already present in the list for the node, just update its state.\n        for existing_animation in animation_state.animations.iter_mut() {\n            if existing_animation.state == AnimationState::Canceled {\n                continue;\n            }\n\n            if new_animation.name == existing_animation.name {\n                existing_animation\n                    .update_from_other(&new_animation, context.current_time_for_animations);\n                return;\n            }\n        }\n\n        animation_state.animations.push(new_animation);\n    }\n}\n"
  },
  {
    "path": "style/servo/attr.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parsed representations of [DOM attributes][attr].\n//!\n//! [attr]: https://dom.spec.whatwg.org/#interface-attr\n\nuse super::shadow_parts::ShadowParts;\nuse crate::color::{parsing::parse_color_keyword, AbsoluteColor};\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::Locked;\nuse crate::str::str_join;\nuse crate::str::{read_exponent, read_fraction, HTML_SPACE_CHARACTERS};\nuse crate::str::{read_numbers, split_commas, split_html_space_chars};\nuse crate::values::specified::color::Color;\nuse crate::values::specified::LengthPercentage;\nuse crate::values::AtomString;\nuse crate::{Atom, LocalName, Namespace, Prefix};\nuse app_units::Au;\nuse euclid::num::Zero;\nuse num_traits::ToPrimitive;\nuse selectors::attr::AttrSelectorOperation;\nuse servo_arc::Arc;\nuse std::str::FromStr;\n\n// Duplicated from script::dom::values.\nconst UNSIGNED_LONG_MAX: u32 = 2147483647;\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub enum LengthOrPercentageOrAuto {\n    Auto,\n    Percentage(f32),\n    Length(Au),\n}\n\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub enum AttrValue {\n    String(String),\n    TokenList(String, Vec<Atom>),\n    UInt(String, u32),\n    Int(String, i32),\n    Double(String, f64),\n    Atom(Atom),\n    LengthPercentage(String, Option<LengthPercentage>),\n    Color(String, Option<AbsoluteColor>),\n    Dimension(String, LengthOrPercentageOrAuto),\n\n    /// Stores a URL, computed from the input string and a document's base URL.\n    ///\n    /// The URL is resolved at setting-time, so this kind of attribute value is\n    /// not actually suitable for most URL-reflecting IDL attributes.\n    ResolvedUrl(\n        String,\n        #[ignore_malloc_size_of = \"Arc\"] Option<Arc<url::Url>>,\n    ),\n\n    /// Note that this variant is only used transitively as a fast path to set\n    /// the property declaration block relevant to the style of an element when\n    /// set from the inline declaration of that element (that is,\n    /// `element.style`).\n    ///\n    /// This can, as of this writing, only correspond to the value of the\n    /// `style` element, and is set from its relevant CSSInlineStyleDeclaration,\n    /// and then converted to a string in Element::attribute_mutated.\n    ///\n    /// Note that we don't necessarily need to do that (we could just clone the\n    /// declaration block), but that avoids keeping a refcounted\n    /// declarationblock for longer than needed.\n    Declaration(\n        String,\n        #[ignore_malloc_size_of = \"Arc\"] Arc<Locked<PropertyDeclarationBlock>>,\n    ),\n\n    /// The value of an `exportparts` attribute.\n    ShadowParts(String, ShadowParts),\n}\n\n/// Shared implementation to parse an integer according to\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-integers> or\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-negative-integers>\nfn do_parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i64, ()> {\n    let mut input = input\n        .skip_while(|c| HTML_SPACE_CHARACTERS.iter().any(|s| s == c))\n        .peekable();\n\n    let sign = match input.peek() {\n        None => return Err(()),\n        Some(&'-') => {\n            input.next();\n            -1\n        },\n        Some(&'+') => {\n            input.next();\n            1\n        },\n        Some(_) => 1,\n    };\n\n    let (value, _) = read_numbers(input);\n\n    value.and_then(|value| value.checked_mul(sign)).ok_or(())\n}\n\n/// Parse an integer according to\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-integers>.\npub fn parse_integer<T: Iterator<Item = char>>(input: T) -> Result<i32, ()> {\n    do_parse_integer(input).and_then(|result| result.to_i32().ok_or(()))\n}\n\n/// Parse an integer according to\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-negative-integers>\npub fn parse_unsigned_integer<T: Iterator<Item = char>>(input: T) -> Result<u32, ()> {\n    do_parse_integer(input).and_then(|result| result.to_u32().ok_or(()))\n}\n\n/// Parse a floating-point number according to\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-floating-point-number-values>\npub fn parse_double(string: &str) -> Result<f64, ()> {\n    let trimmed = string.trim_matches(HTML_SPACE_CHARACTERS);\n    let mut input = trimmed.chars().peekable();\n\n    let (value, divisor, chars_skipped) = match input.peek() {\n        None => return Err(()),\n        Some(&'-') => {\n            input.next();\n            (-1f64, -1f64, 1)\n        },\n        Some(&'+') => {\n            input.next();\n            (1f64, 1f64, 1)\n        },\n        _ => (1f64, 1f64, 0),\n    };\n\n    let (value, value_digits) = if let Some(&'.') = input.peek() {\n        (0f64, 0)\n    } else {\n        let (read_val, read_digits) = read_numbers(input);\n        (\n            value * read_val.and_then(|result| result.to_f64()).unwrap_or(1f64),\n            read_digits,\n        )\n    };\n\n    let input = trimmed\n        .chars()\n        .skip(value_digits + chars_skipped)\n        .peekable();\n\n    let (mut value, fraction_digits) = read_fraction(input, divisor, value);\n\n    let input = trimmed\n        .chars()\n        .skip(value_digits + chars_skipped + fraction_digits)\n        .peekable();\n\n    if let Some(exp) = read_exponent(input) {\n        value *= 10f64.powi(exp)\n    };\n\n    Ok(value)\n}\n\nimpl AttrValue {\n    pub fn from_serialized_tokenlist(tokens: String) -> AttrValue {\n        let atoms =\n            split_html_space_chars(&tokens)\n                .map(Atom::from)\n                .fold(vec![], |mut acc, atom| {\n                    if !acc.contains(&atom) {\n                        acc.push(atom)\n                    }\n                    acc\n                });\n        AttrValue::TokenList(tokens, atoms)\n    }\n\n    pub fn from_comma_separated_tokenlist(tokens: String) -> AttrValue {\n        let atoms = split_commas(&tokens)\n            .map(Atom::from)\n            .fold(vec![], |mut acc, atom| {\n                if !acc.contains(&atom) {\n                    acc.push(atom)\n                }\n                acc\n            });\n        AttrValue::TokenList(tokens, atoms)\n    }\n\n    pub fn from_atomic_tokens(atoms: Vec<Atom>) -> AttrValue {\n        // TODO(ajeffrey): effecient conversion of Vec<Atom> to String\n        let tokens = String::from(str_join(&atoms, \"\\x20\"));\n        AttrValue::TokenList(tokens, atoms)\n    }\n\n    // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-unsigned-long\n    pub fn from_u32(string: String, default: u32) -> AttrValue {\n        let result = parse_unsigned_integer(string.chars()).unwrap_or(default);\n        let result = if result > UNSIGNED_LONG_MAX {\n            default\n        } else {\n            result\n        };\n        AttrValue::UInt(string, result)\n    }\n\n    pub fn from_i32(string: String, default: i32) -> AttrValue {\n        let result = parse_integer(string.chars()).unwrap_or(default);\n        AttrValue::Int(string, result)\n    }\n\n    // https://html.spec.whatwg.org/multipage/#reflecting-content-attributes-in-idl-attributes:idl-double\n    pub fn from_double(string: String, default: f64) -> AttrValue {\n        let result = parse_double(&string).unwrap_or(default);\n\n        if result.is_normal() {\n            AttrValue::Double(string, result)\n        } else {\n            AttrValue::Double(string, default)\n        }\n    }\n\n    // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers\n    pub fn from_limited_i32(string: String, default: i32) -> AttrValue {\n        let result = parse_integer(string.chars()).unwrap_or(default);\n\n        if result < 0 {\n            AttrValue::Int(string, default)\n        } else {\n            AttrValue::Int(string, result)\n        }\n    }\n\n    // https://html.spec.whatwg.org/multipage/#limited-to-only-non-negative-numbers-greater-than-zero\n    pub fn from_limited_u32(string: String, default: u32) -> AttrValue {\n        let result = parse_unsigned_integer(string.chars()).unwrap_or(default);\n        let result = if result == 0 || result > UNSIGNED_LONG_MAX {\n            default\n        } else {\n            result\n        };\n        AttrValue::UInt(string, result)\n    }\n\n    pub fn from_atomic(string: String) -> AttrValue {\n        let value = Atom::from(string);\n        AttrValue::Atom(value)\n    }\n\n    pub fn from_resolved_url(base: &Arc<::url::Url>, url: String) -> AttrValue {\n        let joined = base.join(&url).ok().map(Arc::new);\n        AttrValue::ResolvedUrl(url, joined)\n    }\n\n    pub fn from_legacy_color(string: String) -> AttrValue {\n        let parsed = parse_legacy_color(&string).ok();\n        AttrValue::Color(string, parsed)\n    }\n\n    pub fn from_dimension(string: String) -> AttrValue {\n        let parsed = parse_length(&string);\n        AttrValue::Dimension(string, parsed)\n    }\n\n    pub fn from_nonzero_dimension(string: String) -> AttrValue {\n        let parsed = parse_nonzero_length(&string);\n        AttrValue::Dimension(string, parsed)\n    }\n\n    pub fn from_shadow_parts(string: String) -> AttrValue {\n        let shadow_parts = ShadowParts::parse(&string);\n        AttrValue::ShadowParts(string, shadow_parts)\n    }\n\n    /// Assumes the `AttrValue` is a `TokenList` and returns its tokens\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `TokenList`\n    pub fn as_tokens(&self) -> &[Atom] {\n        match *self {\n            AttrValue::TokenList(_, ref tokens) => tokens,\n            _ => panic!(\"Tokens not found\"),\n        }\n    }\n\n    /// Assumes the `AttrValue` is an `Atom` and returns its value\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not an `Atom`\n    pub fn as_atom(&self) -> &Atom {\n        match *self {\n            AttrValue::Atom(ref value) => value,\n            _ => panic!(\"Atom not found\"),\n        }\n    }\n\n    /// Assumes the `AttrValue` is a `LengthPercentage` and returns its value\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `LengthPercentage`\n    pub fn as_length_percentage(&self) -> Option<&LengthPercentage> {\n        match *self {\n            AttrValue::LengthPercentage(_, ref length_percentage) => length_percentage.as_ref(),\n            _ => panic!(\"LengthPercentage not found\"),\n        }\n    }\n\n    /// Assumes the `AttrValue` is a `Color` and returns its value\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `Color`\n    pub fn as_color(&self) -> Option<&AbsoluteColor> {\n        match *self {\n            AttrValue::Color(_, ref color) => color.as_ref(),\n            _ => panic!(\"Color not found\"),\n        }\n    }\n\n    /// Assumes the `AttrValue` is a `Dimension` and returns its value\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `Dimension`\n    pub fn as_dimension(&self) -> &LengthOrPercentageOrAuto {\n        match *self {\n            AttrValue::Dimension(_, ref l) => l,\n            _ => panic!(\"Dimension not found\"),\n        }\n    }\n\n    /// Assumes the `AttrValue` is a `ResolvedUrl` and returns its value.\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `ResolvedUrl`\n    pub fn as_resolved_url(&self) -> Option<&Arc<::url::Url>> {\n        match *self {\n            AttrValue::ResolvedUrl(_, ref url) => url.as_ref(),\n            _ => panic!(\"Url not found\"),\n        }\n    }\n\n    /// Return the AttrValue as its signed integer representation, if any.\n    /// This corresponds to attribute values returned as `AttrValue::Int(_)`\n    /// by `VirtualMethods::parse_plain_attribute()`.\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `Int`\n    pub fn as_int(&self) -> i32 {\n        if let AttrValue::Int(_, value) = *self {\n            value\n        } else {\n            panic!(\"Int not found\");\n        }\n    }\n\n    /// Return the AttrValue as its unsigned integer representation, if any.\n    /// This corresponds to attribute values returned as `AttrValue::UInt(_)`\n    /// by `VirtualMethods::parse_plain_attribute()`.\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `UInt`\n    pub fn as_uint(&self) -> u32 {\n        if let AttrValue::UInt(_, value) = *self {\n            value\n        } else {\n            panic!(\"Uint not found\");\n        }\n    }\n\n    /// Return the AttrValue as a dimension computed from its unsigned integer\n    /// representation, assuming that integer representation specifies pixels.\n    ///\n    /// This corresponds to attribute values returned as `AttrValue::UInt(_)`\n    /// by `VirtualMethods::parse_plain_attribute()`.\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a `UInt`\n    pub fn as_uint_px_dimension(&self) -> LengthOrPercentageOrAuto {\n        if let AttrValue::UInt(_, value) = *self {\n            LengthOrPercentageOrAuto::Length(Au::from_px(value as i32))\n        } else {\n            panic!(\"Uint not found\");\n        }\n    }\n\n    /// Return the AttrValue as it's shadow-part representation.\n    ///\n    /// This corresponds to attribute values returned as `AttrValue::ShadowParts(_)`\n    /// by `VirtualMethods::parse_plain_attribute()`.\n    ///\n    /// ## Panics\n    ///\n    /// Panics if the `AttrValue` is not a shadow-part.\n    pub fn as_shadow_parts(&self) -> &ShadowParts {\n        if let AttrValue::ShadowParts(_, value) = &self {\n            value\n        } else {\n            panic!(\"Not a shadowpart attribute\");\n        }\n    }\n\n    pub fn eval_selector(&self, selector: &AttrSelectorOperation<&AtomString>) -> bool {\n        // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants\n        // and doing Atom comparisons instead of string comparisons where possible,\n        // with SelectorImpl::AttrValue changed to Atom.\n        selector.eval_str(self)\n    }\n}\n\nimpl ::std::ops::Deref for AttrValue {\n    type Target = str;\n\n    fn deref(&self) -> &str {\n        match *self {\n            AttrValue::String(ref value)\n            | AttrValue::TokenList(ref value, _)\n            | AttrValue::UInt(ref value, _)\n            | AttrValue::Double(ref value, _)\n            | AttrValue::LengthPercentage(ref value, _)\n            | AttrValue::Color(ref value, _)\n            | AttrValue::Int(ref value, _)\n            | AttrValue::ResolvedUrl(ref value, _)\n            | AttrValue::Declaration(ref value, _)\n            | AttrValue::ShadowParts(ref value, _)\n            | AttrValue::Dimension(ref value, _) => &value,\n            AttrValue::Atom(ref value) => &value,\n        }\n    }\n}\n\nimpl PartialEq<Atom> for AttrValue {\n    fn eq(&self, other: &Atom) -> bool {\n        match *self {\n            AttrValue::Atom(ref value) => value == other,\n            _ => other == &**self,\n        }\n    }\n}\n\n/// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-non-zero-dimension-values>\npub fn parse_nonzero_length(value: &str) -> LengthOrPercentageOrAuto {\n    match parse_length(value) {\n        LengthOrPercentageOrAuto::Length(x) if x == Au::zero() => LengthOrPercentageOrAuto::Auto,\n        LengthOrPercentageOrAuto::Percentage(x) if x == 0. => LengthOrPercentageOrAuto::Auto,\n        x => x,\n    }\n}\n\n/// Parses a [legacy color][color]. If unparseable, `Err` is returned.\n///\n/// [color]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-colour-value\npub fn parse_legacy_color(mut input: &str) -> Result<AbsoluteColor, ()> {\n    // Steps 1 and 2.\n    if input.is_empty() {\n        return Err(());\n    }\n\n    // Step 3.\n    input = input.trim_matches(HTML_SPACE_CHARACTERS);\n\n    // Step 4.\n    if input.eq_ignore_ascii_case(\"transparent\") {\n        return Err(());\n    }\n\n    // Step 5.\n    if let Ok(Color::Absolute(ref absolute)) = parse_color_keyword(input) {\n        return Ok(absolute.color);\n    }\n\n    // Step 6.\n    if input.len() == 4 {\n        if let (b'#', Ok(r), Ok(g), Ok(b)) = (\n            input.as_bytes()[0],\n            hex(input.as_bytes()[1] as char),\n            hex(input.as_bytes()[2] as char),\n            hex(input.as_bytes()[3] as char),\n        ) {\n            return Ok(AbsoluteColor::srgb_legacy(r * 17, g * 17, b * 17, 1.0));\n        }\n    }\n\n    // Step 7.\n    let mut new_input = String::new();\n    for ch in input.chars() {\n        if ch as u32 > 0xffff {\n            new_input.push_str(\"00\")\n        } else {\n            new_input.push(ch)\n        }\n    }\n    let mut input = &*new_input;\n\n    // Step 8.\n    for (char_count, (index, _)) in input.char_indices().enumerate() {\n        if char_count == 128 {\n            input = &input[..index];\n            break;\n        }\n    }\n\n    // Step 9.\n    if input.as_bytes()[0] == b'#' {\n        input = &input[1..]\n    }\n\n    // Step 10.\n    let mut new_input = Vec::new();\n    for ch in input.chars() {\n        if hex(ch).is_ok() {\n            new_input.push(ch as u8)\n        } else {\n            new_input.push(b'0')\n        }\n    }\n    let mut input = new_input;\n\n    // Step 11.\n    while input.is_empty() || (input.len() % 3) != 0 {\n        input.push(b'0')\n    }\n\n    // Step 12.\n    let mut length = input.len() / 3;\n    let (mut red, mut green, mut blue) = (\n        &input[..length],\n        &input[length..length * 2],\n        &input[length * 2..],\n    );\n\n    // Step 13.\n    if length > 8 {\n        red = &red[length - 8..];\n        green = &green[length - 8..];\n        blue = &blue[length - 8..];\n        length = 8\n    }\n\n    // Step 14.\n    while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {\n        red = &red[1..];\n        green = &green[1..];\n        blue = &blue[1..];\n        length -= 1\n    }\n\n    // Steps 15-20.\n    return Ok(AbsoluteColor::srgb_legacy(\n        hex_string(red).unwrap(),\n        hex_string(green).unwrap(),\n        hex_string(blue).unwrap(),\n        1.0,\n    ));\n\n    fn hex(ch: char) -> Result<u8, ()> {\n        match ch {\n            '0'..='9' => Ok((ch as u8) - b'0'),\n            'a'..='f' => Ok((ch as u8) - b'a' + 10),\n            'A'..='F' => Ok((ch as u8) - b'A' + 10),\n            _ => Err(()),\n        }\n    }\n\n    fn hex_string(string: &[u8]) -> Result<u8, ()> {\n        match string.len() {\n            0 => Err(()),\n            1 => hex(string[0] as char),\n            _ => {\n                let upper = hex(string[0] as char)?;\n                let lower = hex(string[1] as char)?;\n                Ok((upper << 4) | lower)\n            },\n        }\n    }\n}\n\n/// Parses a [dimension value][dim]. If unparseable, `Auto` is returned.\n///\n/// [dim]: https://html.spec.whatwg.org/multipage/#rules-for-parsing-dimension-values\n// TODO: this function can be rewritten to return Result<LengthPercentage, _>\npub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {\n    // Steps 1 & 2 are not relevant\n\n    // Step 3\n    value = value.trim_start_matches(HTML_SPACE_CHARACTERS);\n\n    // Step 4\n    match value.chars().nth(0) {\n        Some('0'..='9') => {},\n        _ => return LengthOrPercentageOrAuto::Auto,\n    }\n\n    // Steps 5 to 8\n    // We trim the string length to the minimum of:\n    // 1. the end of the string\n    // 2. the first occurence of a '%' (U+0025 PERCENT SIGN)\n    // 3. the second occurrence of a '.' (U+002E FULL STOP)\n    // 4. the occurrence of a character that is neither a digit nor '%' nor '.'\n    // Note: Step 7.4 is directly subsumed by FromStr::from_str\n    let mut end_index = value.len();\n    let (mut found_full_stop, mut found_percent) = (false, false);\n    for (i, ch) in value.chars().enumerate() {\n        match ch {\n            '0'..='9' => continue,\n            '%' => {\n                found_percent = true;\n                end_index = i;\n                break;\n            },\n            '.' if !found_full_stop => {\n                found_full_stop = true;\n                continue;\n            },\n            _ => {\n                end_index = i;\n                break;\n            },\n        }\n    }\n    value = &value[..end_index];\n\n    if found_percent {\n        let result: Result<f32, _> = FromStr::from_str(value);\n        match result {\n            Ok(number) => return LengthOrPercentageOrAuto::Percentage((number as f32) / 100.0),\n            Err(_) => return LengthOrPercentageOrAuto::Auto,\n        }\n    }\n\n    match FromStr::from_str(value) {\n        Ok(number) => LengthOrPercentageOrAuto::Length(Au::from_f64_px(number)),\n        Err(_) => LengthOrPercentageOrAuto::Auto,\n    }\n}\n\n/// A struct that uniquely identifies an element's attribute.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct AttrIdentifier {\n    pub local_name: LocalName,\n    pub name: LocalName,\n    pub namespace: Namespace,\n    pub prefix: Option<Prefix>,\n}\n"
  },
  {
    "path": "style/servo/encoding_support.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parsing stylesheets from bytes (not `&str`).\n\nuse crate::context::QuirksMode;\nuse crate::error_reporting::ParseErrorReporter;\nuse crate::media_queries::MediaList;\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::stylesheets::{AllowImportRules, Origin, Stylesheet, StylesheetLoader, UrlExtraData};\nuse cssparser::{stylesheet_encoding, EncodingSupport};\nuse servo_arc::Arc;\nuse std::borrow::Cow;\nuse std::str;\n\nstruct EncodingRs;\n\nimpl EncodingSupport for EncodingRs {\n    type Encoding = &'static encoding_rs::Encoding;\n\n    fn utf8() -> Self::Encoding {\n        encoding_rs::UTF_8\n    }\n\n    fn is_utf16_be_or_le(encoding: &Self::Encoding) -> bool {\n        *encoding == encoding_rs::UTF_16LE || *encoding == encoding_rs::UTF_16BE\n    }\n\n    fn from_label(ascii_label: &[u8]) -> Option<Self::Encoding> {\n        encoding_rs::Encoding::for_label(ascii_label)\n    }\n}\n\nfn decode_stylesheet_bytes<'a>(\n    css: &'a [u8],\n    protocol_encoding_label: Option<&str>,\n    environment_encoding: Option<&'static encoding_rs::Encoding>,\n) -> Cow<'a, str> {\n    let fallback_encoding = stylesheet_encoding::<EncodingRs>(\n        css,\n        protocol_encoding_label.map(str::as_bytes),\n        environment_encoding,\n    );\n    let (result, _used_encoding, _) = fallback_encoding.decode(&css);\n    // FIXME record used encoding for environment encoding of @import\n    result\n}\n\nimpl Stylesheet {\n    /// Parse a stylesheet from a set of bytes, potentially received over the\n    /// network.\n    ///\n    /// Takes care of decoding the network bytes and forwards the resulting\n    /// string to `Stylesheet::from_str`.\n    pub fn from_bytes(\n        bytes: &[u8],\n        url_data: UrlExtraData,\n        protocol_encoding_label: Option<&str>,\n        environment_encoding: Option<&'static encoding_rs::Encoding>,\n        origin: Origin,\n        media: Arc<Locked<MediaList>>,\n        shared_lock: SharedRwLock,\n        stylesheet_loader: Option<&dyn StylesheetLoader>,\n        error_reporter: Option<&dyn ParseErrorReporter>,\n        quirks_mode: QuirksMode,\n    ) -> Stylesheet {\n        let string = decode_stylesheet_bytes(bytes, protocol_encoding_label, environment_encoding);\n        Stylesheet::from_str(\n            &string,\n            url_data,\n            origin,\n            media,\n            shared_lock,\n            stylesheet_loader,\n            error_reporter,\n            quirks_mode,\n            AllowImportRules::Yes,\n        )\n    }\n}\n"
  },
  {
    "path": "style/servo/media_features.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Servo's media feature list and evaluator.\n\nuse crate::derives::*;\nuse crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};\nuse crate::queries::values::PrefersColorScheme;\nuse crate::values::computed::{CSSPixelLength, Context, Resolution};\nuse std::fmt::Debug;\n\n/// https://drafts.csswg.org/mediaqueries-4/#width\nfn eval_width(context: &Context) -> CSSPixelLength {\n    CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px())\n}\n\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]\n#[repr(u8)]\nenum Scan {\n    Progressive,\n    Interlace,\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#scan\nfn eval_scan(_: &Context, _: Option<Scan>) -> bool {\n    // Since we doesn't support the 'tv' media type, the 'scan' feature never\n    // matches.\n    false\n}\n\n/// https://drafts.csswg.org/mediaqueries-4/#resolution\nfn eval_resolution(context: &Context) -> Resolution {\n    Resolution::from_dppx(context.device().device_pixel_ratio().0)\n}\n\n/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio\nfn eval_device_pixel_ratio(context: &Context) -> f32 {\n    eval_resolution(context).dppx()\n}\n\nfn eval_prefers_color_scheme(context: &Context, query_value: Option<PrefersColorScheme>) -> bool {\n    match query_value {\n        Some(v) => context.device().color_scheme() == v,\n        None => true,\n    }\n}\n\n/// A list with all the media features that Servo supports.\npub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [\n    feature!(\n        atom!(\"width\"),\n        AllowsRanges::Yes,\n        Evaluator::Length(eval_width),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"scan\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_scan, Scan),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"resolution\"),\n        AllowsRanges::Yes,\n        Evaluator::Resolution(eval_resolution),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"device-pixel-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::Float(eval_device_pixel_ratio),\n        FeatureFlags::WEBKIT_PREFIX,\n    ),\n    feature!(\n        atom!(\"-moz-device-pixel-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::Float(eval_device_pixel_ratio),\n        FeatureFlags::empty(),\n    ),\n    feature!(\n        atom!(\"prefers-color-scheme\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme),\n        FeatureFlags::empty(),\n    ),\n];\n"
  },
  {
    "path": "style/servo/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Servo-specific bits of the style system.\n//!\n//! These get compiled out on a Gecko build.\n\npub mod animation;\n#[allow(missing_docs)] // TODO.\npub mod attr;\nmod encoding_support;\npub mod media_features;\npub mod restyle_damage;\npub mod selector_parser;\nmod shadow_parts;\npub mod url;\n"
  },
  {
    "path": "style/servo/restyle_damage.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The restyle damage is a hint that tells layout which kind of operations may\n//! be needed in presence of incremental style changes.\n\nuse bitflags::Flags;\n\nuse crate::computed_values::isolation::T as Isolation;\nuse crate::computed_values::mix_blend_mode::T as MixBlendMode;\nuse crate::computed_values::transform_style::T as TransformStyle;\nuse crate::dom::TElement;\nuse crate::matching::{StyleChange, StyleDifference};\nuse crate::properties::{\n    restyle_damage_rebuild_box, restyle_damage_rebuild_stacking_context,\n    restyle_damage_recalculate_overflow, restyle_damage_repaint, style_structs, ComputedValues,\n};\nuse crate::values::computed::basic_shape::ClipPath;\nuse crate::values::computed::Perspective;\nuse crate::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate};\nuse std::fmt;\n\nbitflags! {\n    /// Major phases of layout that need to be run due to the damage to a node during restyling. In\n    /// addition to the 4 bytes used for that, the rest of the `u16` is exposed as an extension point\n    /// for users of the crate to add their own custom types of damage that correspond to the\n    /// layout system they are implementing.\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct ServoRestyleDamage: u16 {\n        /// Repaint the node itself.\n        ///\n        /// Propagates both up and down the flow tree.\n        const REPAINT = 0b0001;\n\n        /// Rebuilds the stacking contexts.\n        ///\n        /// Propagates both up and down the flow tree.\n        const REBUILD_STACKING_CONTEXT = 0b0011;\n\n        /// Recalculates the scrollable overflow.\n        ///\n        /// Propagates both up and down the flow tree.\n        const RECALCULATE_OVERFLOW = 0b0111;\n\n        /// Any other type of damage, which requires running layout again.\n        ///\n        /// Propagates both up and down the flow tree.\n        const RELAYOUT = 0b1111;\n    }\n}\n\nmalloc_size_of::malloc_size_of_is_0!(ServoRestyleDamage);\n\nimpl ServoRestyleDamage {\n    /// Compute the `StyleDifference` (including the appropriate restyle damage)\n    /// for a given style change between `old` and `new`.\n    pub fn compute_style_difference<E: TElement>(\n        old: &ComputedValues,\n        new: &ComputedValues,\n    ) -> StyleDifference {\n        if std::ptr::eq(old, new) {\n            return StyleDifference {\n                damage: ServoRestyleDamage::empty(),\n                change: StyleChange::Unchanged,\n            };\n        }\n\n        let mut damage = compute_damage(old, new);\n        if damage.contains(ServoRestyleDamage::RELAYOUT) {\n            damage |= E::compute_layout_damage(old, new);\n        }\n\n        let mut any_style_changed = !damage.is_empty();\n        // FIXME(emilio): Differentiate between reset and inherited\n        // properties here, and set `reset_only` appropriately so the\n        // optimization to skip the cascade in those cases applies.\n        let mut reset_only = !any_style_changed;\n\n        let old_custom_props = old.custom_properties();\n        let new_custom_props = new.custom_properties();\n        let mut custom_properties_changed = false;\n        if old_custom_props.inherited != new_custom_props.inherited {\n            any_style_changed = true;\n            custom_properties_changed = true;\n            reset_only = false;\n        } else if old_custom_props.non_inherited != new_custom_props.non_inherited {\n            any_style_changed = true;\n            custom_properties_changed = true;\n        };\n        if custom_properties_changed {\n            // Paint worklets may depend on custom properties, so if they have changed we should repaint.\n            damage.insert(ServoRestyleDamage::REPAINT);\n        }\n\n        let change = if any_style_changed {\n            StyleChange::Changed {\n                reset_only,\n                custom_properties_changed,\n            }\n        } else {\n            StyleChange::Unchanged\n        };\n\n        StyleDifference { damage, change }\n    }\n\n    /// Returns a bitmask indicating that the frame needs to be reconstructed.\n    pub fn reconstruct() -> ServoRestyleDamage {\n        // There's no way of knowing what kind of damage system the embedder will use, but part of\n        // this interface is that a fully saturated restyle damage means to rebuild everything.\n        ServoRestyleDamage::from_bits_retain(<ServoRestyleDamage as Flags>::Bits::MAX)\n    }\n}\n\nimpl Default for ServoRestyleDamage {\n    fn default() -> Self {\n        Self::empty()\n    }\n}\n\nimpl fmt::Display for ServoRestyleDamage {\n    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {\n        let mut first_elem = true;\n\n        let to_iter = [\n            (ServoRestyleDamage::REPAINT, \"Repaint\"),\n            (\n                ServoRestyleDamage::REBUILD_STACKING_CONTEXT,\n                \"Rebuild stacking context\",\n            ),\n            (\n                ServoRestyleDamage::RECALCULATE_OVERFLOW,\n                \"Recalculate overflow\",\n            ),\n            (ServoRestyleDamage::RELAYOUT, \"Relayout\"),\n        ];\n\n        for &(damage, damage_str) in &to_iter {\n            if self.contains(damage) {\n                if !first_elem {\n                    write!(f, \" | \")?;\n                }\n                write!(f, \"{}\", damage_str)?;\n                first_elem = false;\n            }\n        }\n\n        if first_elem {\n            write!(f, \"NoDamage\")?;\n        }\n\n        Ok(())\n    }\n}\n\nfn augmented_restyle_damage_rebuild_box(old: &ComputedValues, new: &ComputedValues) -> bool {\n    let old_box = old.get_box();\n    let new_box = new.get_box();\n    restyle_damage_rebuild_box(old, new)\n        || old_box.original_display != new_box.original_display\n        || old_box.has_transform_or_perspective() != new_box.has_transform_or_perspective()\n        || old.get_effects().filter.0.is_empty() != new.get_effects().filter.0.is_empty()\n}\n\nfn augmented_restyle_damage_rebuild_stacking_context(\n    old: &ComputedValues,\n    new: &ComputedValues,\n) -> bool {\n    restyle_damage_rebuild_stacking_context(old, new)\n        || old.guarantees_stacking_context() != new.guarantees_stacking_context()\n}\n\nfn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage {\n    // Damage flags higher up the if-else chain imply damage flags lower down the if-else chain,\n    // so we can skip the diffing process for later flags if an earlier flag is true\n    if augmented_restyle_damage_rebuild_box(old, new) {\n        ServoRestyleDamage::RELAYOUT\n    } else if restyle_damage_recalculate_overflow(old, new) {\n        ServoRestyleDamage::RECALCULATE_OVERFLOW\n    } else if augmented_restyle_damage_rebuild_stacking_context(old, new) {\n        ServoRestyleDamage::REBUILD_STACKING_CONTEXT\n    } else if restyle_damage_repaint(old, new) {\n        ServoRestyleDamage::REPAINT\n    } else {\n        ServoRestyleDamage::empty()\n    }\n}\n\nimpl ComputedValues {\n    /// Some properties establish a stacking context when they are set to a non-initial value.\n    /// In that case, the damage is only set to `ServoRestyleDamage::REPAINT` because we don't\n    /// need to rebuild stacking contexts when the style changes between different non-initial\n    /// values. This function checks whether any of these properties is set to a value that\n    /// guarantees a stacking context, so that we only do the work when this changes.\n    /// Note that it's still possible to establish a stacking context when this returns false.\n    pub fn guarantees_stacking_context(&self) -> bool {\n        self.get_effects().opacity != 1.0\n            || self.get_effects().mix_blend_mode != MixBlendMode::Normal\n            || self.get_svg().clip_path != ClipPath::None\n            || self.get_box().isolation == Isolation::Isolate\n    }\n}\n\nimpl style_structs::Box {\n    /// Whether there is a non-default transform or perspective style set\n    pub fn has_transform_or_perspective(&self) -> bool {\n        !self.transform.0.is_empty()\n            || self.scale != GenericScale::None\n            || self.rotate != GenericRotate::None\n            || self.translate != GenericTranslate::None\n            || self.perspective != Perspective::None\n            || self.transform_style == TransformStyle::Preserve3d\n    }\n}\n"
  },
  {
    "path": "style/servo/selector_parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![deny(missing_docs)]\n\n//! Servo's selector parser.\n\nuse crate::attr::{AttrIdentifier, AttrValue};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::derives::*;\nuse crate::dom::{OpaqueNode, TElement, TNode};\nuse crate::invalidation::element::document_state::InvalidationMatchingData;\nuse crate::invalidation::element::element_wrapper::ElementSnapshot;\nuse crate::properties::longhands::display::computed_value::T as Display;\nuse crate::properties::{ComputedValues, PropertyFlags};\nuse crate::selector_parser::AttrValue as SelectorAttrValue;\nuse crate::selector_parser::{PseudoElementCascadeType, SelectorParser};\nuse crate::values::{AtomIdent, AtomString};\nuse crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};\nuse cssparser::{\n    match_ignore_ascii_case, serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation,\n    ToCss,\n};\nuse dom::{DocumentState, ElementState};\nuse rustc_hash::FxHashMap;\nuse selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};\nuse selectors::parser::SelectorParseErrorKind;\nuse selectors::visitor::SelectorVisitor;\nuse std::fmt;\nuse std::mem;\nuse std::ops::{Deref, DerefMut};\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// A pseudo-element, both public and private.\n///\n/// NB: If you add to this list, be sure to update `each_simple_pseudo_element` too.\n#[derive(\n    Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(usize)]\npub enum PseudoElement {\n    // Eager pseudos. Keep these first so that eager_index() works.\n    After = 0,\n    Before,\n    Selection,\n    // If/when :first-letter is added, update is_first_letter accordingly.\n\n    // If/when :first-line is added, update is_first_line accordingly.\n\n    // If/when ::first-letter or ::first-line are added, adjust our\n    // property_restriction implementation to do property filtering for them.\n    // Also, make sure the UA sheet has the !important rules some of the\n    // APPLIES_TO_PLACEHOLDER properties expect!\n    FirstLetter,\n\n    // Non-eager pseudos.\n    Backdrop,\n    DetailsContent,\n    Marker,\n\n    // Implemented pseudos. These pseudo elements are representing the\n    // elements within an UA shadow DOM, and matching the elements with\n    // their appropriate styles.\n    ColorSwatch,\n    FileSelectorButton,\n    Placeholder,\n    SliderFill,\n    SliderThumb,\n    SliderTrack,\n\n    // Private, Servo-specific implemented pseudos. Only matchable in UA sheet.\n    ServoTextControlInnerContainer,\n    ServoTextControlInnerEditor,\n\n    // Other Servo-specific pseudos.\n    ServoAnonymousBox,\n    ServoAnonymousTable,\n    ServoAnonymousTableCell,\n    ServoAnonymousTableRow,\n    ServoTableGrid,\n    ServoTableWrapper,\n}\n\n/// The count of all pseudo-elements.\npub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1;\n\nimpl ToCss for PseudoElement {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use self::PseudoElement::*;\n        dest.write_str(match *self {\n            After => \"::after\",\n            Before => \"::before\",\n            Selection => \"::selection\",\n            FirstLetter => \"::first-letter\",\n            Backdrop => \"::backdrop\",\n            DetailsContent => \"::details-content\",\n            Marker => \"::marker\",\n            ColorSwatch => \"::color-swatch\",\n            FileSelectorButton => \"::file-selector-button\",\n            Placeholder => \"::placeholder\",\n            SliderFill => \"::slider-fill\",\n            SliderTrack => \"::slider-track\",\n            SliderThumb => \"::slider-thumb\",\n            ServoTextControlInnerContainer => \"::-servo-text-control-inner-container\",\n            ServoTextControlInnerEditor => \"::-servo-text-control-inner-editor\",\n            ServoAnonymousBox => \"::-servo-anonymous-box\",\n            ServoAnonymousTable => \"::-servo-anonymous-table\",\n            ServoAnonymousTableCell => \"::-servo-anonymous-table-cell\",\n            ServoAnonymousTableRow => \"::-servo-anonymous-table-row\",\n            ServoTableGrid => \"::-servo-table-grid\",\n            ServoTableWrapper => \"::-servo-table-wrapper\",\n        })\n    }\n}\n\nimpl ::selectors::parser::PseudoElement for PseudoElement {\n    type Impl = SelectorImpl;\n\n    fn parses_as_element_backed(&self) -> bool {\n        matches!(self, Self::DetailsContent)\n    }\n}\n\n/// The number of eager pseudo-elements. Keep this in sync with cascade_type.\npub const EAGER_PSEUDO_COUNT: usize = 4;\n\nimpl PseudoElement {\n    /// Gets the canonical index of this eagerly-cascaded pseudo-element.\n    #[inline]\n    pub fn eager_index(&self) -> usize {\n        debug_assert!(self.is_eager());\n        self.clone() as usize\n    }\n\n    /// An index for this pseudo-element to be indexed in an enumerated array.\n    #[inline]\n    pub fn index(&self) -> usize {\n        self.clone() as usize\n    }\n\n    /// An array of `None`, one per pseudo-element.\n    pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {\n        Default::default()\n    }\n\n    /// Creates a pseudo-element from an eager index.\n    #[inline]\n    pub fn from_eager_index(i: usize) -> Self {\n        assert!(i < EAGER_PSEUDO_COUNT);\n        let result: PseudoElement = unsafe { mem::transmute(i) };\n        debug_assert!(result.is_eager());\n        result\n    }\n\n    /// Whether the current pseudo element is ::before or ::after.\n    #[inline]\n    pub fn is_before_or_after(&self) -> bool {\n        self.is_before() || self.is_after()\n    }\n\n    /// Whether this is an unknown ::-webkit- pseudo-element.\n    #[inline]\n    pub fn is_unknown_webkit_pseudo_element(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is the ::marker pseudo.\n    #[inline]\n    pub fn is_marker(&self) -> bool {\n        *self == PseudoElement::Marker\n    }\n\n    /// Whether this pseudo-element is the ::selection pseudo.\n    #[inline]\n    pub fn is_selection(&self) -> bool {\n        *self == PseudoElement::Selection\n    }\n\n    /// Whether this pseudo-element is the ::before pseudo.\n    #[inline]\n    pub fn is_before(&self) -> bool {\n        *self == PseudoElement::Before\n    }\n\n    /// Whether this pseudo-element is the ::after pseudo.\n    #[inline]\n    pub fn is_after(&self) -> bool {\n        *self == PseudoElement::After\n    }\n\n    /// Whether the current pseudo element is :first-letter\n    #[inline]\n    pub fn is_first_letter(&self) -> bool {\n        *self == PseudoElement::FirstLetter\n    }\n\n    /// Whether the current pseudo element is :first-line\n    #[inline]\n    pub fn is_first_line(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is representing the color swatch\n    /// inside an `<input>` element.\n    #[inline]\n    pub fn is_color_swatch(&self) -> bool {\n        *self == PseudoElement::ColorSwatch\n    }\n\n    /// Whether this pseudo-element is eagerly-cascaded.\n    #[inline]\n    pub fn is_eager(&self) -> bool {\n        self.cascade_type() == PseudoElementCascadeType::Eager\n    }\n\n    /// Whether this pseudo-element is lazily-cascaded.\n    #[inline]\n    pub fn is_lazy(&self) -> bool {\n        self.cascade_type() == PseudoElementCascadeType::Lazy\n    }\n\n    /// Whether this pseudo-element is for an anonymous box.\n    pub fn is_anon_box(&self) -> bool {\n        self.is_precomputed()\n    }\n\n    /// Whether this pseudo-element skips flex/grid container display-based\n    /// fixup.\n    #[inline]\n    pub fn skip_item_display_fixup(&self) -> bool {\n        !self.is_before_or_after()\n    }\n\n    /// Whether this pseudo-element is precomputed.\n    #[inline]\n    pub fn is_precomputed(&self) -> bool {\n        self.cascade_type() == PseudoElementCascadeType::Precomputed\n    }\n\n    /// Returns which kind of cascade type has this pseudo.\n    ///\n    /// See the documentation for `PseudoElementCascadeType` for how we choose\n    /// which cascade type to use.\n    ///\n    /// Note: Keep eager pseudos in sync with `EAGER_PSEUDO_COUNT` and\n    /// `EMPTY_PSEUDO_ARRAY` in `style/data.rs`\n    #[inline]\n    pub fn cascade_type(&self) -> PseudoElementCascadeType {\n        match *self {\n            PseudoElement::After\n            | PseudoElement::Before\n            | PseudoElement::FirstLetter\n            | PseudoElement::Selection => PseudoElementCascadeType::Eager,\n            PseudoElement::Backdrop\n            | PseudoElement::ColorSwatch\n            | PseudoElement::FileSelectorButton\n            | PseudoElement::Marker\n            | PseudoElement::Placeholder\n            | PseudoElement::DetailsContent\n            | PseudoElement::SliderFill\n            | PseudoElement::SliderThumb\n            | PseudoElement::SliderTrack\n            | PseudoElement::ServoTextControlInnerContainer\n            | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,\n            PseudoElement::ServoAnonymousBox\n            | PseudoElement::ServoAnonymousTable\n            | PseudoElement::ServoAnonymousTableCell\n            | PseudoElement::ServoAnonymousTableRow\n            | PseudoElement::ServoTableGrid\n            | PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,\n        }\n    }\n\n    /// Covert non-canonical pseudo-element to canonical one, and keep a\n    /// canonical one as it is.\n    pub fn canonical(&self) -> PseudoElement {\n        self.clone()\n    }\n\n    /// Stub, only Gecko needs this\n    pub fn pseudo_info(&self) {\n        ()\n    }\n\n    /// Property flag that properties must have to apply to this pseudo-element.\n    #[inline]\n    pub fn property_restriction(&self) -> Option<PropertyFlags> {\n        Some(match self {\n            PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,\n            PseudoElement::Marker if static_prefs::pref!(\"layout.css.marker.restricted\") => {\n                PropertyFlags::APPLIES_TO_MARKER\n            },\n            PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,\n            _ => return None,\n        })\n    }\n\n    /// Whether this pseudo-element should actually exist if it has\n    /// the given styles.\n    pub fn should_exist(&self, style: &ComputedValues) -> bool {\n        let display = style.get_box().clone_display();\n        if display == Display::None {\n            return false;\n        }\n        if self.is_before_or_after() && style.ineffective_content_property() {\n            return false;\n        }\n\n        true\n    }\n\n    /// Whether this pseudo-element is the ::highlight pseudo.\n    pub fn is_highlight(&self) -> bool {\n        false\n    }\n\n    /// Whether this pseudo-element is the ::target-text pseudo.\n    #[inline]\n    pub fn is_target_text(&self) -> bool {\n        false\n    }\n\n    /// Whether this is a highlight pseudo-element that is styled lazily during\n    /// painting rather than during the restyle traversal. These pseudos need\n    /// explicit repaint triggering when their styles change.\n    #[inline]\n    pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {\n        self.is_selection() || self.is_highlight() || self.is_target_text()\n    }\n\n    /// Whether this pseudo-element is \"element-backed\", which means that it inherits from its regular\n    /// flat tree parent, which might not be the originating element.\n    #[inline]\n    pub fn is_element_backed(&self) -> bool {\n        use selectors::parser::PseudoElement;\n        self.parses_as_element_backed()\n            || matches!(\n                self,\n                Self::Placeholder\n                    | Self::ColorSwatch\n                    | Self::FileSelectorButton\n                    | Self::SliderFill\n                    | Self::SliderThumb\n                    | Self::SliderTrack\n                    | Self::ServoTextControlInnerContainer\n                    | Self::ServoTextControlInnerEditor,\n            )\n    }\n}\n\n/// The type used for storing `:lang` arguments.\npub type Lang = Box<str>;\n\n/// The type used to store the state argument to the `:state` pseudo-class.\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]\npub struct CustomState(pub AtomIdent);\n\n/// A non tree-structural pseudo-class.\n/// See https://drafts.csswg.org/selectors-4/#structural-pseudos\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]\n#[allow(missing_docs)]\npub enum NonTSPseudoClass {\n    Active,\n    AnyLink,\n    Autofill,\n    Checked,\n    /// The :state` pseudo-class.\n    CustomState(CustomState),\n    Default,\n    Defined,\n    Disabled,\n    Enabled,\n    Focus,\n    FocusWithin,\n    FocusVisible,\n    Fullscreen,\n    Hover,\n    InRange,\n    Indeterminate,\n    Invalid,\n    Lang(Lang),\n    Link,\n    Modal,\n    MozMeterOptimum,\n    MozMeterSubOptimum,\n    MozMeterSubSubOptimum,\n    Open,\n    Optional,\n    OutOfRange,\n    PlaceholderShown,\n    PopoverOpen,\n    ReadOnly,\n    ReadWrite,\n    Required,\n    ServoNonZeroBorder,\n    Target,\n    UserInvalid,\n    UserValid,\n    Valid,\n    Visited,\n}\n\nimpl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {\n    type Impl = SelectorImpl;\n\n    #[inline]\n    fn is_active_or_hover(&self) -> bool {\n        matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)\n    }\n\n    #[inline]\n    fn is_user_action_state(&self) -> bool {\n        matches!(\n            *self,\n            NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus\n        )\n    }\n\n    fn visit<V>(&self, _: &mut V) -> bool\n    where\n        V: SelectorVisitor<Impl = Self::Impl>,\n    {\n        true\n    }\n}\n\nimpl ToCss for NonTSPseudoClass {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use self::NonTSPseudoClass::*;\n        if let Lang(ref lang) = *self {\n            dest.write_str(\":lang(\")?;\n            serialize_identifier(lang, dest)?;\n            return dest.write_char(')');\n        }\n\n        dest.write_str(match *self {\n            Self::Active => \":active\",\n            Self::AnyLink => \":any-link\",\n            Self::Autofill => \":autofill\",\n            Self::Checked => \":checked\",\n            Self::CustomState(ref state) => {\n                dest.write_str(\":state(\")?;\n                state.0.to_css(dest)?;\n                return dest.write_char(')');\n            },\n            Self::Default => \":default\",\n            Self::Defined => \":defined\",\n            Self::Disabled => \":disabled\",\n            Self::Enabled => \":enabled\",\n            Self::Focus => \":focus\",\n            Self::FocusVisible => \":focus-visible\",\n            Self::FocusWithin => \":focus-within\",\n            Self::Fullscreen => \":fullscreen\",\n            Self::Hover => \":hover\",\n            Self::InRange => \":in-range\",\n            Self::Indeterminate => \":indeterminate\",\n            Self::Invalid => \":invalid\",\n            Self::Link => \":link\",\n            Self::Modal => \":modal\",\n            Self::MozMeterOptimum => \":-moz-meter-optimum\",\n            Self::MozMeterSubOptimum => \":-moz-meter-sub-optimum\",\n            Self::MozMeterSubSubOptimum => \":-moz-meter-sub-sub-optimum\",\n            Self::Open => \":open\",\n            Self::Optional => \":optional\",\n            Self::OutOfRange => \":out-of-range\",\n            Self::PlaceholderShown => \":placeholder-shown\",\n            Self::PopoverOpen => \":popover-open\",\n            Self::ReadOnly => \":read-only\",\n            Self::ReadWrite => \":read-write\",\n            Self::Required => \":required\",\n            Self::ServoNonZeroBorder => \":-servo-nonzero-border\",\n            Self::Target => \":target\",\n            Self::UserInvalid => \":user-invalid\",\n            Self::UserValid => \":user-valid\",\n            Self::Valid => \":valid\",\n            Self::Visited => \":visited\",\n            Self::Lang(_) => unreachable!(),\n        })\n    }\n}\n\nimpl NonTSPseudoClass {\n    /// Gets a given state flag for this pseudo-class. This is used to do\n    /// selector matching, and it's set from the DOM.\n    pub fn state_flag(&self) -> ElementState {\n        match *self {\n            Self::Active => ElementState::ACTIVE,\n            Self::AnyLink => ElementState::VISITED_OR_UNVISITED,\n            Self::Autofill => ElementState::AUTOFILL,\n            Self::Checked => ElementState::CHECKED,\n            Self::Default => ElementState::DEFAULT,\n            Self::Defined => ElementState::DEFINED,\n            Self::Disabled => ElementState::DISABLED,\n            Self::Enabled => ElementState::ENABLED,\n            Self::Focus => ElementState::FOCUS,\n            Self::FocusVisible => ElementState::FOCUSRING,\n            Self::FocusWithin => ElementState::FOCUS_WITHIN,\n            Self::Fullscreen => ElementState::FULLSCREEN,\n            Self::Hover => ElementState::HOVER,\n            Self::InRange => ElementState::INRANGE,\n            Self::Indeterminate => ElementState::INDETERMINATE,\n            Self::Invalid => ElementState::INVALID,\n            Self::Link => ElementState::UNVISITED,\n            Self::Modal => ElementState::MODAL,\n            Self::MozMeterOptimum => ElementState::OPTIMUM,\n            Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,\n            Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,\n            Self::Open => ElementState::OPEN,\n            Self::Optional => ElementState::OPTIONAL_,\n            Self::OutOfRange => ElementState::OUTOFRANGE,\n            Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,\n            Self::PopoverOpen => ElementState::POPOVER_OPEN,\n            Self::ReadOnly => ElementState::READONLY,\n            Self::ReadWrite => ElementState::READWRITE,\n            Self::Required => ElementState::REQUIRED,\n            Self::Target => ElementState::URLTARGET,\n            Self::UserInvalid => ElementState::USER_INVALID,\n            Self::UserValid => ElementState::USER_VALID,\n            Self::Valid => ElementState::VALID,\n            Self::Visited => ElementState::VISITED,\n            Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => {\n                ElementState::empty()\n            },\n        }\n    }\n\n    /// Get the document state flag associated with a pseudo-class, if any.\n    pub fn document_state_flag(&self) -> DocumentState {\n        DocumentState::empty()\n    }\n\n    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.\n    pub fn needs_cache_revalidation(&self) -> bool {\n        self.state_flag().is_empty()\n    }\n}\n\n/// The abstract struct we implement the selector parser implementation on top\n/// of.\n#[derive(Clone, Debug, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct SelectorImpl;\n\n/// A set of extra data to carry along with the matching context, either for\n/// selector-matching or invalidation.\n#[derive(Debug, Default)]\npub struct ExtraMatchingData<'a> {\n    /// The invalidation data to invalidate doc-state pseudo-classes correctly.\n    pub invalidation_data: InvalidationMatchingData,\n\n    /// The invalidation bits from matching container queries. These are here\n    /// just for convenience mostly.\n    pub cascade_input_flags: ComputedValueFlags,\n\n    /// The style of the originating element in order to evaluate @container\n    /// size queries affecting pseudo-elements.\n    pub originating_element_style: Option<&'a ComputedValues>,\n}\n\nimpl ::selectors::SelectorImpl for SelectorImpl {\n    type PseudoElement = PseudoElement;\n    type NonTSPseudoClass = NonTSPseudoClass;\n\n    type ExtraMatchingData<'a> = ExtraMatchingData<'a>;\n    type AttrValue = AtomString;\n    type Identifier = AtomIdent;\n    type LocalName = LocalName;\n    type NamespacePrefix = Prefix;\n    type NamespaceUrl = Namespace;\n    type BorrowedLocalName = web_atoms::LocalName;\n    type BorrowedNamespaceUrl = web_atoms::Namespace;\n}\n\nimpl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {\n    type Impl = SelectorImpl;\n    type Error = StyleParseErrorKind<'i>;\n\n    #[inline]\n    fn parse_nth_child_of(&self) -> bool {\n        false\n    }\n\n    #[inline]\n    fn parse_is_and_where(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_has(&self) -> bool {\n        false\n    }\n\n    #[inline]\n    fn parse_parent_selector(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn parse_part(&self) -> bool {\n        true\n    }\n\n    #[inline]\n    fn allow_forgiving_selectors(&self) -> bool {\n        !self.for_supports_rule\n    }\n\n    fn parse_non_ts_pseudo_class(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<NonTSPseudoClass, ParseError<'i>> {\n        let pseudo_class = match_ignore_ascii_case! { &name,\n            \"active\" => NonTSPseudoClass::Active,\n            \"any-link\" => NonTSPseudoClass::AnyLink,\n            \"autofill\" => NonTSPseudoClass::Autofill,\n            \"checked\" => NonTSPseudoClass::Checked,\n            \"default\" => NonTSPseudoClass::Default,\n            \"defined\" => NonTSPseudoClass::Defined,\n            \"disabled\" => NonTSPseudoClass::Disabled,\n            \"enabled\" => NonTSPseudoClass::Enabled,\n            \"focus\" => NonTSPseudoClass::Focus,\n            \"focus-visible\" => NonTSPseudoClass::FocusVisible,\n            \"focus-within\" => NonTSPseudoClass::FocusWithin,\n            \"fullscreen\" => NonTSPseudoClass::Fullscreen,\n            \"hover\" => NonTSPseudoClass::Hover,\n            \"indeterminate\" => NonTSPseudoClass::Indeterminate,\n            \"invalid\" => NonTSPseudoClass::Invalid,\n            \"link\" => NonTSPseudoClass::Link,\n            \"modal\" => NonTSPseudoClass::Modal,\n            \"open\" => NonTSPseudoClass::Open,\n            \"optional\" => NonTSPseudoClass::Optional,\n            \"out-of-range\" => NonTSPseudoClass::OutOfRange,\n            \"placeholder-shown\" => NonTSPseudoClass::PlaceholderShown,\n            \"popover-open\" => NonTSPseudoClass::PopoverOpen,\n            \"read-only\" => NonTSPseudoClass::ReadOnly,\n            \"read-write\" => NonTSPseudoClass::ReadWrite,\n            \"required\" => NonTSPseudoClass::Required,\n            \"target\" => NonTSPseudoClass::Target,\n            \"user-invalid\" => NonTSPseudoClass::UserInvalid,\n            \"user-valid\" => NonTSPseudoClass::UserValid,\n            \"valid\" => NonTSPseudoClass::Valid,\n            \"visited\" => NonTSPseudoClass::Visited,\n            \"-moz-meter-optimum\" => NonTSPseudoClass::MozMeterOptimum,\n            \"-moz-meter-sub-optimum\" => NonTSPseudoClass::MozMeterSubOptimum,\n            \"-moz-meter-sub-sub-optimum\" => NonTSPseudoClass::MozMeterSubSubOptimum,\n            \"-servo-nonzero-border\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(\n                        SelectorParseErrorKind::UnexpectedIdent(\"-servo-nonzero-border\".into())\n                    ))\n                }\n                NonTSPseudoClass::ServoNonZeroBorder\n            },\n            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),\n        };\n\n        Ok(pseudo_class)\n    }\n\n    fn parse_non_ts_functional_pseudo_class<'t>(\n        &self,\n        name: CowRcStr<'i>,\n        parser: &mut CssParser<'i, 't>,\n        after_part: bool,\n    ) -> Result<NonTSPseudoClass, ParseError<'i>> {\n        let pseudo_class = match_ignore_ascii_case! { &name,\n            \"lang\" if !after_part => {\n                NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into())\n            },\n            \"state\" => {\n                let result = AtomIdent::from(parser.expect_ident()?.as_ref());\n                NonTSPseudoClass::CustomState(CustomState(result))\n            },\n            _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),\n        };\n\n        Ok(pseudo_class)\n    }\n\n    fn parse_pseudo_element(\n        &self,\n        location: SourceLocation,\n        name: CowRcStr<'i>,\n    ) -> Result<PseudoElement, ParseError<'i>> {\n        use self::PseudoElement::*;\n        let pseudo_element = match_ignore_ascii_case! { &name,\n            \"before\" => Before,\n            \"after\" => After,\n            \"backdrop\" => Backdrop,\n            \"selection\" => Selection,\n            \"file-selector-button\" => FileSelectorButton,\n            \"first-letter\" => FirstLetter,\n            \"marker\" => Marker,\n            \"details-content\" => DetailsContent,\n            \"color-swatch\" => ColorSwatch,\n            \"placeholder\" => Placeholder,\n            \"-servo-text-control-inner-container\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoTextControlInnerContainer\n            },\n            \"-servo-text-control-inner-editor\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoTextControlInnerEditor\n            },\n            \"slider-fill\" => SliderFill,\n            \"slider-thumb\" => SliderThumb,\n            \"slider-track\" => SliderTrack,\n            \"-servo-anonymous-box\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoAnonymousBox\n            },\n            \"-servo-anonymous-table\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoAnonymousTable\n            },\n            \"-servo-anonymous-table-row\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoAnonymousTableRow\n            },\n            \"-servo-anonymous-table-cell\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoAnonymousTableCell\n            },\n            \"-servo-table-grid\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoTableGrid\n            },\n            \"-servo-table-wrapper\" => {\n                if !self.in_user_agent_stylesheet() {\n                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n                }\n                ServoTableWrapper\n            },\n            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))\n\n        };\n\n        Ok(pseudo_element)\n    }\n\n    fn default_namespace(&self) -> Option<Namespace> {\n        self.namespaces.default.as_ref().map(|ns| ns.clone())\n    }\n\n    fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {\n        self.namespaces.prefixes.get(prefix).cloned()\n    }\n\n    fn parse_host(&self) -> bool {\n        true\n    }\n\n    fn parse_slotted(&self) -> bool {\n        true\n    }\n}\n\nimpl SelectorImpl {\n    /// A helper to traverse each eagerly cascaded pseudo-element, executing\n    /// `fun` on it.\n    #[inline]\n    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)\n    where\n        F: FnMut(PseudoElement),\n    {\n        for i in 0..EAGER_PSEUDO_COUNT {\n            fun(PseudoElement::from_eager_index(i));\n        }\n    }\n}\n\n/// A map from elements to snapshots for the Servo style back-end.\n#[derive(Debug)]\npub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);\n\nimpl SnapshotMap {\n    /// Create a new empty `SnapshotMap`.\n    pub fn new() -> Self {\n        SnapshotMap(FxHashMap::default())\n    }\n\n    /// Get a snapshot given an element.\n    pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {\n        self.0.get(&el.as_node().opaque())\n    }\n}\n\nimpl Deref for SnapshotMap {\n    type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl DerefMut for SnapshotMap {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.0\n    }\n}\n\n/// Servo's version of an element snapshot.\n#[derive(Debug, Default, MallocSizeOf)]\npub struct ServoElementSnapshot {\n    /// The stored state of the element.\n    pub state: Option<ElementState>,\n    /// The set of stored attributes and its values.\n    pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,\n    /// The set of changed attributes and its values.\n    pub changed_attrs: Vec<LocalName>,\n    /// Whether the class attribute changed or not.\n    pub class_changed: bool,\n    /// Whether the id attribute changed or not.\n    pub id_changed: bool,\n    /// Whether other attributes other than id or class changed or not.\n    pub other_attributes_changed: bool,\n}\n\nimpl ServoElementSnapshot {\n    /// Create an empty element snapshot.\n    pub fn new() -> Self {\n        Self::default()\n    }\n\n    /// Returns whether the id attribute changed or not.\n    pub fn id_changed(&self) -> bool {\n        self.id_changed\n    }\n\n    /// Returns whether the class attribute changed or not.\n    pub fn class_changed(&self) -> bool {\n        self.class_changed\n    }\n\n    /// Returns whether other attributes other than id or class changed or not.\n    pub fn other_attr_changed(&self) -> bool {\n        self.other_attributes_changed\n    }\n\n    fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {\n        self.attrs\n            .as_ref()\n            .unwrap()\n            .iter()\n            .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)\n            .map(|&(_, ref v)| v)\n    }\n\n    /// Executes the callback once for each attribute that changed.\n    #[inline]\n    pub fn each_attr_changed<F>(&self, mut callback: F)\n    where\n        F: FnMut(&LocalName),\n    {\n        for name in &self.changed_attrs {\n            callback(name)\n        }\n    }\n\n    fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool\n    where\n        F: FnMut(&AttrValue) -> bool,\n    {\n        self.attrs\n            .as_ref()\n            .unwrap()\n            .iter()\n            .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))\n    }\n}\n\nimpl ElementSnapshot for ServoElementSnapshot {\n    fn state(&self) -> Option<ElementState> {\n        self.state.clone()\n    }\n\n    fn has_attrs(&self) -> bool {\n        self.attrs.is_some()\n    }\n\n    fn id_attr(&self) -> Option<&Atom> {\n        self.get_attr(&ns!(), &local_name!(\"id\"))\n            .map(|v| v.as_atom())\n    }\n\n    fn is_part(&self, part_name: &AtomIdent) -> bool {\n        self.get_attr(&ns!(), &local_name!(\"part\"))\n            .is_some_and(|v| {\n                v.as_tokens()\n                    .iter()\n                    .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))\n            })\n    }\n\n    fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {\n        None\n    }\n\n    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {\n        self.get_attr(&ns!(), &local_name!(\"class\"))\n            .map_or(false, |v| {\n                v.as_tokens()\n                    .iter()\n                    .any(|atom| case_sensitivity.eq_atom(atom, name))\n            })\n    }\n\n    fn each_class<F>(&self, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        if let Some(v) = self.get_attr(&ns!(), &local_name!(\"class\")) {\n            for class in v.as_tokens() {\n                callback(AtomIdent::cast(class));\n            }\n        }\n    }\n\n    fn lang_attr(&self) -> Option<SelectorAttrValue> {\n        self.get_attr(&ns!(xml), &local_name!(\"lang\"))\n            .or_else(|| self.get_attr(&ns!(), &local_name!(\"lang\")))\n            .map(|v| SelectorAttrValue::from(v as &str))\n    }\n\n    /// Returns true if the snapshot has stored state for custom states\n    #[inline]\n    fn has_custom_states(&self) -> bool {\n        false\n    }\n\n    /// Returns true if the snapshot has a given CustomState\n    #[inline]\n    fn has_custom_state(&self, _state: &AtomIdent) -> bool {\n        false\n    }\n\n    #[inline]\n    fn each_custom_state<F>(&self, mut _callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n    }\n}\n\nimpl ServoElementSnapshot {\n    /// selectors::Element::attr_matches\n    pub fn attr_matches(\n        &self,\n        ns: &NamespaceConstraint<&Namespace>,\n        local_name: &LocalName,\n        operation: &AttrSelectorOperation<&AtomString>,\n    ) -> bool {\n        match *ns {\n            NamespaceConstraint::Specific(ref ns) => self\n                .get_attr(ns, local_name)\n                .map_or(false, |value| value.eval_selector(operation)),\n            NamespaceConstraint::Any => {\n                self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))\n            },\n        }\n    }\n}\n\n/// Returns whether the language is matched, as defined by\n/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).\npub fn extended_filtering(tag: &str, range: &str) -> bool {\n    range.split(',').any(|lang_range| {\n        // step 1\n        let mut range_subtags = lang_range.split('\\x2d');\n        let mut tag_subtags = tag.split('\\x2d');\n\n        // step 2\n        // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card\n        if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {\n            if !(range_subtag.eq_ignore_ascii_case(tag_subtag)\n                || range_subtag.eq_ignore_ascii_case(\"*\"))\n            {\n                return false;\n            }\n        }\n\n        let mut current_tag_subtag = tag_subtags.next();\n\n        // step 3\n        for range_subtag in range_subtags {\n            // step 3a\n            if range_subtag == \"*\" {\n                continue;\n            }\n            match current_tag_subtag.clone() {\n                Some(tag_subtag) => {\n                    // step 3c\n                    if range_subtag.eq_ignore_ascii_case(tag_subtag) {\n                        current_tag_subtag = tag_subtags.next();\n                        continue;\n                    }\n                    // step 3d\n                    if tag_subtag.len() == 1 {\n                        return false;\n                    }\n                    // else step 3e - continue with loop\n                    current_tag_subtag = tag_subtags.next();\n                    if current_tag_subtag.is_none() {\n                        return false;\n                    }\n                },\n                // step 3b\n                None => {\n                    return false;\n                },\n            }\n        }\n        // step 4\n        true\n    })\n}\n"
  },
  {
    "path": "style/servo/shadow_parts.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::derives::*;\nuse crate::values::AtomIdent;\nuse crate::Atom;\n\ntype Mapping<'a> = (&'a str, &'a str);\n\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct ShadowParts {\n    // FIXME: Consider a smarter data structure for this.\n    // Gecko uses a hashtable in both directions:\n    // https://searchfox.org/mozilla-central/rev/5d4178378f84c7130ccb8ac1723d33e380d7f7d7/layout/style/ShadowParts.h\n    mappings: Vec<(Atom, Atom)>,\n}\n\n/// <https://drafts.csswg.org/css-shadow-parts/#parsing-mapping>\n///\n/// Returns `None` in the failure case.\npub fn parse_part_mapping(input: &str) -> Option<Mapping<'_>> {\n    // Step 1. Let input be the string being parsed.\n    // Step 2. Let position be a pointer into input, initially pointing at the start of the string.\n    // NOTE: We don't need an explicit position, we just drop stuff from the input\n\n    // Step 3. Collect a sequence of code points that are space characters\n    let input = input.trim_start_matches(|c| c == ' ');\n\n    // Step 4. Collect a sequence of code points that are not space characters or U+003A COLON characters,\n    // and let first token be the result.\n    let space_or_colon_position = input\n        .char_indices()\n        .find(|(_, c)| matches!(c, ' ' | ':'))\n        .map(|(index, _)| index)\n        .unwrap_or(input.len());\n    let (first_token, input) = input.split_at(space_or_colon_position);\n\n    // Step 5. If first token is empty then return error.\n    if first_token.is_empty() {\n        return None;\n    }\n\n    // Step 6. Collect a sequence of code points that are space characters.\n    let input = input.trim_start_matches(|c| c == ' ');\n\n    // Step 7. If the end of the input has been reached, return the tuple (first token, first token)\n    if input.is_empty() {\n        return Some((first_token, first_token));\n    }\n\n    // Step 8. If character at position is not a U+003A COLON character, return error.\n    // Step 9. Consume the U+003A COLON character.\n    let Some(input) = input.strip_prefix(':') else {\n        return None;\n    };\n\n    // Step 10. Collect a sequence of code points that are space characters.\n    let input = input.trim_start_matches(|c| c == ' ');\n\n    // Step 11. Collect a sequence of code points that are not space characters or U+003A COLON characters.\n    // and let second token be the result.\n    let space_or_colon_position = input\n        .char_indices()\n        .find(|(_, c)| matches!(c, ' ' | ':'))\n        .map(|(index, _)| index)\n        .unwrap_or(input.len());\n    let (second_token, input) = input.split_at(space_or_colon_position);\n\n    // Step 12. If second token is empty then return error.\n    if second_token.is_empty() {\n        return None;\n    }\n\n    // Step 13. Collect a sequence of code points that are space characters.\n    let input = input.trim_start_matches(|c| c == ' ');\n\n    // Step 14. If position is not past the end of input then return error.\n    if !input.is_empty() {\n        return None;\n    }\n\n    // Step 14. Return the tuple (first token, second token).\n    Some((first_token, second_token))\n}\n\n/// <https://drafts.csswg.org/css-shadow-parts/#parsing-mapping-list>\nfn parse_mapping_list(input: &str) -> impl Iterator<Item = Mapping<'_>> {\n    // Step 1. Let input be the string being parsed.\n    // Step 2. Split the string input on commas. Let unparsed mappings be the resulting list of strings.\n    let unparsed_mappings = input.split(',');\n\n    // Step 3. Let mappings be an initially empty list of tuples of tokens.\n    // This list will be the result of this algorithm.\n    // NOTE: We return an iterator here - it is up to the caller to turn it into a list\n\n    // Step 4. For each string unparsed mapping in unparsed mappings, run the following substeps:\n    unparsed_mappings.filter_map(|unparsed_mapping| {\n        // Step 4.1 If unparsed mapping is empty or contains only space characters,\n        // continue to the next iteration of the loop.\n        if unparsed_mapping.chars().all(|c| c == ' ') {\n            return None;\n        }\n\n        // Step 4.2 Let mapping be the result of parsing unparsed mapping using the rules for parsing part mappings.\n        // Step 4.3 If mapping is an error then continue to the next iteration of the loop.\n        // This allows clients to skip over new syntax that is not understood.\n        // Step 4.4 Append mapping to mappings.\n        parse_part_mapping(unparsed_mapping)\n    })\n}\n\nimpl ShadowParts {\n    pub fn parse(input: &str) -> Self {\n        Self {\n            mappings: parse_mapping_list(input)\n                .map(|(first, second)| (first.into(), second.into()))\n                .collect(),\n        }\n    }\n\n    /// Call the provided callback for each exported part with the given name.\n    pub fn for_each_exported_part<F>(&self, name: &Atom, mut callback: F)\n    where\n        F: FnMut(&AtomIdent),\n    {\n        for (from, to) in &self.mappings {\n            if from == name {\n                callback(AtomIdent::cast(to));\n            }\n        }\n    }\n\n    pub fn imported_part(&self, name: &Atom) -> Option<&Atom> {\n        self.mappings\n            .iter()\n            .find(|(_, to)| to == name)\n            .map(|(from, _)| from)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn parse_valid_mapping() {\n        assert_eq!(\n            parse_part_mapping(\"foo\"),\n            Some((\"foo\", \"foo\")),\n            \"Single token\"\n        );\n        assert_eq!(\n            parse_part_mapping(\"  foo\"),\n            Some((\"foo\", \"foo\")),\n            \"Single token with leading whitespace\"\n        );\n        assert_eq!(\n            parse_part_mapping(\"foo \"),\n            Some((\"foo\", \"foo\")),\n            \"Single token with trailing whitespace\"\n        );\n        assert_eq!(\n            parse_part_mapping(\"foo:bar\"),\n            Some((\"foo\", \"bar\")),\n            \"Two tokens\"\n        );\n        assert_eq!(\n            parse_part_mapping(\"foo:bar \"),\n            Some((\"foo\", \"bar\")),\n            \"Two tokens with trailing whitespace\"\n        );\n        assert_eq!(\n            parse_part_mapping(\"🦀:🚀\"),\n            Some((\"🦀\", \"🚀\")),\n            \"Two tokens consisting of non-ascii characters\"\n        );\n    }\n\n    #[test]\n    fn reject_invalid_mapping() {\n        assert!(parse_part_mapping(\"\").is_none(), \"Empty input\");\n        assert!(parse_part_mapping(\"    \").is_none(), \"Only whitespace\");\n        assert!(parse_part_mapping(\"foo bar\").is_none(), \"Missing colon\");\n        assert!(parse_part_mapping(\":bar\").is_none(), \"Empty first token\");\n        assert!(parse_part_mapping(\"foo:\").is_none(), \"Empty second token\");\n        assert!(\n            parse_part_mapping(\"foo:bar baz\").is_none(),\n            \"Trailing input\"\n        );\n    }\n\n    #[test]\n    fn parse_valid_mapping_list() {\n        let mut mappings = parse_mapping_list(\"foo: bar, totally-invalid-mapping,,\");\n\n        // \"foo: bar\" is a valid mapping\n        assert_eq!(\n            mappings.next(),\n            Some((\"foo\", \"bar\")),\n            \"First mapping should be in the list\"\n        );\n        // \"totally-invalid-mapping\" is not a valid mapping and should be ignored\n        // \"\" is not valid (and consists of nothing but whitespace), so it should be ignored\n        assert!(mappings.next().is_none(), \"No more mappings should exist\");\n    }\n}\n"
  },
  {
    "path": "style/servo/url.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common handling for the specified value CSS url() values.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::stylesheets::CorsMode;\nuse crate::values::computed::{Context, ToComputedValue};\nuse cssparser::{Parser, SourceLocation};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse std::ops::Deref;\nuse style_traits::{CssWriter, ParseError, ToCss};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\nuse url::Url;\n\n/// A CSS url() value for servo.\n///\n/// Servo eagerly resolves SpecifiedUrls, which it can then take advantage of\n/// when computing values. In contrast, Gecko uses a different URL backend, so\n/// eagerly resolving with rust-url would be duplicated work.\n///\n/// However, this approach is still not necessarily optimal: See\n/// <https://bugzilla.mozilla.org/show_bug.cgi?id=1347435#c6>\n#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]\n#[css(function = \"url\")]\n#[repr(C)]\npub struct CssUrl(#[ignore_malloc_size_of = \"Arc\"] pub Arc<CssUrlData>);\n\n/// Data shared between CssUrls.\n///\n#[derive(Debug, Deserialize, MallocSizeOf, Serialize, SpecifiedValueInfo)]\n#[repr(C)]\npub struct CssUrlData {\n    /// The original URI. This might be optional since we may insert computed\n    /// values of images into the cascade directly, and we don't bother to\n    /// convert their serialization.\n    ///\n    /// Refcounted since cloning this should be cheap and data: uris can be\n    /// really large.\n    #[ignore_malloc_size_of = \"Arc\"]\n    original: Option<Arc<String>>,\n\n    /// The resolved value for the url, if valid.\n    #[ignore_malloc_size_of = \"Arc\"]\n    resolved: Option<Arc<Url>>,\n}\n\nimpl ToShmem for CssUrl {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        unimplemented!(\"If servo wants to share stylesheets across processes, ToShmem for Url must be implemented\");\n    }\n}\n\nimpl Deref for CssUrl {\n    type Target = CssUrlData;\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl CssUrl {\n    /// Parse a URL from a string value that is a valid CSS token for a URL,\n    /// enforcing attr()-tainting constraints if applicable.\n    /// https://drafts.csswg.org/css-values-5/#attr-security\n    pub fn parse_from_string<'i>(\n        url: String,\n        url_start: usize,\n        url_end: usize,\n        context: &ParserContext,\n        cors_mode: CorsMode,\n        location: SourceLocation,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::custom_properties::AttrTaintedRange;\n        use style_traits::StyleParseErrorKind;\n        let range = AttrTaintedRange::new(url_start, url_end);\n        if context.disallow_urls_in_range(&range) {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(Self::new_from_string(url, context, cors_mode))\n    }\n\n    /// Create a new CSS URL that is attr()-untainted given a valid CSS token for a URL.\n    /// Be cautious when calling `expect_url()` to not bypass attr()-tainting checks. If\n    /// it's possible attr()'s were substituted into the `url`, DO NOT use this method.\n    /// https://drafts.csswg.org/css-values-5/#attr-security\n    pub fn new_from_untainted_string(\n        url: String,\n        context: &ParserContext,\n        cors_mode: CorsMode,\n    ) -> Self {\n        debug_assert!(context.attr_tainted_regions.is_empty());\n        Self::new_from_string(url, context, cors_mode)\n    }\n\n    /// Try to parse a URL from a string value that is a valid CSS token for a\n    /// URL.\n    ///\n    /// FIXME(emilio): Should honor CorsMode.\n    fn new_from_string(url: String, context: &ParserContext, _cors_mode: CorsMode) -> Self {\n        let serialization = Arc::new(url);\n        // Per https://drafts.csswg.org/css-values-4/#url-empty\n        // If the original url is empty, then the resolved url is considered invalid.\n        let resolved = (!serialization.is_empty())\n            .then(|| context.url_data.0.join(&serialization))\n            .and_then(Result::ok)\n            .map(Arc::new);\n        CssUrl(Arc::new(CssUrlData {\n            original: Some(serialization),\n            resolved: resolved,\n        }))\n    }\n\n    /// Returns true if the URL is definitely invalid. For Servo URLs, we can\n    /// use its |resolved| status.\n    pub fn is_invalid(&self) -> bool {\n        self.resolved.is_none()\n    }\n\n    /// Returns true if this URL looks like a fragment.\n    /// See https://drafts.csswg.org/css-values/#local-urls\n    ///\n    /// Since Servo currently stores resolved URLs, this is hard to implement. We\n    /// either need to change servo to lazily resolve (like Gecko), or note this\n    /// information in the tokenizer.\n    pub fn is_fragment(&self) -> bool {\n        error!(\"Can't determine whether the url is a fragment.\");\n        false\n    }\n\n    /// Returns the resolved url if it was valid.\n    pub fn url(&self) -> Option<&Arc<Url>> {\n        self.resolved.as_ref()\n    }\n\n    /// Return the resolved url as string, or the empty string if it's invalid.\n    ///\n    /// TODO(emilio): Should we return the original one if needed?\n    pub fn as_str(&self) -> &str {\n        match self.resolved {\n            Some(ref url) => url.as_str(),\n            None => \"\",\n        }\n    }\n\n    /// Creates an already specified url value from an already resolved URL\n    /// for insertion in the cascade.\n    pub fn for_cascade(url: Arc<::url::Url>) -> Self {\n        CssUrl(Arc::new(CssUrlData {\n            original: None,\n            resolved: Some(url),\n        }))\n    }\n\n    /// Gets a new url from a string for unit tests.\n    pub fn new_for_testing(url: &str) -> Self {\n        CssUrl(Arc::new(CssUrlData {\n            original: Some(Arc::new(url.into())),\n            resolved: (!url.is_empty())\n                .then(|| ::url::Url::parse(url))\n                .and_then(Result::ok)\n                .map(Arc::new),\n        }))\n    }\n\n    /// Parses a URL request and records that the corresponding request needs to\n    /// be CORS-enabled.\n    ///\n    /// This is only for shape images and masks in Gecko, thus unimplemented for\n    /// now so somebody notices when trying to do so.\n    pub fn parse_with_cors_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n    ) -> Result<Self, ParseError<'i>> {\n        let start = input.position().byte_index();\n        let location = input.current_source_location();\n        let url = input.expect_url()?;\n        let end = input.position().byte_index();\n        Self::parse_from_string(\n            url.as_ref().to_owned(),\n            start,\n            end,\n            context,\n            cors_mode,\n            location,\n        )\n    }\n}\n\nimpl Parse for CssUrl {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_cors_mode(context, input, CorsMode::None)\n    }\n}\n\nimpl PartialEq for CssUrl {\n    fn eq(&self, other: &Self) -> bool {\n        // TODO(emilio): maybe we care about equality of the specified values if\n        // present? Seems not.\n        self.resolved == other.resolved\n    }\n}\n\nimpl Eq for CssUrl {}\n\nimpl ToCss for CssUrl {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let string = match self.0.original {\n            Some(ref original) => &**original,\n            None => match self.resolved {\n                Some(ref url) => url.as_str(),\n                // This can only happen if the url wasn't specified by the\n                // user *and* it's an invalid url that has been transformed\n                // back to specified value via the \"uncompute\" functionality.\n                None => \"about:invalid\",\n            },\n        };\n\n        dest.write_str(\"url(\")?;\n        string.to_css(dest)?;\n        dest.write_char(')')\n    }\n}\n\n/// A specified url() value for servo.\npub type SpecifiedUrl = CssUrl;\n\nimpl ToComputedValue for SpecifiedUrl {\n    type ComputedValue = ComputedUrl;\n\n    // If we can't resolve the URL from the specified one, we fall back to the original\n    // but still return it as a ComputedUrl::Invalid\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        match self.resolved {\n            Some(ref url) => ComputedUrl::Valid(url.clone()),\n            None => match self.original {\n                Some(ref url) => ComputedUrl::Invalid(url.clone()),\n                None => {\n                    unreachable!(\"Found specified url with neither resolved or original URI!\");\n                },\n            },\n        }\n    }\n\n    fn from_computed_value(computed: &ComputedUrl) -> Self {\n        let data = match *computed {\n            ComputedUrl::Valid(ref url) => CssUrlData {\n                original: None,\n                resolved: Some(url.clone()),\n            },\n            ComputedUrl::Invalid(ref url) => CssUrlData {\n                original: Some(url.clone()),\n                resolved: None,\n            },\n        };\n        CssUrl(Arc::new(data))\n    }\n}\n\n/// The computed value of a CSS `url()`, resolved relative to the stylesheet URL.\n#[derive(Clone, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]\npub enum ComputedUrl {\n    /// The `url()` was invalid or it wasn't specified by the user.\n    Invalid(#[ignore_malloc_size_of = \"Arc\"] Arc<String>),\n    /// The resolved `url()` relative to the stylesheet URL.\n    Valid(#[ignore_malloc_size_of = \"Arc\"] Arc<Url>),\n}\n\nimpl ComputedUrl {\n    /// Returns the resolved url if it was valid.\n    pub fn url(&self) -> Option<&Arc<Url>> {\n        match *self {\n            ComputedUrl::Valid(ref url) => Some(url),\n            _ => None,\n        }\n    }\n}\n\nimpl ToCss for ComputedUrl {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let string = match *self {\n            ComputedUrl::Valid(ref url) => url.as_str(),\n            ComputedUrl::Invalid(ref invalid_string) => invalid_string,\n        };\n\n        dest.write_str(\"url(\")?;\n        string.to_css(dest)?;\n        dest.write_char(')')\n    }\n}\n"
  },
  {
    "path": "style/shared_lock.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Different objects protected by the same lock\n\nuse crate::stylesheets::Origin;\n#[cfg(feature = \"gecko\")]\nuse atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};\n#[cfg(feature = \"servo\")]\nuse parking_lot::RwLock;\nuse servo_arc::Arc;\nuse std::cell::UnsafeCell;\nuse std::fmt;\n#[cfg(feature = \"servo\")]\nuse std::mem;\n#[cfg(feature = \"gecko\")]\nuse std::ptr;\nuse style_traits::{CssString, CssStringWriter};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\n/// A shared read/write lock that can protect multiple objects.\n///\n/// In Gecko builds, we don't need the blocking behavior, just the safety. As\n/// such we implement this with an AtomicRefCell instead in Gecko builds,\n/// which is ~2x as fast, and panics (rather than deadlocking) when things go\n/// wrong (which is much easier to debug on CI).\n///\n/// Servo needs the blocking behavior for its unsynchronized animation setup,\n/// but that may not be web-compatible and may need to be changed (at which\n/// point Servo could use AtomicRefCell too).\n///\n/// Gecko also needs the ability to have \"read only\" SharedRwLocks, which are\n/// used for objects stored in (read only) shared memory. Attempting to acquire\n/// write access to objects protected by a read only SharedRwLock will panic.\n#[derive(Clone)]\n#[cfg_attr(feature = \"servo\", derive(crate::derives::MallocSizeOf))]\npub struct SharedRwLock {\n    #[cfg(feature = \"servo\")]\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n    arc: Arc<RwLock<()>>,\n\n    #[cfg(feature = \"gecko\")]\n    cell: Option<Arc<AtomicRefCell<SomethingZeroSizedButTyped>>>,\n}\n\n#[cfg(feature = \"gecko\")]\nstruct SomethingZeroSizedButTyped;\n\nimpl fmt::Debug for SharedRwLock {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.write_str(\"SharedRwLock\")\n    }\n}\n\nimpl SharedRwLock {\n    /// Create a new shared lock (servo).\n    #[cfg(feature = \"servo\")]\n    pub fn new() -> Self {\n        SharedRwLock {\n            arc: Arc::new(RwLock::new(())),\n        }\n    }\n\n    /// Create a new shared lock (gecko).\n    #[cfg(feature = \"gecko\")]\n    pub fn new() -> Self {\n        SharedRwLock {\n            cell: Some(Arc::new(AtomicRefCell::new(SomethingZeroSizedButTyped))),\n        }\n    }\n\n    /// Create a new global shared lock (servo).\n    #[cfg(feature = \"servo\")]\n    pub fn new_leaked() -> Self {\n        SharedRwLock {\n            arc: Arc::new_leaked(RwLock::new(())),\n        }\n    }\n\n    /// Create a new global shared lock (gecko).\n    #[cfg(feature = \"gecko\")]\n    pub fn new_leaked() -> Self {\n        SharedRwLock {\n            cell: Some(Arc::new_leaked(AtomicRefCell::new(\n                SomethingZeroSizedButTyped,\n            ))),\n        }\n    }\n\n    /// Create a new read-only shared lock (gecko).\n    #[cfg(feature = \"gecko\")]\n    pub fn read_only() -> Self {\n        SharedRwLock { cell: None }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    fn ptr(&self) -> *const SomethingZeroSizedButTyped {\n        self.cell\n            .as_ref()\n            .map(|cell| cell.as_ptr() as *const _)\n            .unwrap_or(ptr::null())\n    }\n\n    /// Wrap the given data to make its access protected by this lock.\n    pub fn wrap<T>(&self, data: T) -> Locked<T> {\n        Locked {\n            shared_lock: self.clone(),\n            data: UnsafeCell::new(data),\n        }\n    }\n\n    /// Obtain the lock for reading (servo).\n    #[cfg(feature = \"servo\")]\n    pub fn read(&self) -> SharedRwLockReadGuard<'_> {\n        mem::forget(self.arc.read());\n        SharedRwLockReadGuard(self)\n    }\n\n    /// Obtain the lock for reading (gecko).\n    #[cfg(feature = \"gecko\")]\n    pub fn read(&self) -> SharedRwLockReadGuard<'_> {\n        SharedRwLockReadGuard(self.cell.as_ref().map(|cell| cell.borrow()))\n    }\n\n    /// Obtain the lock for writing (servo).\n    #[cfg(feature = \"servo\")]\n    pub fn write(&self) -> SharedRwLockWriteGuard<'_> {\n        mem::forget(self.arc.write());\n        SharedRwLockWriteGuard(self)\n    }\n\n    /// Obtain the lock for writing (gecko).\n    #[cfg(feature = \"gecko\")]\n    pub fn write(&self) -> SharedRwLockWriteGuard<'_> {\n        SharedRwLockWriteGuard(self.cell.as_ref().unwrap().borrow_mut())\n    }\n}\n\n/// Proof that a shared lock was obtained for reading (servo).\n#[cfg(feature = \"servo\")]\npub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock);\n/// Proof that a shared lock was obtained for reading (gecko).\n#[cfg(feature = \"gecko\")]\npub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>);\n#[cfg(feature = \"servo\")]\nimpl<'a> Drop for SharedRwLockReadGuard<'a> {\n    fn drop(&mut self) {\n        // Unsafe: self.lock is private to this module, only ever set after `read()`,\n        // and never copied or cloned (see `compile_time_assert` below).\n        unsafe { self.0.arc.force_unlock_read() }\n    }\n}\n\nimpl<'a> SharedRwLockReadGuard<'a> {\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    fn ptr(&self) -> *const SomethingZeroSizedButTyped {\n        self.0\n            .as_ref()\n            .map(|r| &**r as *const _)\n            .unwrap_or(ptr::null())\n    }\n}\n\n/// Proof that a shared lock was obtained for writing (servo).\n#[cfg(feature = \"servo\")]\npub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock);\n/// Proof that a shared lock was obtained for writing (gecko).\n#[cfg(feature = \"gecko\")]\npub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>);\n#[cfg(feature = \"servo\")]\nimpl<'a> Drop for SharedRwLockWriteGuard<'a> {\n    fn drop(&mut self) {\n        // Unsafe: self.lock is private to this module, only ever set after `write()`,\n        // and never copied or cloned (see `compile_time_assert` below).\n        unsafe { self.0.arc.force_unlock_write() }\n    }\n}\n\n/// Data protect by a shared lock.\npub struct Locked<T> {\n    shared_lock: SharedRwLock,\n    data: UnsafeCell<T>,\n}\n\n// Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`,\n// where guards ensure synchronization.\nunsafe impl<T: Send> Send for Locked<T> {}\nunsafe impl<T: Send + Sync> Sync for Locked<T> {}\n\nimpl<T: fmt::Debug> fmt::Debug for Locked<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let guard = self.shared_lock.read();\n        self.read_with(&guard).fmt(f)\n    }\n}\n\nimpl<T> Locked<T> {\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    fn is_read_only_lock(&self) -> bool {\n        self.shared_lock.cell.is_none()\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn same_lock_as(&self, lock: &SharedRwLock) -> bool {\n        Arc::ptr_eq(&self.shared_lock.arc, &lock.arc)\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn same_lock_as(&self, ptr: *const SomethingZeroSizedButTyped) -> bool {\n        ptr::eq(self.shared_lock.ptr(), ptr)\n    }\n\n    /// Access the data for reading.\n    pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T {\n        #[cfg(feature = \"gecko\")]\n        assert!(\n            self.is_read_only_lock() || self.same_lock_as(guard.ptr()),\n            \"Locked::read_with called with a guard from an unrelated SharedRwLock: {:?} vs. {:?}\",\n            self.shared_lock.ptr(),\n            guard.ptr(),\n        );\n        #[cfg(not(feature = \"gecko\"))]\n        assert!(self.same_lock_as(&guard.0));\n\n        let ptr = self.data.get();\n\n        // Unsafe:\n        //\n        // * The guard guarantees that the lock is taken for reading,\n        //   and we’ve checked that it’s the correct lock.\n        // * The returned reference borrows *both* the data and the guard,\n        //   so that it can outlive neither.\n        unsafe { &*ptr }\n    }\n\n    /// Access the data for reading without verifying the lock. Use with caution.\n    #[cfg(feature = \"gecko\")]\n    pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T {\n        let ptr = self.data.get();\n        &*ptr\n    }\n\n    /// Access the data for writing.\n    pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T {\n        #[cfg(feature = \"gecko\")]\n        assert!(\n            !self.is_read_only_lock() && self.same_lock_as(&*guard.0),\n            \"Locked::write_with called with a guard from a read only or unrelated SharedRwLock\"\n        );\n        #[cfg(not(feature = \"gecko\"))]\n        assert!(self.same_lock_as(&guard.0));\n\n        let ptr = self.data.get();\n\n        // Unsafe:\n        //\n        // * The guard guarantees that the lock is taken for writing,\n        //   and we’ve checked that it’s the correct lock.\n        // * The returned reference borrows *both* the data and the guard,\n        //   so that it can outlive neither.\n        // * We require a mutable borrow of the guard,\n        //   so that one write guard can only be used once at a time.\n        unsafe { &mut *ptr }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl<T: ToShmem> ToShmem for Locked<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        use std::mem::ManuallyDrop;\n\n        let guard = self.shared_lock.read();\n        Ok(ManuallyDrop::new(Locked {\n            shared_lock: SharedRwLock::read_only(),\n            data: UnsafeCell::new(ManuallyDrop::into_inner(\n                self.read_with(&guard).to_shmem(builder)?,\n            )),\n        }))\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<T: ToShmem> ToShmem for Locked<T> {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        panic!(\"ToShmem not supported in Servo currently\")\n    }\n}\n\n#[allow(dead_code)]\nmod compile_time_assert {\n    use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard};\n\n    trait Marker1 {}\n    impl<T: Clone> Marker1 for T {}\n    impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone\n    impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone\n\n    trait Marker2 {}\n    impl<T: Copy> Marker2 for T {}\n    impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy\n    impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy\n}\n\n/// Like ToCss, but with a lock guard given by the caller, and with the writer specified\n/// concretely rather than with a parameter.\npub trait ToCssWithGuard {\n    /// Serialize `self` in CSS syntax, writing to `dest`, using the given lock guard.\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result;\n\n    /// Serialize `self` in CSS syntax using the given lock guard and return a string.\n    ///\n    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)\n    #[inline]\n    fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> CssString {\n        let mut s = CssString::new();\n        self.to_css(guard, &mut s).unwrap();\n        s\n    }\n}\n\n/// A trait to do a deep clone of a given CSS type. Gets a lock and a read\n/// guard, in order to be able to read and clone nested structures.\npub trait DeepCloneWithLock: Sized {\n    /// Deep clones this object.\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self;\n}\n\n/// Guards for a document\n#[derive(Clone)]\npub struct StylesheetGuards<'a> {\n    /// For author-origin stylesheets.\n    pub author: &'a SharedRwLockReadGuard<'a>,\n\n    /// For user-agent-origin and user-origin stylesheets\n    pub ua_or_user: &'a SharedRwLockReadGuard<'a>,\n}\n\nimpl<'a> StylesheetGuards<'a> {\n    /// Get the guard for a given stylesheet origin.\n    pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> {\n        match origin {\n            Origin::Author => &self.author,\n            _ => &self.ua_or_user,\n        }\n    }\n\n    /// Same guard for all origins\n    pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self {\n        StylesheetGuards {\n            author: guard,\n            ua_or_user: guard,\n        }\n    }\n}\n"
  },
  {
    "path": "style/sharing/checks.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Different checks done during the style sharing process in order to determine\n//! quickly whether it's worth to share style, and whether two different\n//! elements can indeed share the same style.\n\nuse crate::bloom::StyleBloom;\nuse crate::context::SharedStyleContext;\nuse crate::dom::TElement;\nuse crate::sharing::{StyleSharingCandidate, StyleSharingTarget};\nuse selectors::matching::SelectorCaches;\n\n/// Determines whether a target and a candidate have compatible parents for\n/// sharing.\npub fn parents_allow_sharing<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n) -> bool\nwhere\n    E: TElement,\n{\n    // If the identity of the parent style isn't equal, we can't share. We check\n    // this first, because the result is cached.\n    if target.parent_style_identity() != candidate.parent_style_identity() {\n        return false;\n    }\n\n    // Siblings can always share.\n    let parent = target.inheritance_parent().unwrap();\n    let candidate_parent = candidate.element.inheritance_parent().unwrap();\n    if parent == candidate_parent {\n        return true;\n    }\n\n    // If a parent element was already styled and we traversed past it without\n    // restyling it, that may be because our clever invalidation logic was able\n    // to prove that the styles of that element would remain unchanged despite\n    // changes to the id or class attributes. However, style sharing relies in\n    // the strong guarantee that all the classes and ids up the respective parent\n    // chains are identical. As such, if we skipped styling for one (or both) of\n    // the parents on this traversal, we can't share styles across cousins.\n    //\n    // This is a somewhat conservative check. We could tighten it by having the\n    // invalidation logic explicitly flag elements for which it ellided styling.\n    let parent_data = parent.borrow_data().unwrap();\n    let candidate_parent_data = candidate_parent.borrow_data().unwrap();\n    if !parent_data.safe_for_cousin_sharing() || !candidate_parent_data.safe_for_cousin_sharing() {\n        return false;\n    }\n\n    true\n}\n\n/// Whether two elements have the same style attribute.\n///\n/// First checks pointer identity (fast path), then falls back to value comparison.\npub fn have_same_style_attribute<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n    shared_context: &SharedStyleContext,\n) -> bool\nwhere\n    E: TElement,\n{\n    match (target.style_attribute(), candidate.style_attribute()) {\n        (None, None) => true,\n        (Some(_), None) | (None, Some(_)) => false,\n        (Some(a), Some(b)) => {\n            if std::ptr::eq(&*a, &*b) {\n                return true;\n            }\n            let guard = shared_context.guards.author;\n            *a.read_with(guard) == *b.read_with(guard)\n        },\n    }\n}\n\n/// Whether two elements have the same same presentational attributes.\npub fn have_same_presentational_hints<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n) -> bool\nwhere\n    E: TElement,\n{\n    target.pres_hints() == candidate.pres_hints()\n}\n\n/// Whether a given element has the same class attribute as a given candidate.\n///\n/// We don't try to share style across elements with different class attributes.\npub fn have_same_class<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n) -> bool\nwhere\n    E: TElement,\n{\n    target.class_list() == candidate.class_list()\n}\n\n/// Whether a given element has the same part attribute as a given candidate.\n///\n/// We don't try to share style across elements with different part attributes.\npub fn have_same_parts<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n) -> bool\nwhere\n    E: TElement,\n{\n    target.part_list() == candidate.part_list()\n}\n\n/// Whether a given element and a candidate match the same set of \"revalidation\"\n/// selectors.\n///\n/// Revalidation selectors are those that depend on the DOM structure, like\n/// :first-child, etc, or on attributes that we don't check off-hand (pretty\n/// much every attribute selector except `id` and `class`.\n#[inline]\npub fn revalidate<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n    shared_context: &SharedStyleContext,\n    bloom: &StyleBloom<E>,\n    selector_caches: &mut SelectorCaches,\n) -> bool\nwhere\n    E: TElement,\n{\n    let stylist = &shared_context.stylist;\n\n    let for_element = target.revalidation_match_results(stylist, bloom, selector_caches);\n\n    let for_candidate = candidate.revalidation_match_results(stylist, bloom, selector_caches);\n\n    for_element == for_candidate\n}\n\n/// Whether the given element and a candidate have the same values for the the\n/// attributes used in an `attr()` function.\n#[inline]\npub fn have_same_referenced_attrs<E>(\n    target: &StyleSharingTarget<E>,\n    candidate: &StyleSharingCandidate<E>,\n) -> bool\nwhere\n    E: TElement,\n{\n    // The candidate must be styled in order to be in the cache.\n    let borrowed_data = candidate.element.borrow_data().unwrap();\n    let attrs_used = borrowed_data.styles.primary().attribute_references.as_ref();\n\n    let Some(attrs_used) = attrs_used else {\n        return true;\n    };\n\n    attrs_used.iter().all(|(name, namespaces)| {\n        namespaces.iter().all(|namespace| {\n            target.get_attr(name, namespace) == candidate.get_attr(name, namespace)\n        })\n    })\n}\n\n/// Whether a given element and a candidate share a set of scope activations\n/// for revalidation.\n#[inline]\npub fn revalidate_scope<E>(\n    target: &mut StyleSharingTarget<E>,\n    candidate: &mut StyleSharingCandidate<E>,\n    shared_context: &SharedStyleContext,\n    selector_caches: &mut SelectorCaches,\n) -> bool\nwhere\n    E: TElement,\n{\n    let stylist = &shared_context.stylist;\n    let for_element = target.scope_revalidation_results(stylist, selector_caches);\n    let for_candidate = candidate.scope_revalidation_results(stylist, selector_caches);\n\n    for_element == for_candidate\n}\n\n/// Checks whether we might have rules for either of the two ids.\n#[inline]\npub fn may_match_different_id_rules<E>(\n    shared_context: &SharedStyleContext,\n    element: E,\n    candidate: E,\n) -> bool\nwhere\n    E: TElement,\n{\n    let element_id = element.id();\n    let candidate_id = candidate.id();\n\n    if element_id == candidate_id {\n        return false;\n    }\n\n    let stylist = &shared_context.stylist;\n\n    let may_have_rules_for_element = match element_id {\n        Some(id) => stylist.may_have_rules_for_id(id, element),\n        None => false,\n    };\n\n    if may_have_rules_for_element {\n        return true;\n    }\n\n    match candidate_id {\n        Some(id) => stylist.may_have_rules_for_id(id, candidate),\n        None => false,\n    }\n}\n"
  },
  {
    "path": "style/sharing/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Code related to the style sharing cache, an optimization that allows similar\n//! nodes to share style without having to run selector matching twice.\n//!\n//! The basic setup is as follows.  We have an LRU cache of style sharing\n//! candidates.  When we try to style a target element, we first check whether\n//! we can quickly determine that styles match something in this cache, and if\n//! so we just use the cached style information.  This check is done with a\n//! StyleBloom filter set up for the target element, which may not be a correct\n//! state for the cached candidate element if they're cousins instead of\n//! siblings.\n//!\n//! The complicated part is determining that styles match.  This is subject to\n//! the following constraints:\n//!\n//! 1) The target and candidate must be inheriting the same styles.\n//! 2) The target and candidate must have exactly the same rules matching them.\n//! 3) The target and candidate must have exactly the same non-selector-based\n//!    style information (inline styles, presentation hints).\n//! 4) The target and candidate must have exactly the same rules matching their\n//!    pseudo-elements, because an element's style data points to the style\n//!    data for its pseudo-elements.\n//!\n//! These constraints are satisfied in the following ways:\n//!\n//! * We check that the parents of the target and the candidate have the same\n//!   computed style.  This addresses constraint 1.\n//!\n//! * We check that the target and candidate have the same inline style and\n//!   presentation hint declarations.  This addresses constraint 3.\n//!\n//! * We ensure that a target matches a candidate only if they have the same\n//!   matching result for all selectors that target either elements or the\n//!   originating elements of pseudo-elements.  This addresses constraint 4\n//!   (because it prevents a target that has pseudo-element styles from matching\n//!   a candidate that has different pseudo-element styles) as well as\n//!   constraint 2.\n//!\n//! The actual checks that ensure that elements match the same rules are\n//! conceptually split up into two pieces.  First, we do various checks on\n//! elements that make sure that the set of possible rules in all selector maps\n//! in the stylist (for normal styling and for pseudo-elements) that might match\n//! the two elements is the same.  For example, we enforce that the target and\n//! candidate must have the same localname and namespace.  Second, we have a\n//! selector map of \"revalidation selectors\" that the stylist maintains that we\n//! actually match against the target and candidate and then check whether the\n//! two sets of results were the same.  Due to the up-front selector map checks,\n//! we know that the target and candidate will be matched against the same exact\n//! set of revalidation selectors, so the match result arrays can be compared\n//! directly.\n//!\n//! It's very important that a selector be added to the set of revalidation\n//! selectors any time there are two elements that could pass all the up-front\n//! checks but match differently against some ComplexSelector in the selector.\n//! If that happens, then they can have descendants that might themselves pass\n//! the up-front checks but would have different matching results for the\n//! selector in question.  In this case, \"descendants\" includes pseudo-elements,\n//! so there is a single selector map of revalidation selectors that includes\n//! both selectors targeting elements and selectors targeting pseudo-element\n//! originating elements.  We ensure that the pseudo-element parts of all these\n//! selectors are effectively stripped off, so that matching them all against\n//! elements makes sense.\n\nuse crate::applicable_declarations::ApplicableDeclarationBlock;\nuse crate::bloom::StyleBloom;\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::{CascadeInputs, SharedStyleContext, StyleContext};\nuse crate::dom::{SendElement, TElement, TShadowRoot};\nuse crate::properties::ComputedValues;\nuse crate::selector_map::RelevantAttributes;\nuse crate::style_resolver::{PrimaryStyle, ResolvedElementStyles};\nuse crate::stylist::Stylist;\nuse crate::values::AtomIdent;\nuse atomic_refcell::{AtomicRefCell, AtomicRefMut};\nuse selectors::matching::{NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode};\nuse smallbitvec::SmallBitVec;\nuse smallvec::SmallVec;\nuse std::marker::PhantomData;\nuse std::mem;\nuse std::ops::Deref;\nuse std::ptr::NonNull;\nuse uluru::LRUCache;\n\nmod checks;\n\n/// The amount of nodes that the style sharing candidate cache should hold at\n/// most.\n///\n/// The cache size was chosen by measuring style sharing and resulting\n/// performance on a few pages; sizes up to about 32 were giving good sharing\n/// improvements (e.g. 3x fewer styles having to be resolved than at size 8) and\n/// slight performance improvements.  Sizes larger than 32 haven't really been\n/// tested.\npub const SHARING_CACHE_SIZE: usize = 32;\n\n/// Opaque pointer type to compare ComputedValues identities.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct OpaqueComputedValues(NonNull<()>);\n\nunsafe impl Send for OpaqueComputedValues {}\nunsafe impl Sync for OpaqueComputedValues {}\n\nimpl OpaqueComputedValues {\n    fn from(cv: &ComputedValues) -> Self {\n        let p =\n            unsafe { NonNull::new_unchecked(cv as *const ComputedValues as *const () as *mut ()) };\n        OpaqueComputedValues(p)\n    }\n\n    fn eq(&self, cv: &ComputedValues) -> bool {\n        Self::from(cv) == *self\n    }\n}\n\n/// The results from the revalidation step.\n///\n/// Rather than either:\n///\n///  * Plainly rejecting sharing for elements with different attributes (which would be unfortunate\n///    because a lot of elements have different attributes yet those attributes are not\n///    style-relevant).\n///\n///  * Having to give up on per-attribute bucketing, which would be unfortunate because it\n///    increases the cost of revalidation for pages with lots of global attribute selectors (see\n///    bug 1868316).\n///\n///  * We also store the style-relevant attributes for these elements, in order to guarantee that\n///    we end up looking at the same selectors.\n///\n#[derive(Debug, Default)]\npub struct RevalidationResult {\n    /// A bit for each selector matched. This is sound because we guarantee we look up into the\n    /// same buckets via the pre-revalidation checks and relevant_attributes.\n    pub selectors_matched: SmallBitVec,\n    /// The set of attributes of this element that were relevant for its style.\n    pub relevant_attributes: RelevantAttributes,\n}\n\n/// The results from trying to revalidate scopes this element is in.\n#[derive(Debug, Default, PartialEq)]\npub struct ScopeRevalidationResult {\n    /// A bit for each scope activated.\n    pub scopes_matched: SmallBitVec,\n}\n\nimpl PartialEq for RevalidationResult {\n    fn eq(&self, other: &Self) -> bool {\n        if self.relevant_attributes != other.relevant_attributes {\n            return false;\n        }\n\n        // This assert \"ensures\", to some extent, that the two candidates have matched the\n        // same rulehash buckets, and as such, that the bits we're comparing represent the\n        // same set of selectors.\n        debug_assert_eq!(self.selectors_matched.len(), other.selectors_matched.len());\n        self.selectors_matched == other.selectors_matched\n    }\n}\n\n/// Some data we want to avoid recomputing all the time while trying to share\n/// style.\n#[derive(Debug, Default)]\npub struct ValidationData {\n    /// The class list of this element.\n    ///\n    /// TODO(emilio): Maybe check whether rules for these classes apply to the\n    /// element?\n    class_list: Option<SmallVec<[AtomIdent; 5]>>,\n\n    /// The part list of this element.\n    ///\n    /// TODO(emilio): Maybe check whether rules with these part names apply to\n    /// the element?\n    part_list: Option<SmallVec<[AtomIdent; 5]>>,\n\n    /// The list of presentational attributes of the element.\n    pres_hints: Option<SmallVec<[ApplicableDeclarationBlock; 5]>>,\n\n    /// The pointer identity of the parent ComputedValues.\n    parent_style_identity: Option<OpaqueComputedValues>,\n\n    /// The cached result of matching this entry against the revalidation\n    /// selectors.\n    revalidation_match_results: Option<RevalidationResult>,\n}\n\nimpl ValidationData {\n    /// Move the cached data to a new instance, and return it.\n    pub fn take(&mut self) -> Self {\n        mem::replace(self, Self::default())\n    }\n\n    /// Get or compute the list of presentational attributes associated with\n    /// this element.\n    pub fn pres_hints<E>(&mut self, element: E) -> &[ApplicableDeclarationBlock]\n    where\n        E: TElement,\n    {\n        self.pres_hints.get_or_insert_with(|| {\n            let mut pres_hints = SmallVec::new();\n            element.synthesize_presentational_hints_for_legacy_attributes(\n                VisitedHandlingMode::AllLinksUnvisited,\n                &mut pres_hints,\n            );\n            pres_hints\n        })\n    }\n\n    /// Get or compute the part-list associated with this element.\n    pub fn part_list<E>(&mut self, element: E) -> &[AtomIdent]\n    where\n        E: TElement,\n    {\n        if !element.has_part_attr() {\n            return &[];\n        }\n        self.part_list.get_or_insert_with(|| {\n            let mut list = SmallVec::<[_; 5]>::new();\n            element.each_part(|p| list.push(p.clone()));\n            // See below for the reasoning.\n            if !list.spilled() {\n                list.sort_unstable_by_key(|a| a.get_hash());\n            }\n            list\n        })\n    }\n\n    /// Get or compute the class-list associated with this element.\n    pub fn class_list<E>(&mut self, element: E) -> &[AtomIdent]\n    where\n        E: TElement,\n    {\n        self.class_list.get_or_insert_with(|| {\n            let mut list = SmallVec::<[_; 5]>::new();\n            element.each_class(|c| list.push(c.clone()));\n            // Assuming there are a reasonable number of classes (we use the\n            // inline capacity as \"reasonable number\"), sort them to so that\n            // we don't mistakenly reject sharing candidates when one element\n            // has \"foo bar\" and the other has \"bar foo\".\n            if !list.spilled() {\n                list.sort_unstable_by_key(|a| a.get_hash());\n            }\n            list\n        })\n    }\n\n    /// Get or compute the parent style identity.\n    pub fn parent_style_identity<E>(&mut self, el: E) -> OpaqueComputedValues\n    where\n        E: TElement,\n    {\n        self.parent_style_identity\n            .get_or_insert_with(|| {\n                let parent = el.inheritance_parent().unwrap();\n                let values =\n                    OpaqueComputedValues::from(parent.borrow_data().unwrap().styles.primary());\n                values\n            })\n            .clone()\n    }\n\n    /// Computes the revalidation results if needed, and returns it.\n    /// Inline so we know at compile time what bloom_known_valid is.\n    #[inline]\n    fn revalidation_match_results<E>(\n        &mut self,\n        element: E,\n        stylist: &Stylist,\n        bloom: &StyleBloom<E>,\n        selector_caches: &mut SelectorCaches,\n        bloom_known_valid: bool,\n        needs_selector_flags: NeedsSelectorFlags,\n    ) -> &RevalidationResult\n    where\n        E: TElement,\n    {\n        self.revalidation_match_results.get_or_insert_with(|| {\n            // The bloom filter may already be set up for our element.\n            // If it is, use it.  If not, we must be in a candidate\n            // (i.e. something in the cache), and the element is one\n            // of our cousins, not a sibling.  In that case, we'll\n            // just do revalidation selector matching without a bloom\n            // filter, to avoid thrashing the filter.\n            let bloom_to_use = if bloom_known_valid {\n                debug_assert_eq!(bloom.current_parent(), element.traversal_parent());\n                Some(bloom.filter())\n            } else {\n                if bloom.current_parent() == element.traversal_parent() {\n                    Some(bloom.filter())\n                } else {\n                    None\n                }\n            };\n            stylist.match_revalidation_selectors(\n                element,\n                bloom_to_use,\n                selector_caches,\n                needs_selector_flags,\n            )\n        })\n    }\n}\n\n/// Information regarding a style sharing candidate, that is, an entry in the\n/// style sharing cache.\n///\n/// Note that this information is stored in TLS and cleared after the traversal,\n/// and once here, the style information of the element is immutable, so it's\n/// safe to access.\n///\n/// Important: If you change the members/layout here, You need to do the same for\n/// FakeCandidate below.\n#[derive(Debug)]\npub struct StyleSharingCandidate<E: TElement> {\n    /// The element.\n    element: E,\n    validation_data: ValidationData,\n    considered_nontrivial_scoped_style: bool,\n}\n\nstruct FakeCandidate {\n    _element: usize,\n    _validation_data: ValidationData,\n    _may_contain_scoped_style: bool,\n}\n\nimpl<E: TElement> Deref for StyleSharingCandidate<E> {\n    type Target = E;\n\n    fn deref(&self) -> &Self::Target {\n        &self.element\n    }\n}\n\nimpl<E: TElement> StyleSharingCandidate<E> {\n    /// Get the classlist of this candidate.\n    fn class_list(&mut self) -> &[AtomIdent] {\n        self.validation_data.class_list(self.element)\n    }\n\n    /// Get the part list of this candidate.\n    fn part_list(&mut self) -> &[AtomIdent] {\n        self.validation_data.part_list(self.element)\n    }\n\n    /// Get the pres hints of this candidate.\n    fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {\n        self.validation_data.pres_hints(self.element)\n    }\n\n    /// Get the parent style identity.\n    fn parent_style_identity(&mut self) -> OpaqueComputedValues {\n        self.validation_data.parent_style_identity(self.element)\n    }\n\n    /// Compute the bit vector of revalidation selector match results\n    /// for this candidate.\n    fn revalidation_match_results(\n        &mut self,\n        stylist: &Stylist,\n        bloom: &StyleBloom<E>,\n        selector_caches: &mut SelectorCaches,\n    ) -> &RevalidationResult {\n        self.validation_data.revalidation_match_results(\n            self.element,\n            stylist,\n            bloom,\n            selector_caches,\n            /* bloom_known_valid = */ false,\n            // The candidate must already have the right bits already, if\n            // needed.\n            NeedsSelectorFlags::No,\n        )\n    }\n\n    fn scope_revalidation_results(\n        &mut self,\n        stylist: &Stylist,\n        selector_caches: &mut SelectorCaches,\n    ) -> ScopeRevalidationResult {\n        stylist.revalidate_scopes(&self.element, selector_caches, NeedsSelectorFlags::No)\n    }\n}\n\nimpl<E: TElement> PartialEq<StyleSharingCandidate<E>> for StyleSharingCandidate<E> {\n    fn eq(&self, other: &Self) -> bool {\n        self.element == other.element\n    }\n}\n\n/// An element we want to test against the style sharing cache.\npub struct StyleSharingTarget<E: TElement> {\n    element: E,\n    validation_data: ValidationData,\n}\n\nimpl<E: TElement> Deref for StyleSharingTarget<E> {\n    type Target = E;\n\n    fn deref(&self) -> &Self::Target {\n        &self.element\n    }\n}\n\nimpl<E: TElement> StyleSharingTarget<E> {\n    /// Trivially construct a new StyleSharingTarget to test against the cache.\n    pub fn new(element: E) -> Self {\n        Self {\n            element: element,\n            validation_data: ValidationData::default(),\n        }\n    }\n\n    fn class_list(&mut self) -> &[AtomIdent] {\n        self.validation_data.class_list(self.element)\n    }\n\n    fn part_list(&mut self) -> &[AtomIdent] {\n        self.validation_data.part_list(self.element)\n    }\n\n    /// Get the pres hints of this candidate.\n    fn pres_hints(&mut self) -> &[ApplicableDeclarationBlock] {\n        self.validation_data.pres_hints(self.element)\n    }\n\n    /// Get the parent style identity.\n    fn parent_style_identity(&mut self) -> OpaqueComputedValues {\n        self.validation_data.parent_style_identity(self.element)\n    }\n\n    fn revalidation_match_results(\n        &mut self,\n        stylist: &Stylist,\n        bloom: &StyleBloom<E>,\n        selector_caches: &mut SelectorCaches,\n    ) -> &RevalidationResult {\n        // It's important to set the selector flags. Otherwise, if we succeed in\n        // sharing the style, we may not set the slow selector flags for the\n        // right elements (which may not necessarily be |element|), causing\n        // missed restyles after future DOM mutations.\n        //\n        // Gecko's test_bug534804.html exercises this. A minimal testcase is:\n        // <style> #e:empty + span { ... } </style>\n        // <span id=\"e\">\n        //   <span></span>\n        // </span>\n        // <span></span>\n        //\n        // The style sharing cache will get a hit for the second span. When the\n        // child span is subsequently removed from the DOM, missing selector\n        // flags would cause us to miss the restyle on the second span.\n        self.validation_data.revalidation_match_results(\n            self.element,\n            stylist,\n            bloom,\n            selector_caches,\n            /* bloom_known_valid = */ true,\n            NeedsSelectorFlags::Yes,\n        )\n    }\n\n    fn scope_revalidation_results(\n        &mut self,\n        stylist: &Stylist,\n        selector_caches: &mut SelectorCaches,\n    ) -> ScopeRevalidationResult {\n        stylist.revalidate_scopes(&self.element, selector_caches, NeedsSelectorFlags::Yes)\n    }\n\n    /// Attempts to share a style with another node.\n    pub fn share_style_if_possible(\n        &mut self,\n        context: &mut StyleContext<E>,\n    ) -> Option<ResolvedElementStyles> {\n        let cache = &mut context.thread_local.sharing_cache;\n        let shared_context = &context.shared;\n        let bloom_filter = &context.thread_local.bloom_filter;\n        let selector_caches = &mut context.thread_local.selector_caches;\n\n        if cache.dom_depth != bloom_filter.matching_depth() {\n            debug!(\n                \"Can't share style, because DOM depth changed from {:?} to {:?}, element: {:?}\",\n                cache.dom_depth,\n                bloom_filter.matching_depth(),\n                self.element\n            );\n            return None;\n        }\n        debug_assert_eq!(\n            bloom_filter.current_parent(),\n            self.element.traversal_parent()\n        );\n\n        cache.share_style_if_possible(shared_context, bloom_filter, selector_caches, self)\n    }\n\n    /// Gets the validation data used to match against this target, if any.\n    pub fn take_validation_data(&mut self) -> ValidationData {\n        self.validation_data.take()\n    }\n}\n\nstruct SharingCacheBase<Candidate> {\n    entries: LRUCache<Candidate, SHARING_CACHE_SIZE>,\n}\n\nimpl<Candidate> Default for SharingCacheBase<Candidate> {\n    fn default() -> Self {\n        Self {\n            entries: LRUCache::default(),\n        }\n    }\n}\n\nimpl<Candidate> SharingCacheBase<Candidate> {\n    fn clear(&mut self) {\n        self.entries.clear();\n    }\n\n    fn is_empty(&self) -> bool {\n        self.entries.len() == 0\n    }\n}\n\nimpl<E: TElement> SharingCache<E> {\n    fn insert(\n        &mut self,\n        element: E,\n        validation_data_holder: Option<&mut StyleSharingTarget<E>>,\n        considered_nontrivial_scoped_style: bool,\n    ) {\n        let validation_data = match validation_data_holder {\n            Some(v) => v.take_validation_data(),\n            None => ValidationData::default(),\n        };\n        self.entries.insert(StyleSharingCandidate {\n            element,\n            validation_data,\n            considered_nontrivial_scoped_style,\n        });\n    }\n}\n\n/// Style sharing caches are are large allocations, so we store them in thread-local\n/// storage such that they can be reused across style traversals. Ideally, we'd just\n/// stack-allocate these buffers with uninitialized memory, but right now rustc can't\n/// avoid memmoving the entire cache during setup, which gets very expensive. See\n/// issues like [1] and [2].\n///\n/// Given that the cache stores entries of type TElement, we transmute to usize\n/// before storing in TLS. This is safe as long as we make sure to empty the cache\n/// before we let it go.\n///\n/// [1] https://github.com/rust-lang/rust/issues/42763\n/// [2] https://github.com/rust-lang/rust/issues/13707\ntype SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;\ntype TypelessSharingCache = SharingCacheBase<FakeCandidate>;\n\nthread_local! {\n    // See the comment on bloom.rs about why do we leak this.\n    static SHARING_CACHE_KEY: &'static AtomicRefCell<TypelessSharingCache> =\n        Box::leak(Default::default());\n}\n\n/// An LRU cache of the last few nodes seen, so that we can aggressively try to\n/// reuse their styles.\n///\n/// Note that this cache is flushed every time we steal work from the queue, so\n/// storing nodes here temporarily is safe.\npub struct StyleSharingCache<E: TElement> {\n    /// The LRU cache, with the type cast away to allow persisting the allocation.\n    cache_typeless: AtomicRefMut<'static, TypelessSharingCache>,\n    /// Bind this structure to the lifetime of E, since that's what we effectively store.\n    marker: PhantomData<SendElement<E>>,\n    /// The DOM depth we're currently at.  This is used as an optimization to\n    /// clear the cache when we change depths, since we know at that point\n    /// nothing in the cache will match.\n    dom_depth: usize,\n}\n\nimpl<E: TElement> Drop for StyleSharingCache<E> {\n    fn drop(&mut self) {\n        self.clear();\n    }\n}\n\nimpl<E: TElement> StyleSharingCache<E> {\n    #[allow(dead_code)]\n    fn cache(&self) -> &SharingCache<E> {\n        let base: &TypelessSharingCache = &*self.cache_typeless;\n        unsafe { mem::transmute(base) }\n    }\n\n    fn cache_mut(&mut self) -> &mut SharingCache<E> {\n        let base: &mut TypelessSharingCache = &mut *self.cache_typeless;\n        unsafe { mem::transmute(base) }\n    }\n\n    /// Create a new style sharing candidate cache.\n\n    // Forced out of line to limit stack frame sizes after extra inlining from\n    // https://github.com/rust-lang/rust/pull/43931\n    //\n    // See https://github.com/servo/servo/pull/18420#issuecomment-328769322\n    #[inline(never)]\n    pub fn new() -> Self {\n        assert_eq!(\n            mem::size_of::<SharingCache<E>>(),\n            mem::size_of::<TypelessSharingCache>()\n        );\n        assert_eq!(\n            mem::align_of::<SharingCache<E>>(),\n            mem::align_of::<TypelessSharingCache>()\n        );\n        let cache = SHARING_CACHE_KEY.with(|c| c.borrow_mut());\n        debug_assert!(cache.is_empty());\n\n        StyleSharingCache {\n            cache_typeless: cache,\n            marker: PhantomData,\n            dom_depth: 0,\n        }\n    }\n\n    /// Tries to insert an element in the style sharing cache.\n    ///\n    /// Fails if we know it should never be in the cache.\n    ///\n    /// NB: We pass a source for the validation data, rather than the data itself,\n    /// to avoid memmoving at each function call. See rust issue #42763.\n    pub fn insert_if_possible(\n        &mut self,\n        element: &E,\n        style: &PrimaryStyle,\n        validation_data_holder: Option<&mut StyleSharingTarget<E>>,\n        dom_depth: usize,\n        shared_context: &SharedStyleContext,\n    ) {\n        let parent = match element.traversal_parent() {\n            Some(element) => element,\n            None => {\n                debug!(\"Failing to insert to the cache: no parent element\");\n                return;\n            },\n        };\n\n        if !element.matches_user_and_content_rules() {\n            debug!(\"Failing to insert into the cache: no tree rules:\");\n            return;\n        }\n\n        // If the element has running animations, we can't share style.\n        //\n        // This is distinct from the specifies_{animations,transitions} check below,\n        // because:\n        //   * Animations can be triggered directly via the Web Animations API.\n        //   * Our computed style can still be affected by animations after we no\n        //     longer match any animation rules, since removing animations involves\n        //     a sequential task and an additional traversal.\n        if element.has_animations(shared_context) {\n            debug!(\"Failing to insert to the cache: running animations\");\n            return;\n        }\n\n        if element.smil_override().is_some() {\n            debug!(\"Failing to insert to the cache: SMIL\");\n            return;\n        }\n\n        debug!(\n            \"Inserting into cache: {:?} with parent {:?}\",\n            element, parent\n        );\n\n        if self.dom_depth != dom_depth {\n            debug!(\n                \"Clearing cache because depth changed from {:?} to {:?}, element: {:?}\",\n                self.dom_depth, dom_depth, element\n            );\n            self.clear();\n            self.dom_depth = dom_depth;\n        }\n        self.cache_mut().insert(\n            *element,\n            validation_data_holder,\n            style\n                .style()\n                .flags\n                .intersects(ComputedValueFlags::CONSIDERED_NONTRIVIAL_SCOPED_STYLE),\n        );\n    }\n\n    /// Clear the style sharing candidate cache.\n    pub fn clear(&mut self) {\n        self.cache_mut().clear();\n    }\n\n    /// Attempts to share a style with another node.\n    fn share_style_if_possible(\n        &mut self,\n        shared_context: &SharedStyleContext,\n        bloom_filter: &StyleBloom<E>,\n        selector_caches: &mut SelectorCaches,\n        target: &mut StyleSharingTarget<E>,\n    ) -> Option<ResolvedElementStyles> {\n        if shared_context.options.disable_style_sharing_cache {\n            debug!(\n                \"{:?} Cannot share style: style sharing cache disabled\",\n                target.element\n            );\n            return None;\n        }\n\n        if target.inheritance_parent().is_none() {\n            debug!(\n                \"{:?} Cannot share style: element has no parent\",\n                target.element\n            );\n            return None;\n        }\n\n        if !target.matches_user_and_content_rules() {\n            debug!(\"{:?} Cannot share style: content rules\", target.element);\n            return None;\n        }\n\n        self.cache_mut().entries.lookup(|candidate| {\n            Self::test_candidate(\n                target,\n                candidate,\n                &shared_context,\n                bloom_filter,\n                selector_caches,\n                shared_context,\n            )\n        })\n    }\n\n    fn test_candidate(\n        target: &mut StyleSharingTarget<E>,\n        candidate: &mut StyleSharingCandidate<E>,\n        shared: &SharedStyleContext,\n        bloom: &StyleBloom<E>,\n        selector_caches: &mut SelectorCaches,\n        shared_context: &SharedStyleContext,\n    ) -> Option<ResolvedElementStyles> {\n        debug_assert!(target.matches_user_and_content_rules());\n\n        // Check that we have the same parent, or at least that the parents\n        // share styles and permit sharing across their children. The latter\n        // check allows us to share style between cousins if the parents\n        // shared style.\n        if !checks::parents_allow_sharing(target, candidate) {\n            trace!(\"Miss: Parent\");\n            return None;\n        }\n\n        if target.local_name() != candidate.element.local_name() {\n            trace!(\"Miss: Local Name\");\n            return None;\n        }\n\n        if target.namespace() != candidate.element.namespace() {\n            trace!(\"Miss: Namespace\");\n            return None;\n        }\n\n        // We do not ignore visited state here, because Gecko needs to store\n        // extra bits on visited styles, so these contexts cannot be shared.\n        if target.element.state() != candidate.state() {\n            trace!(\"Miss: User and Author State\");\n            return None;\n        }\n\n        if target.is_link() != candidate.element.is_link() {\n            trace!(\"Miss: Link\");\n            return None;\n        }\n\n        // If two elements belong to different shadow trees, different rules may\n        // apply to them, from the respective trees.\n        if target.element.containing_shadow() != candidate.element.containing_shadow() {\n            trace!(\"Miss: Different containing shadow roots\");\n            return None;\n        }\n\n        // If the elements are not assigned to the same slot they could match\n        // different ::slotted() rules in the slot scope.\n        //\n        // If two elements are assigned to different slots, even within the same\n        // shadow root, they could match different rules, due to the slot being\n        // assigned to yet another slot in another shadow root.\n        if target.element.assigned_slot() != candidate.element.assigned_slot() {\n            // TODO(emilio): We could have a look at whether the shadow roots\n            // actually have slotted rules and such.\n            trace!(\"Miss: Different assigned slots\");\n            return None;\n        }\n\n        if target.implemented_pseudo_element() != candidate.implemented_pseudo_element() {\n            trace!(\"Miss: Element backed pseudo-element\");\n            return None;\n        }\n\n        // Shadow hosts can share style when they have matching CascadeData pointers, which\n        // ensures they match the same :host rules.\n        match (\n            target.element.shadow_root().and_then(|s| s.style_data()),\n            candidate.element.shadow_root().and_then(|s| s.style_data()),\n        ) {\n            (Some(td), Some(cd)) if std::ptr::eq(td, cd) => {},\n            (None, None) => {},\n            _ => {\n                trace!(\"Miss: Different shadow root style data\");\n                return None;\n            },\n        }\n\n        if target.element.has_animations(shared_context)\n            || candidate.element.has_animations(shared_context)\n        {\n            trace!(\"Miss: Has Animations\");\n            return None;\n        }\n\n        if target.element.smil_override().is_some() {\n            trace!(\"Miss: SMIL\");\n            return None;\n        }\n\n        if target.matches_user_and_content_rules()\n            != candidate.element.matches_user_and_content_rules()\n        {\n            trace!(\"Miss: User and Author Rules\");\n            return None;\n        }\n\n        // It's possible that there are no styles for either id.\n        if checks::may_match_different_id_rules(shared, target.element, candidate.element) {\n            trace!(\"Miss: ID Attr\");\n            return None;\n        }\n\n        if !checks::have_same_style_attribute(target, candidate, shared_context) {\n            trace!(\"Miss: Style Attr\");\n            return None;\n        }\n\n        if !checks::have_same_class(target, candidate) {\n            trace!(\"Miss: Class\");\n            return None;\n        }\n\n        if !checks::have_same_presentational_hints(target, candidate) {\n            trace!(\"Miss: Pres Hints\");\n            return None;\n        }\n\n        if !checks::have_same_parts(target, candidate) {\n            trace!(\"Miss: Shadow parts\");\n            return None;\n        }\n\n        if !checks::have_same_referenced_attrs(target, candidate) {\n            trace!(\"Miss: Attr references\");\n            return None;\n        }\n\n        if !checks::revalidate(target, candidate, shared, bloom, selector_caches) {\n            trace!(\"Miss: Revalidation\");\n            return None;\n        }\n\n        // While the scoped style rules may be different (e.g. `@scope { .foo + .foo { /* .. */} }`),\n        // we rely on revalidation to handle that.\n        if candidate.considered_nontrivial_scoped_style\n            && !checks::revalidate_scope(target, candidate, shared, selector_caches)\n        {\n            trace!(\"Miss: Active Scopes\");\n            return None;\n        }\n\n        debug!(\n            \"Sharing allowed between {:?} and {:?}\",\n            target.element, candidate.element\n        );\n        Some(candidate.element.borrow_data().unwrap().share_styles())\n    }\n\n    /// Attempts to find an element in the cache with the given primary rule\n    /// node and parent.\n    ///\n    /// FIXME(emilio): re-measure this optimization, and remove if it's not very\n    /// useful... It's probably not worth the complexity / obscure bugs.\n    pub fn lookup_by_rules(\n        &mut self,\n        shared_context: &SharedStyleContext,\n        inherited: &ComputedValues,\n        inputs: &CascadeInputs,\n        target: E,\n    ) -> Option<PrimaryStyle> {\n        if shared_context.options.disable_style_sharing_cache {\n            return None;\n        }\n\n        self.cache_mut().entries.lookup(|candidate| {\n            debug_assert_ne!(candidate.element, target);\n            if !candidate.parent_style_identity().eq(inherited) {\n                return None;\n            }\n            if !checks::have_same_referenced_attrs(&StyleSharingTarget::new(target), candidate) {\n                return None;\n            }\n            let data = candidate.element.borrow_data().unwrap();\n            let style = data.styles.primary();\n            if style.rules.as_ref() != Some(&inputs.rules.as_ref().unwrap()) {\n                return None;\n            }\n            if style.visited_rules() != inputs.visited_rules.as_ref() {\n                return None;\n            }\n            // NOTE(emilio): We only need to check name / namespace because we\n            // do name-dependent style adjustments, like the display: contents\n            // to display: none adjustment.\n            if target.namespace() != candidate.element.namespace()\n                || target.local_name() != candidate.element.local_name()\n            {\n                return None;\n            }\n            // When using container units, inherited style + rules matched aren't enough to\n            // determine whether the style is the same. We could actually do a full container\n            // lookup but for now we just check that our actual traversal parent matches.\n            if data\n                .styles\n                .primary()\n                .flags\n                .intersects(ComputedValueFlags::USES_CONTAINER_UNITS)\n                && candidate.element.traversal_parent() != target.traversal_parent()\n            {\n                return None;\n            }\n            // Rule nodes and styles are computed independent of the element's actual visitedness,\n            // but at the end of the cascade (in `adjust_for_visited`) we do store the\n            // RELEVANT_LINK_VISITED flag, so we can't share by rule node between visited and\n            // unvisited styles. We don't check for visitedness and just refuse to share for links\n            // entirely, so that visitedness doesn't affect timing.\n            if target.is_link() || candidate.element.is_link() {\n                return None;\n            }\n\n            let target_depends_on_style_queries = inputs\n                .flags\n                .contains(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);\n            let candidate_depends_on_style_queries = style\n                .flags\n                .contains(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);\n\n            if target_depends_on_style_queries != candidate_depends_on_style_queries {\n                // If we're considering sharing across two elements, target\n                // depends on style queries and candidate doesn't, right\n                // now we can share it, but by cloning the candidate style\n                // if we adjust the flags.\n                // If we're considering sharing across two elements, target\n                // does not depend on style queries and candidate does, we\n                // can share them with the same flags, but that would\n                // overinvalidate if we already know we don't need to keep\n                // `DEPENDS_ON_CONTAINER_STYLE_QUERY`.\n                let mut new_flags = inputs.flags | style.flags;\n                new_flags.set(\n                    ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY,\n                    target_depends_on_style_queries,\n                );\n\n                return Some(PrimaryStyle {\n                    style: data.clone_style_with_flags(new_flags),\n                    reused_via_rule_node: true,\n                });\n            }\n\n            Some(data.share_primary_style())\n        })\n    }\n}\n"
  },
  {
    "path": "style/simple_buckets_map.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::derives::*;\nuse crate::selector_map::{MaybeCaseInsensitiveHashMap, PrecomputedHashMap};\nuse crate::{Atom, LocalName, ShrinkIfNeeded};\n\n/// A map for filtering by easily-discernable features in a selector.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct SimpleBucketsMap<T> {\n    pub classes: MaybeCaseInsensitiveHashMap<Atom, T>,\n    pub ids: MaybeCaseInsensitiveHashMap<Atom, T>,\n    pub local_names: PrecomputedHashMap<LocalName, T>,\n}\n\nimpl<T> Default for SimpleBucketsMap<T> {\n    fn default() -> Self {\n        // TODO(dshin): This is a bit annoying - even if these maps would be empty,\n        // deriving this trait requires `T: Default`\n        // This is a known issue - See https://github.com/rust-lang/rust/issues/26925.\n        Self {\n            classes: Default::default(),\n            ids: Default::default(),\n            local_names: Default::default(),\n        }\n    }\n}\n\nimpl<T> SimpleBucketsMap<T> {\n    /// Clears the map.\n    #[inline(always)]\n    pub fn clear(&mut self) {\n        self.classes.clear();\n        self.ids.clear();\n        self.local_names.clear();\n    }\n\n    /// Shrink the capacity of the map if needed.\n    #[inline(always)]\n    pub fn shrink_if_needed(&mut self) {\n        self.classes.shrink_if_needed();\n        self.ids.shrink_if_needed();\n        self.local_names.shrink_if_needed();\n    }\n\n    /// Returns whether there's nothing in the map.\n    #[inline(always)]\n    pub fn is_empty(&self) -> bool {\n        self.classes.is_empty() && self.ids.is_empty() && self.local_names.is_empty()\n    }\n}\n"
  },
  {
    "path": "style/str.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! String utils for attributes and similar stuff.\n\n#![deny(missing_docs)]\n\nuse num_traits::ToPrimitive;\nuse std::borrow::Cow;\nuse std::iter::{Filter, Peekable};\nuse std::str::Split;\n\n/// A static slice of characters.\npub type StaticCharVec = &'static [char];\n\n/// A static slice of `str`s.\npub type StaticStringVec = &'static [&'static str];\n\n/// A \"space character\" according to:\n///\n/// <https://html.spec.whatwg.org/multipage/#space-character>\npub static HTML_SPACE_CHARACTERS: StaticCharVec =\n    &['\\u{0020}', '\\u{0009}', '\\u{000a}', '\\u{000c}', '\\u{000d}'];\n\n/// Whether a character is a HTML whitespace character.\n#[inline]\npub fn char_is_whitespace(c: char) -> bool {\n    HTML_SPACE_CHARACTERS.contains(&c)\n}\n\n/// Whether all the string is HTML whitespace.\n#[inline]\npub fn is_whitespace(s: &str) -> bool {\n    s.chars().all(char_is_whitespace)\n}\n\n#[inline]\nfn not_empty(&split: &&str) -> bool {\n    !split.is_empty()\n}\n\n/// Split a string on HTML whitespace.\n#[inline]\npub fn split_html_space_chars<'a>(\n    s: &'a str,\n) -> Filter<Split<'a, StaticCharVec>, fn(&&str) -> bool> {\n    s.split(HTML_SPACE_CHARACTERS)\n        .filter(not_empty as fn(&&str) -> bool)\n}\n\n/// Split a string on commas.\n#[inline]\npub fn split_commas<'a>(s: &'a str) -> Filter<Split<'a, char>, fn(&&str) -> bool> {\n    s.split(',').filter(not_empty as fn(&&str) -> bool)\n}\n\n/// Character is ascii digit\npub fn is_ascii_digit(c: &char) -> bool {\n    match *c {\n        '0'..='9' => true,\n        _ => false,\n    }\n}\n\nfn is_decimal_point(c: char) -> bool {\n    c == '.'\n}\n\nfn is_exponent_char(c: char) -> bool {\n    match c {\n        'e' | 'E' => true,\n        _ => false,\n    }\n}\n\n/// Read a set of ascii digits and read them into a number.\npub fn read_numbers<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> (Option<i64>, usize) {\n    match iter.peek() {\n        Some(c) if is_ascii_digit(c) => (),\n        _ => return (None, 0),\n    }\n\n    iter.take_while(is_ascii_digit)\n        .map(|d| d as i64 - '0' as i64)\n        .fold((Some(0i64), 0), |accumulator, d| {\n            let digits = accumulator\n                .0\n                .and_then(|accumulator| accumulator.checked_mul(10))\n                .and_then(|accumulator| accumulator.checked_add(d));\n            (digits, accumulator.1 + 1)\n        })\n}\n\n/// Read a decimal fraction.\npub fn read_fraction<I: Iterator<Item = char>>(\n    mut iter: Peekable<I>,\n    mut divisor: f64,\n    value: f64,\n) -> (f64, usize) {\n    match iter.peek() {\n        Some(c) if is_decimal_point(*c) => (),\n        _ => return (value, 0),\n    }\n    iter.next();\n\n    iter.take_while(is_ascii_digit)\n        .map(|d| d as i64 - '0' as i64)\n        .fold((value, 1), |accumulator, d| {\n            divisor *= 10f64;\n            (accumulator.0 + d as f64 / divisor, accumulator.1 + 1)\n        })\n}\n\n/// Reads an exponent from an iterator over chars, for example `e100`.\npub fn read_exponent<I: Iterator<Item = char>>(mut iter: Peekable<I>) -> Option<i32> {\n    match iter.peek() {\n        Some(c) if is_exponent_char(*c) => (),\n        _ => return None,\n    }\n    iter.next();\n\n    match iter.peek() {\n        None => None,\n        Some(&'-') => {\n            iter.next();\n            read_numbers(iter).0.map(|exp| -exp.to_i32().unwrap_or(0))\n        },\n        Some(&'+') => {\n            iter.next();\n            read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0))\n        },\n        Some(_) => read_numbers(iter).0.map(|exp| exp.to_i32().unwrap_or(0)),\n    }\n}\n\n/// Join a set of strings with a given delimiter `join`.\npub fn str_join<I, T>(strs: I, join: &str) -> String\nwhere\n    I: IntoIterator<Item = T>,\n    T: AsRef<str>,\n{\n    strs.into_iter()\n        .enumerate()\n        .fold(String::new(), |mut acc, (i, s)| {\n            if i > 0 {\n                acc.push_str(join);\n            }\n            acc.push_str(s.as_ref());\n            acc\n        })\n}\n\n/// Returns true if a given string has a given prefix with case-insensitive match.\npub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {\n    string.len() >= prefix.len()\n        && string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())\n}\n\n/// Returns an ascii lowercase version of a string, only allocating if needed.\npub fn string_as_ascii_lowercase<'a>(input: &'a str) -> Cow<'a, str> {\n    if input.bytes().any(|c| matches!(c, b'A'..=b'Z')) {\n        input.to_ascii_lowercase().into()\n    } else {\n        // Already ascii lowercase.\n        Cow::Borrowed(input)\n    }\n}\n"
  },
  {
    "path": "style/style_adjuster.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A struct to encapsulate all the style fixups and flags propagations\n//! a computed style needs in order for it to adhere to the CSS spec.\n\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::dom::TElement;\nuse crate::logical_geometry::PhysicalSide;\nuse crate::properties::longhands::display::computed_value::T as Display;\nuse crate::properties::longhands::float::computed_value::T as Float;\nuse crate::properties::longhands::position::computed_value::T as Position;\n#[cfg(feature = \"gecko\")]\nuse crate::properties::longhands::{\n    contain::computed_value::T as Contain, container_type::computed_value::T as ContainerType,\n    content_visibility::computed_value::T as ContentVisibility,\n};\n#[cfg(feature = \"gecko\")]\nuse crate::properties::LonghandId;\nuse crate::properties::{ComputedValues, LonghandIdSet, StyleBuilder};\nuse crate::values::computed::position::{\n    PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword, TryTacticAdjustment,\n};\nuse crate::values::specified::align::AlignFlags;\n\n/// A struct that implements all the adjustment methods.\n///\n/// NOTE(emilio): If new adjustments are introduced that depend on reset\n/// properties of the parent, you may need tweaking the\n/// `ChildCascadeRequirement` code in `matching.rs`.\n///\n/// NOTE(emilio): Also, if new adjustments are introduced that break the\n/// following invariant:\n///\n///   Given same tag name, namespace, rules and parent style, two elements would\n///   end up with exactly the same style.\n///\n/// Then you need to adjust the lookup_by_rules conditions in the sharing cache.\npub struct StyleAdjuster<'a, 'b: 'a> {\n    style: &'a mut StyleBuilder<'b>,\n}\n\n#[cfg(feature = \"gecko\")]\nfn is_topmost_svg_svg_element<E>(e: E) -> bool\nwhere\n    E: TElement,\n{\n    debug_assert!(e.is_svg_element());\n    if e.local_name() != &*atom!(\"svg\") {\n        return false;\n    }\n\n    let parent = match e.traversal_parent() {\n        Some(n) => n,\n        None => return true,\n    };\n\n    if !parent.is_svg_element() {\n        return true;\n    }\n\n    parent.local_name() == &*atom!(\"foreignObject\")\n}\n\n// https://drafts.csswg.org/css-display/#unbox\n#[cfg(feature = \"gecko\")]\nfn is_effective_display_none_for_display_contents<E>(element: E) -> bool\nwhere\n    E: TElement,\n{\n    use crate::Atom;\n\n    const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [\n        atom!(\"br\"),\n        atom!(\"wbr\"),\n        atom!(\"meter\"),\n        atom!(\"progress\"),\n        atom!(\"canvas\"),\n        atom!(\"embed\"),\n        atom!(\"object\"),\n        atom!(\"audio\"),\n        atom!(\"iframe\"),\n        atom!(\"img\"),\n        atom!(\"video\"),\n        atom!(\"frame\"),\n        atom!(\"frameset\"),\n        atom!(\"input\"),\n        atom!(\"textarea\"),\n        atom!(\"select\"),\n    ];\n\n    // https://drafts.csswg.org/css-display/#unbox-svg\n    //\n    // There's a note about \"Unknown elements\", but there's not a good way to\n    // know what that means, or to get that information from here, and no other\n    // UA implements this either.\n    const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [\n        atom!(\"svg\"),\n        atom!(\"a\"),\n        atom!(\"g\"),\n        atom!(\"use\"),\n        atom!(\"tspan\"),\n        atom!(\"textPath\"),\n    ];\n\n    // https://drafts.csswg.org/css-display/#unbox-html\n    if element.is_html_element() {\n        let local_name = element.local_name();\n        return SPECIAL_HTML_ELEMENTS\n            .iter()\n            .any(|name| &**name == local_name);\n    }\n\n    // https://drafts.csswg.org/css-display/#unbox-svg\n    if element.is_svg_element() {\n        if is_topmost_svg_svg_element(element) {\n            return true;\n        }\n        let local_name = element.local_name();\n        return !SPECIAL_SVG_ELEMENTS\n            .iter()\n            .any(|name| &**name == local_name);\n    }\n\n    // https://drafts.csswg.org/css-display/#unbox-mathml\n    if element.is_mathml_element() {\n        return true;\n    }\n\n    false\n}\n\nimpl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {\n    /// Trivially constructs a new StyleAdjuster.\n    #[inline]\n    pub fn new(style: &'a mut StyleBuilder<'b>) -> Self {\n        StyleAdjuster { style }\n    }\n\n    /// <https://fullscreen.spec.whatwg.org/#new-stacking-layer>\n    ///\n    ///    Any position value other than 'absolute' and 'fixed' are\n    ///    computed to 'absolute' if the element is in a top layer.\n    ///\n    fn adjust_for_top_layer(&mut self) {\n        if !self.style.in_top_layer() {\n            return;\n        }\n        if !self.style.is_absolutely_positioned() {\n            self.style.mutate_box().set_position(Position::Absolute);\n        }\n        if self.style.get_box().clone_display().is_contents() {\n            self.style.mutate_box().set_display(Display::Block);\n        }\n    }\n\n    /// -webkit-box with line-clamp and vertical orientation gets turned into\n    /// flow-root at computed-value time.\n    ///\n    /// This makes the element not be a flex container, with all that it\n    /// implies, but it should be safe. It matches blink, see\n    /// https://bugzilla.mozilla.org/show_bug.cgi?id=1786147#c10\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_webkit_line_clamp(&mut self) {\n        use crate::properties::longhands::_moz_box_orient::computed_value::T as BoxOrient;\n        use crate::values::specified::box_::{DisplayInside, DisplayOutside};\n        let box_style = self.style.get_box();\n        if box_style.clone__webkit_line_clamp().is_none() {\n            return;\n        }\n        let disp = box_style.clone_display();\n        if disp.inside() != DisplayInside::WebkitBox {\n            return;\n        }\n        if self.style.get_xul().clone__moz_box_orient() != BoxOrient::Vertical {\n            return;\n        }\n        let new_display = if disp.outside() == DisplayOutside::Block {\n            Display::FlowRoot\n        } else {\n            debug_assert_eq!(disp.outside(), DisplayOutside::Inline);\n            Display::InlineBlock\n        };\n        self.style\n            .mutate_box()\n            .set_adjusted_display(new_display, false);\n    }\n\n    /// CSS 2.1 section 9.7:\n    ///\n    ///    If 'position' has the value 'absolute' or 'fixed', [...] the computed\n    ///    value of 'float' is 'none'.\n    ///\n    fn adjust_for_position(&mut self) {\n        if self.style.is_absolutely_positioned() && self.style.is_floating() {\n            self.style.mutate_box().set_float(Float::None);\n        }\n    }\n\n    /// Whether we should skip any item-based display property blockification on\n    /// this element.\n    fn skip_item_display_fixup<E>(&self, element: Option<E>) -> bool\n    where\n        E: TElement,\n    {\n        if let Some(pseudo) = self.style.pseudo {\n            return pseudo.skip_item_display_fixup();\n        }\n\n        element.is_some_and(|e| e.skip_item_display_fixup())\n    }\n\n    /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.\n    /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>\n    /// A ::marker pseudo-element with 'list-style-position:outside' needs to\n    /// have its 'display' blockified, unless the ::marker is for an inline\n    /// list-item (for which 'list-style-position:outside' behaves as 'inside').\n    /// https://drafts.csswg.org/css-lists-3/#list-style-position-property\n    fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)\n    where\n        E: TElement,\n    {\n        let mut blockify = false;\n        macro_rules! blockify_if {\n            ($if_what:expr) => {\n                if !blockify {\n                    blockify = $if_what;\n                }\n            };\n        }\n\n        blockify_if!(self.style.is_root_element);\n        if !self.skip_item_display_fixup(element) {\n            let parent_display = layout_parent_style.get_box().clone_display();\n            blockify_if!(parent_display.is_item_container());\n        }\n\n        let is_item_or_root = blockify;\n\n        blockify_if!(self.style.is_floating());\n        blockify_if!(self.style.is_absolutely_positioned());\n\n        if !blockify {\n            return;\n        }\n\n        let display = self.style.get_box().clone_display();\n        let blockified_display = display.equivalent_block_display(self.style.is_root_element);\n        if display != blockified_display {\n            self.style\n                .mutate_box()\n                .set_adjusted_display(blockified_display, is_item_or_root);\n        }\n    }\n\n    /// Compute a few common flags for both text and element's style.\n    fn set_bits(&mut self) {\n        let box_style = self.style.get_box();\n        let display = box_style.clone_display();\n\n        if !display.is_contents() {\n            if !self\n                .style\n                .get_text()\n                .clone_text_decoration_line()\n                .is_empty()\n            {\n                self.style\n                    .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES);\n            }\n\n            if self.style.get_effects().clone_opacity() == 0. {\n                self.style\n                    .add_flags(ComputedValueFlags::IS_IN_OPACITY_ZERO_SUBTREE);\n            }\n        } else if self\n            .style\n            .get_parent_box()\n            .clone_display()\n            .is_item_container()\n            || self\n                .style\n                .get_parent_flags()\n                .contains(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER)\n        {\n            self.style\n                .add_flags(ComputedValueFlags::DISPLAY_CONTENTS_IN_ITEM_CONTAINER);\n        }\n\n        if self.style.pseudo.is_some_and(|p| p.is_first_line()) {\n            self.style\n                .add_flags(ComputedValueFlags::IS_IN_FIRST_LINE_SUBTREE);\n        }\n\n        if self.style.is_root_element {\n            self.style\n                .add_flags(ComputedValueFlags::IS_ROOT_ELEMENT_STYLE);\n        }\n\n        #[cfg(feature = \"gecko\")]\n        if box_style\n            .clone_effective_containment()\n            .contains(Contain::STYLE)\n        {\n            self.style\n                .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);\n        }\n\n        if box_style.clone_container_type().is_size_container_type() {\n            self.style\n                .add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);\n        }\n    }\n\n    /// Adjust the style for text style.\n    ///\n    /// The adjustments here are a subset of the adjustments generally, because\n    /// text only inherits properties.\n    ///\n    /// Note that this, for Gecko, comes through Servo_ComputedValues_Inherit.\n    #[cfg(feature = \"gecko\")]\n    pub fn adjust_for_text(&mut self) {\n        debug_assert!(!self.style.is_root_element);\n        self.adjust_for_text_combine_upright();\n        self.adjust_for_text_in_ruby();\n        self.set_bits();\n    }\n\n    /// Change writing mode of the text frame for text-combine-upright.\n    ///\n    /// It is safe to look at our own style because we are looking at inherited\n    /// properties, and text is just plain inheritance.\n    ///\n    /// TODO(emilio): we should (Gecko too) revise these adjustments in presence\n    /// of display: contents.\n    ///\n    /// FIXME(emilio): How does this play with logical properties? Doesn't\n    /// mutating writing-mode change the potential physical sides chosen?\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_text_combine_upright(&mut self) {\n        use crate::computed_values::text_combine_upright::T as TextCombineUpright;\n        use crate::computed_values::writing_mode::T as WritingMode;\n        use crate::logical_geometry;\n\n        let writing_mode = self.style.get_inherited_box().clone_writing_mode();\n        let text_combine_upright = self.style.get_inherited_text().clone_text_combine_upright();\n\n        if matches!(\n            writing_mode,\n            WritingMode::VerticalRl | WritingMode::VerticalLr\n        ) && text_combine_upright == TextCombineUpright::All\n        {\n            self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED);\n            self.style\n                .mutate_inherited_box()\n                .set_writing_mode(WritingMode::HorizontalTb);\n            self.style.writing_mode =\n                logical_geometry::WritingMode::new(self.style.get_inherited_box());\n        }\n    }\n\n    /// Unconditionally propagates the line break suppression flag to text, and\n    /// additionally it applies it if it is in any ruby box.\n    ///\n    /// This is necessary because its parent may not itself have the flag set\n    /// (e.g. ruby or ruby containers), thus we may not inherit the flag from\n    /// them.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_text_in_ruby(&mut self) {\n        let parent_display = self.style.get_parent_box().clone_display();\n        if parent_display.is_ruby_type()\n            || self\n                .style\n                .get_parent_flags()\n                .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)\n        {\n            self.style\n                .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);\n        }\n    }\n\n    /// <https://drafts.csswg.org/css-writing-modes-3/#block-flow:>\n    ///\n    ///    If a box has a different writing-mode value than its containing\n    ///    block:\n    ///\n    ///        - If the box has a specified display of inline, its display\n    ///          computes to inline-block. [CSS21]\n    ///\n    /// This matches the adjustment that Gecko does, not exactly following\n    /// the spec. See also:\n    ///\n    /// <https://lists.w3.org/Archives/Public/www-style/2017Mar/0045.html>\n    /// <https://github.com/servo/servo/issues/15754>\n    fn adjust_for_writing_mode(&mut self, layout_parent_style: &ComputedValues) {\n        let our_writing_mode = self.style.get_inherited_box().clone_writing_mode();\n        let parent_writing_mode = layout_parent_style.get_inherited_box().clone_writing_mode();\n\n        if our_writing_mode != parent_writing_mode\n            && self.style.get_box().clone_display() == Display::Inline\n        {\n            // TODO(emilio): Figure out if we can just set the adjusted display\n            // on Gecko too and unify this code path.\n            if cfg!(feature = \"servo\") {\n                self.style\n                    .mutate_box()\n                    .set_adjusted_display(Display::InlineBlock, false);\n            } else {\n                self.style.mutate_box().set_display(Display::InlineBlock);\n            }\n        }\n    }\n\n    /// CSS overflow-x and overflow-y require some fixup as well in some cases.\n    /// https://drafts.csswg.org/css-overflow-3/#overflow-properties\n    /// \"Computed value: as specified, except with `visible`/`clip` computing to\n    /// `auto`/`hidden` (respectively) if one of `overflow-x` or `overflow-y` is\n    /// neither `visible` nor `clip`.\"\n    fn adjust_for_overflow(&mut self) {\n        let overflow_x = self.style.get_box().clone_overflow_x();\n        let overflow_y = self.style.get_box().clone_overflow_y();\n        if overflow_x == overflow_y {\n            return; // optimization for the common case\n        }\n\n        if overflow_x.is_scrollable() != overflow_y.is_scrollable() {\n            let box_style = self.style.mutate_box();\n            box_style.set_overflow_x(overflow_x.to_scrollable());\n            box_style.set_overflow_y(overflow_y.to_scrollable());\n        }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_contain(&mut self) {\n        let box_style = self.style.get_box();\n        let container_type = box_style.clone_container_type();\n        let content_visibility = box_style.clone_content_visibility();\n        if !container_type.is_size_container_type()\n            && content_visibility == ContentVisibility::Visible\n        {\n            debug_assert_eq!(\n                box_style.clone_contain(),\n                box_style.clone_effective_containment()\n            );\n            return;\n        }\n        let old_contain = box_style.clone_contain();\n        let mut new_contain = old_contain;\n        match content_visibility {\n            ContentVisibility::Visible => {},\n            // `content-visibility:auto` also applies size containment when content\n            // is not relevant (and therefore skipped). This is checked in\n            // nsIFrame::GetContainSizeAxes.\n            ContentVisibility::Auto => {\n                new_contain.insert(Contain::LAYOUT | Contain::PAINT | Contain::STYLE)\n            },\n            ContentVisibility::Hidden => new_contain\n                .insert(Contain::LAYOUT | Contain::PAINT | Contain::SIZE | Contain::STYLE),\n        }\n        if container_type.intersects(ContainerType::INLINE_SIZE) {\n            // https://drafts.csswg.org/css-contain-3/#valdef-container-type-inline-size:\n            //     Applies layout containment, style containment, and inline-size\n            //     containment to the principal box.\n            new_contain.insert(Contain::STYLE | Contain::INLINE_SIZE);\n        } else if container_type.intersects(ContainerType::SIZE) {\n            // https://drafts.csswg.org/css-contain-3/#valdef-container-type-size:\n            //     Applies layout containment, style containment, and size\n            //     containment to the principal box.\n            new_contain.insert(Contain::STYLE | Contain::SIZE);\n        }\n        if new_contain == old_contain {\n            debug_assert_eq!(\n                box_style.clone_contain(),\n                box_style.clone_effective_containment()\n            );\n            return;\n        }\n        self.style\n            .mutate_box()\n            .set_effective_containment(new_contain);\n    }\n\n    /// content-visibility: auto should force contain-intrinsic-size to gain\n    /// an auto value\n    ///\n    /// <https://github.com/w3c/csswg-drafts/issues/8407>\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_contain_intrinsic_size(&mut self) {\n        let content_visibility = self.style.get_box().clone_content_visibility();\n        if content_visibility != ContentVisibility::Auto {\n            return;\n        }\n\n        let pos = self.style.get_position();\n        let new_width = pos.clone_contain_intrinsic_width().add_auto_if_needed();\n        let new_height = pos.clone_contain_intrinsic_height().add_auto_if_needed();\n        if new_width.is_none() && new_height.is_none() {\n            return;\n        }\n\n        let pos = self.style.mutate_position();\n        if let Some(width) = new_width {\n            pos.set_contain_intrinsic_width(width);\n        }\n        if let Some(height) = new_height {\n            pos.set_contain_intrinsic_height(height);\n        }\n    }\n\n    /// Handles the relevant sections in:\n    ///\n    /// https://drafts.csswg.org/css-display/#unbox-html\n    ///\n    /// And forbidding display: contents in pseudo-elements, at least for now.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_prohibited_display_contents<E>(&mut self, element: Option<E>)\n    where\n        E: TElement,\n    {\n        if self.style.get_box().clone_display() != Display::Contents {\n            return;\n        }\n\n        // FIXME(emilio): ::before and ::after should support display: contents, see bug 1418138.\n        if self.style.pseudo.is_some_and(|p| !p.is_element_backed()) {\n            self.style.mutate_box().set_display(Display::Inline);\n            return;\n        }\n\n        let element = match element {\n            Some(e) => e,\n            None => return,\n        };\n\n        if is_effective_display_none_for_display_contents(element) {\n            self.style.mutate_box().set_display(Display::None);\n        }\n    }\n\n    /// <textarea>'s editor root needs to inherit the overflow value from its\n    /// parent, but we need to make sure it's still scrollable.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_text_control_editing_root(&mut self) {\n        use crate::properties::longhands::white_space_collapse::computed_value::T as WhiteSpaceCollapse;\n        use crate::selector_parser::PseudoElement;\n\n        if self.style.pseudo != Some(&PseudoElement::MozTextControlEditingRoot) {\n            return;\n        }\n\n        let old_collapse = self.style.get_inherited_text().clone_white_space_collapse();\n        let new_collapse = match old_collapse {\n            WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces => old_collapse,\n            WhiteSpaceCollapse::Collapse\n            | WhiteSpaceCollapse::PreserveSpaces\n            | WhiteSpaceCollapse::PreserveBreaks => WhiteSpaceCollapse::Preserve,\n        };\n        if new_collapse != old_collapse {\n            self.style\n                .mutate_inherited_text()\n                .set_white_space_collapse(new_collapse);\n        }\n    }\n\n    /// If a <fieldset> has grid/flex display type, we need to inherit\n    /// this type into its ::-moz-fieldset-content anonymous box.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_fieldset_content(&mut self) {\n        use crate::selector_parser::PseudoElement;\n        if self.style.pseudo != Some(&PseudoElement::MozFieldsetContent) {\n            return;\n        }\n        let parent_display = self.style.get_parent_box().clone_display();\n        debug_assert!(\n            !parent_display.is_contents(),\n            \"How did we create a fieldset-content box with display: contents?\"\n        );\n        let new_display = match parent_display {\n            Display::Flex | Display::InlineFlex => Some(Display::Flex),\n            Display::Grid | Display::InlineGrid => Some(Display::Grid),\n            _ => None,\n        };\n        if let Some(new_display) = new_display {\n            self.style.mutate_box().set_display(new_display);\n        }\n    }\n\n    /// -moz-center, -moz-left and -moz-right are used for HTML's alignment.\n    ///\n    /// This is covering the <div align=\"right\"><table>...</table></div> case.\n    ///\n    /// In this case, we don't want to inherit the text alignment into the\n    /// table.\n    fn adjust_for_table_text_align(&mut self) {\n        use crate::properties::longhands::text_align::computed_value::T as TextAlign;\n        if self.style.get_box().clone_display() != Display::Table {\n            return;\n        }\n\n        match self.style.get_inherited_text().clone_text_align() {\n            TextAlign::MozLeft | TextAlign::MozCenter | TextAlign::MozRight => {},\n            _ => return,\n        }\n\n        self.style\n            .mutate_inherited_text()\n            .set_text_align(TextAlign::Start)\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn should_suppress_linebreak<E>(&self, element: Option<E>) -> bool\n    where\n        E: TElement,\n    {\n        // Line break suppression should only be propagated to in-flow children.\n        if self.style.is_floating() || self.style.is_absolutely_positioned() {\n            return false;\n        }\n        let parent_display = self.style.get_parent_box().clone_display();\n        if self\n            .style\n            .get_parent_flags()\n            .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK)\n        {\n            // Line break suppression is propagated to any children of\n            // line participants, and across display: contents boundaries.\n            if parent_display.is_line_participant() || parent_display.is_contents() {\n                return true;\n            }\n        }\n        match self.style.get_box().clone_display() {\n            // Ruby base and text are always non-breakable.\n            Display::RubyBase | Display::RubyText => true,\n            // Ruby base container and text container are breakable.\n            // Non-HTML elements may not form ruby base / text container because\n            // they may not respect ruby-internal display values, so we can't\n            // make them escaped from line break suppression.\n            // Note that, when certain HTML tags, e.g. form controls, have ruby\n            // level container display type, they could also escape from the\n            // line break suppression flag while they shouldn't. However, it is\n            // generally fine as far as they can't break the line inside them.\n            Display::RubyBaseContainer | Display::RubyTextContainer\n                if element.map_or(true, |e| e.is_html_element()) =>\n            {\n                false\n            },\n            // Anything else is non-breakable if and only if its layout parent\n            // has a ruby display type, because any of the ruby boxes can be\n            // anonymous.\n            _ => parent_display.is_ruby_type(),\n        }\n    }\n\n    /// Do ruby-related style adjustments, which include:\n    /// * propagate the line break suppression flag,\n    /// * inlinify block descendants,\n    /// * suppress border and padding for ruby level containers,\n    /// * correct unicode-bidi.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_ruby<E>(&mut self, element: Option<E>)\n    where\n        E: TElement,\n    {\n        use crate::properties::longhands::unicode_bidi::computed_value::T as UnicodeBidi;\n\n        let self_display = self.style.get_box().clone_display();\n        // Check whether line break should be suppressed for this element.\n        if self.should_suppress_linebreak(element) {\n            self.style\n                .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK);\n            // Inlinify the display type if allowed.\n            if !self.skip_item_display_fixup(element) {\n                let inline_display = self_display.inlinify();\n                if self_display != inline_display {\n                    self.style\n                        .mutate_box()\n                        .set_adjusted_display(inline_display, false);\n                }\n            }\n        }\n        // Suppress border and padding for ruby level containers.\n        // This is actually not part of the spec. It is currently unspecified\n        // how border and padding should be handled for ruby level container,\n        // and suppressing them here make it easier for layout to handle.\n        if self_display.is_ruby_level_container() {\n            self.style.reset_border_struct();\n            self.style.reset_padding_struct();\n        }\n\n        // Force bidi isolation on all internal ruby boxes and ruby container\n        // per spec https://drafts.csswg.org/css-ruby-1/#bidi\n        if self_display.is_ruby_type() {\n            let new_value = match self.style.get_text().clone_unicode_bidi() {\n                UnicodeBidi::Normal | UnicodeBidi::Embed => Some(UnicodeBidi::Isolate),\n                UnicodeBidi::BidiOverride => Some(UnicodeBidi::IsolateOverride),\n                _ => None,\n            };\n            if let Some(new_value) = new_value {\n                self.style.mutate_text().set_unicode_bidi(new_value);\n            }\n        }\n    }\n\n    /// Computes the RELEVANT_LINK_VISITED flag based on the parent style and on\n    /// whether we're a relevant link.\n    ///\n    /// NOTE(emilio): We don't do this for text styles, which is... dubious, but\n    /// Gecko doesn't seem to do it either. It's extremely easy to do if needed\n    /// though.\n    ///\n    /// FIXME(emilio): This isn't technically a style adjustment thingie, could\n    /// it move somewhere else?\n    fn adjust_for_visited<E>(&mut self, element: Option<E>)\n    where\n        E: TElement,\n    {\n        if !self.style.has_visited_style() {\n            return;\n        }\n\n        let is_link_element = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_link());\n\n        if !is_link_element {\n            return;\n        }\n\n        if element.unwrap().is_visited_link() {\n            self.style\n                .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);\n        } else {\n            // Need to remove to handle unvisited link inside visited.\n            self.style\n                .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED);\n        }\n    }\n\n    /// Resolves \"justify-items: legacy\" based on the inherited style if needed\n    /// to comply with:\n    ///\n    /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_justify_items(&mut self) {\n        use crate::values::specified::align;\n        let justify_items = self.style.get_position().clone_justify_items();\n        if justify_items.specified != align::JustifyItems::legacy() {\n            return;\n        }\n\n        let parent_justify_items = self.style.get_parent_position().clone_justify_items();\n\n        if !parent_justify_items.computed.contains(AlignFlags::LEGACY) {\n            return;\n        }\n\n        if parent_justify_items.computed == justify_items.computed {\n            return;\n        }\n\n        self.style\n            .mutate_position()\n            .set_computed_justify_items(parent_justify_items.computed);\n    }\n\n    /// If '-webkit-appearance' is 'menulist' on a <select> element then\n    /// the computed value of 'line-height' is 'normal'.\n    ///\n    /// https://github.com/w3c/csswg-drafts/issues/3257\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_appearance<E>(&mut self, element: Option<E>)\n    where\n        E: TElement,\n    {\n        use crate::properties::longhands::appearance::computed_value::T as Appearance;\n        use crate::properties::longhands::line_height::computed_value::T as LineHeight;\n\n        let box_ = self.style.get_box();\n        let appearance = match box_.clone_appearance() {\n            Appearance::Auto => box_.clone__moz_default_appearance(),\n            a => a,\n        };\n\n        if appearance == Appearance::Menulist {\n            if self.style.get_font().clone_line_height() == LineHeight::normal() {\n                return;\n            }\n            if self.style.pseudo.is_some() {\n                return;\n            }\n            let is_html_select_element = element.map_or(false, |e| {\n                e.is_html_element() && e.local_name() == &*atom!(\"select\")\n            });\n            if !is_html_select_element {\n                return;\n            }\n            self.style\n                .mutate_font()\n                .set_line_height(LineHeight::normal());\n        }\n    }\n\n    /// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family'\n    /// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open'\n    /// is assigned 'font-family:-moz-bullet-font'. (This is for <ul><li> etc.)\n    /// We don't want synthesized italic/bold for this font, so turn that off too.\n    /// Likewise for 'letter/word-spacing' -- unless the author specified it then reset\n    /// them to their initial value because traditionally we never added such spacing\n    /// between a legacy bullet and the list item's content, so we keep that behavior\n    /// for web-compat reasons.\n    /// We intentionally don't check 'list-style-image' below since we want it to use\n    /// the same font as its fallback ('list-style-type') in case it fails to load.\n    #[cfg(feature = \"gecko\")]\n    fn adjust_for_marker_pseudo(&mut self, author_specified_properties: &LonghandIdSet) {\n        use crate::values::computed::counters::Content;\n        use crate::values::computed::font::{FontFamily, FontSynthesis, FontSynthesisStyle};\n        use crate::values::computed::text::{LetterSpacing, WordSpacing};\n\n        let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker())\n            && self.style.get_list().clone_list_style_type().is_bullet()\n            && self.style.get_counters().clone_content() == Content::Normal;\n        if !is_legacy_marker {\n            return;\n        }\n        if !author_specified_properties.contains(LonghandId::FontFamily) {\n            self.style\n                .mutate_font()\n                .set_font_family(FontFamily::moz_bullet().clone());\n\n            // FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules.\n            // Then we can add it to the @font-face rule in html.css instead.\n            // https://github.com/w3c/csswg-drafts/issues/6081\n            if !author_specified_properties.contains(LonghandId::FontSynthesisWeight) {\n                self.style\n                    .mutate_font()\n                    .set_font_synthesis_weight(FontSynthesis::None);\n            }\n            if !author_specified_properties.contains(LonghandId::FontSynthesisStyle) {\n                self.style\n                    .mutate_font()\n                    .set_font_synthesis_style(FontSynthesisStyle::None);\n            }\n        }\n        if !author_specified_properties.contains(LonghandId::LetterSpacing) {\n            self.style\n                .mutate_inherited_text()\n                .set_letter_spacing(LetterSpacing::normal());\n        }\n        if !author_specified_properties.contains(LonghandId::WordSpacing) {\n            self.style\n                .mutate_inherited_text()\n                .set_word_spacing(WordSpacing::normal());\n        }\n    }\n\n    /// Performs adjustments for position-try-fallbacks. The properties that need adjustments here\n    /// are luckily not affected by previous adjustments nor by other computed-value-time effects,\n    /// so we can just perform them here.\n    ///\n    /// NOTE(emilio): If we ever perform the interleaving dance, this could / should probably move\n    /// around to the specific properties' to_computed_value implementations, but that seems\n    /// overkill for now.\n    fn adjust_for_try_tactic(&mut self, tactic: &PositionTryFallbacksTryTactic) {\n        debug_assert!(!tactic.is_empty());\n        // TODO: This is supposed to use the containing block's WM (bug 1995256).\n        let wm = self.style.writing_mode;\n        // TODO: Flip inset / margin / sizes percentages and anchor lookup sides as necessary.\n        for tactic in tactic.iter() {\n            use PositionTryFallbacksTryTacticKeyword::*;\n            match tactic {\n                FlipBlock => {\n                    self.flip_self_alignment(/* block = */ true);\n                    self.flip_insets_and_margins(/* horizontal = */ wm.is_vertical());\n                },\n                FlipInline => {\n                    self.flip_self_alignment(/* block = */ false);\n                    self.flip_insets_and_margins(/* horizontal = */ wm.is_horizontal());\n                },\n                FlipX => {\n                    self.flip_self_alignment(/* block = */ wm.is_vertical());\n                    self.flip_insets_and_margins(/* horizontal = */ true);\n                },\n                FlipY => {\n                    self.flip_self_alignment(/* block = */ wm.is_horizontal());\n                    self.flip_insets_and_margins(/* horizontal = */ false);\n                },\n                FlipStart => {\n                    self.flip_start();\n                },\n            }\n            self.apply_position_area_tactic(*tactic);\n        }\n    }\n\n    fn apply_position_area_tactic(&mut self, tactic: PositionTryFallbacksTryTacticKeyword) {\n        let pos = self.style.get_position();\n        let old = pos.clone_position_area();\n        let wm = self.style.writing_mode;\n        let new = old.with_tactic(wm, tactic);\n        if new == old {\n            return;\n        }\n        let pos = self.style.mutate_position();\n        pos.set_position_area(new);\n    }\n\n    // TODO: Could avoid some clones here and below.\n    fn swap_insets(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {\n        debug_assert_ne!(a_side, b_side);\n        let pos = self.style.mutate_position();\n        let mut a = pos.get_inset(a_side).clone();\n        a.try_tactic_adjustment(a_side, b_side);\n        let mut b = pos.get_inset(b_side).clone();\n        b.try_tactic_adjustment(b_side, a_side);\n        pos.set_inset(a_side, b);\n        pos.set_inset(b_side, a);\n    }\n\n    fn swap_margins(&mut self, a_side: PhysicalSide, b_side: PhysicalSide) {\n        debug_assert_ne!(a_side, b_side);\n        let margin = self.style.get_margin();\n        let mut a = margin.get_margin(a_side).clone();\n        a.try_tactic_adjustment(a_side, b_side);\n        let mut b = margin.get_margin(b_side).clone();\n        b.try_tactic_adjustment(b_side, a_side);\n        let margin = self.style.mutate_margin();\n        margin.set_margin(a_side, b);\n        margin.set_margin(b_side, a);\n    }\n\n    fn swap_sizes(&mut self, block_start: PhysicalSide, inline_start: PhysicalSide) {\n        let pos = self.style.mutate_position();\n        let mut min_width = pos.clone_min_width();\n        min_width.try_tactic_adjustment(inline_start, block_start);\n        let mut max_width = pos.clone_max_width();\n        max_width.try_tactic_adjustment(inline_start, block_start);\n        let mut width = pos.clone_width();\n        width.try_tactic_adjustment(inline_start, block_start);\n\n        let mut min_height = pos.clone_min_height();\n        min_height.try_tactic_adjustment(block_start, inline_start);\n        let mut max_height = pos.clone_max_height();\n        max_height.try_tactic_adjustment(block_start, inline_start);\n        let mut height = pos.clone_height();\n        height.try_tactic_adjustment(block_start, inline_start);\n\n        let pos = self.style.mutate_position();\n        pos.set_width(height);\n        pos.set_height(width);\n        pos.set_max_width(max_height);\n        pos.set_max_height(max_width);\n        pos.set_min_width(min_height);\n        pos.set_min_height(min_width);\n    }\n\n    fn flip_start(&mut self) {\n        let wm = self.style.writing_mode;\n        let bs = wm.block_start_physical_side();\n        let is = wm.inline_start_physical_side();\n        let be = wm.block_end_physical_side();\n        let ie = wm.inline_end_physical_side();\n        self.swap_sizes(bs, is);\n        self.swap_insets(bs, is);\n        self.swap_insets(ie, be);\n        self.swap_margins(bs, is);\n        self.swap_margins(ie, be);\n        self.flip_alignment_start();\n    }\n\n    fn flip_insets_and_margins(&mut self, horizontal: bool) {\n        if horizontal {\n            self.swap_insets(PhysicalSide::Left, PhysicalSide::Right);\n            self.swap_margins(PhysicalSide::Left, PhysicalSide::Right);\n        } else {\n            self.swap_insets(PhysicalSide::Top, PhysicalSide::Bottom);\n            self.swap_margins(PhysicalSide::Top, PhysicalSide::Bottom);\n        }\n    }\n\n    fn flip_alignment_start(&mut self) {\n        let pos = self.style.get_position();\n        let align = pos.clone_align_self();\n        let mut justify = pos.clone_justify_self();\n        if align == justify {\n            return;\n        }\n\n        // Fix-up potential justify-self: {left, right} values which might end up as alignment\n        // values.\n        if matches!(justify.value(), AlignFlags::LEFT | AlignFlags::RIGHT) {\n            let left = justify.value() == AlignFlags::LEFT;\n            let ltr = self.style.writing_mode.is_bidi_ltr();\n            justify = justify.with_value(if left == ltr {\n                AlignFlags::SELF_START\n            } else {\n                AlignFlags::SELF_END\n            });\n        }\n\n        let pos = self.style.mutate_position();\n        pos.set_align_self(justify);\n        pos.set_justify_self(align);\n    }\n\n    fn flip_self_alignment(&mut self, block: bool) {\n        let pos = self.style.get_position();\n        let cur = if block {\n            pos.clone_align_self()\n        } else {\n            pos.clone_justify_self()\n        };\n        let flipped = cur.flip_position();\n        if flipped == cur {\n            return;\n        }\n        let pos = self.style.mutate_position();\n        if block {\n            pos.set_align_self(flipped);\n        } else {\n            pos.set_justify_self(flipped);\n        }\n    }\n\n    /// Adjusts the style to account for various fixups that don't fit naturally into the cascade.\n    #[allow(unused_variables)]\n    pub fn adjust<E>(\n        &mut self,\n        layout_parent_style: &ComputedValues,\n        element: Option<E>,\n        try_tactic: &PositionTryFallbacksTryTactic,\n        author_specified_properties: &LonghandIdSet,\n    ) where\n        E: TElement,\n    {\n        if cfg!(debug_assertions) {\n            if let Some(e) = element {\n                if let Some(p) = e.implemented_pseudo_element() {\n                    // It'd be nice to assert `self.style.pseudo == Some(&pseudo)`,\n                    // but we do resolve ::-moz-list pseudos on ::before / ::after\n                    // content, sigh.\n                    debug_assert!(\n                        self.style.pseudo.is_some(),\n                        \"Someone really messed up (no pseudo style for {e:?}, {p:?})\"\n                    );\n                }\n            }\n        }\n        // FIXME(emilio): The apply_declarations callsite in Servo's\n        // animation, and the font stuff for Gecko\n        // (Stylist::compute_for_declarations) should pass an element to\n        // cascade(), then we can make this assertion hold everywhere.\n        // debug_assert!(\n        //     element.is_some() || self.style.pseudo.is_some(),\n        //     \"Should always have an element around for non-pseudo styles\"\n        // );\n\n        self.adjust_for_visited(element);\n        #[cfg(feature = \"gecko\")]\n        {\n            self.adjust_for_prohibited_display_contents(element);\n            self.adjust_for_fieldset_content();\n            self.adjust_for_text_control_editing_root();\n        }\n        self.adjust_for_top_layer();\n        self.blockify_if_necessary(layout_parent_style, element);\n        #[cfg(feature = \"gecko\")]\n        self.adjust_for_webkit_line_clamp();\n        self.adjust_for_position();\n        self.adjust_for_overflow();\n        #[cfg(feature = \"gecko\")]\n        {\n            self.adjust_for_contain();\n            self.adjust_for_contain_intrinsic_size();\n            self.adjust_for_justify_items();\n        }\n        self.adjust_for_table_text_align();\n        self.adjust_for_writing_mode(layout_parent_style);\n        #[cfg(feature = \"gecko\")]\n        {\n            self.adjust_for_ruby(element);\n            self.adjust_for_appearance(element);\n            self.adjust_for_marker_pseudo(author_specified_properties);\n        }\n        if !try_tactic.is_empty() {\n            self.adjust_for_try_tactic(try_tactic);\n        }\n        self.set_bits();\n    }\n}\n"
  },
  {
    "path": "style/style_resolver.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Style resolution for a given element or pseudo-element.\n\nuse crate::applicable_declarations::ApplicableDeclarationList;\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::{CascadeInputs, ElementCascadeInputs, StyleContext};\nuse crate::data::{EagerPseudoStyles, ElementStyles};\nuse crate::dom::TElement;\nuse crate::matching::MatchMethods;\nuse crate::properties::longhands::display::computed_value::T as Display;\nuse crate::properties::{ComputedValues, FirstLineReparenting};\nuse crate::rule_tree::{RuleCascadeFlags, RuleTree, StrongRuleNode};\nuse crate::selector_parser::{PseudoElement, SelectorImpl};\nuse crate::stylist::RuleInclusion;\nuse log::Level::Trace;\nuse selectors::matching::{\n    MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, VisitedHandlingMode,\n};\n#[cfg(feature = \"gecko\")]\nuse selectors::parser::PseudoElement as PseudoElementTrait;\nuse servo_arc::Arc;\n\n/// Whether pseudo-elements should be resolved or not.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum PseudoElementResolution {\n    /// Only resolve pseudo-styles if possibly applicable.\n    IfApplicable,\n    /// Force pseudo-element resolution.\n    Force,\n}\n\n/// A struct that takes care of resolving the style of a given element.\npub struct StyleResolverForElement<'a, 'ctx, 'le, E>\nwhere\n    'ctx: 'a,\n    'le: 'ctx,\n    E: TElement + MatchMethods + 'le,\n{\n    element: E,\n    context: &'a mut StyleContext<'ctx, E>,\n    rule_inclusion: RuleInclusion,\n    pseudo_resolution: PseudoElementResolution,\n    _marker: ::std::marker::PhantomData<&'le E>,\n}\n\nstruct MatchingResults {\n    rule_node: StrongRuleNode,\n    flags: ComputedValueFlags,\n}\n\n/// A style returned from the resolver machinery.\npub struct ResolvedStyle(pub Arc<ComputedValues>);\n\nimpl ResolvedStyle {\n    /// Convenience accessor for the style.\n    #[inline]\n    pub fn style(&self) -> &ComputedValues {\n        &*self.0\n    }\n}\n\n/// The primary style of an element or an element-backed pseudo-element.\npub struct PrimaryStyle {\n    /// The style itself.\n    pub style: ResolvedStyle,\n    /// Whether the style was reused from another element via the rule node (see\n    /// `StyleSharingCache::lookup_by_rules`).\n    pub reused_via_rule_node: bool,\n}\n\n/// A set of style returned from the resolver machinery.\npub struct ResolvedElementStyles {\n    /// Primary style.\n    pub primary: PrimaryStyle,\n    /// Pseudo styles.\n    pub pseudos: EagerPseudoStyles,\n}\n\nimpl ResolvedElementStyles {\n    /// Convenience accessor for the primary style.\n    pub fn primary_style(&self) -> &Arc<ComputedValues> {\n        &self.primary.style.0\n    }\n\n    /// Convenience mutable accessor for the style.\n    pub fn primary_style_mut(&mut self) -> &mut Arc<ComputedValues> {\n        &mut self.primary.style.0\n    }\n}\n\nimpl PrimaryStyle {\n    /// Convenience accessor for the style.\n    pub fn style(&self) -> &ComputedValues {\n        &*self.style.0\n    }\n}\n\nimpl From<ResolvedElementStyles> for ElementStyles {\n    fn from(r: ResolvedElementStyles) -> ElementStyles {\n        ElementStyles {\n            primary: Some(r.primary.style.0),\n            pseudos: r.pseudos,\n        }\n    }\n}\n\npub(crate) fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R\nwhere\n    E: TElement,\n    F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,\n{\n    let parent_el = element.inheritance_parent();\n    let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());\n    let parent_style = parent_data.as_ref().map(|d| d.styles.primary());\n\n    let mut layout_parent_el = parent_el.clone();\n    let layout_parent_data;\n    let mut layout_parent_style = parent_style;\n    if parent_style.map_or(false, |s| s.is_display_contents()) {\n        layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());\n        layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();\n        layout_parent_style = Some(layout_parent_data.styles.primary());\n    }\n\n    f(\n        parent_style.map(|x| &**x),\n        layout_parent_style.map(|s| &**s),\n    )\n}\n\nfn layout_parent_style_for_pseudo<'a>(\n    primary_style: &'a PrimaryStyle,\n    layout_parent_style: Option<&'a ComputedValues>,\n) -> Option<&'a ComputedValues> {\n    if primary_style.style().is_display_contents() {\n        layout_parent_style\n    } else {\n        Some(primary_style.style())\n    }\n}\n\nfn eager_pseudo_is_definitely_not_generated(\n    pseudo: &PseudoElement,\n    style: &ComputedValues,\n) -> bool {\n    if !pseudo.is_before_or_after() {\n        return false;\n    }\n\n    if style\n        .flags\n        .intersects(ComputedValueFlags::DISPLAY_OR_CONTENT_DEPEND_ON_INHERITED_STYLE)\n    {\n        return false;\n    }\n\n    style.get_box().clone_display() == Display::None || style.ineffective_content_property()\n}\n\nimpl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>\nwhere\n    'ctx: 'a,\n    'le: 'ctx,\n    E: TElement + MatchMethods + 'le,\n{\n    /// Trivially construct a new StyleResolverForElement.\n    pub fn new(\n        element: E,\n        context: &'a mut StyleContext<'ctx, E>,\n        rule_inclusion: RuleInclusion,\n        pseudo_resolution: PseudoElementResolution,\n    ) -> Self {\n        Self {\n            element,\n            context,\n            rule_inclusion,\n            pseudo_resolution,\n            _marker: ::std::marker::PhantomData,\n        }\n    }\n\n    /// Resolve just the style of a given element.\n    pub fn resolve_primary_style(\n        &mut self,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n    ) -> PrimaryStyle {\n        let primary_results = self.match_primary(VisitedHandlingMode::AllLinksUnvisited);\n\n        let inside_link = parent_style.map_or(false, |s| s.visited_style().is_some());\n\n        let visited_rules = if self.context.shared.visited_styles_enabled\n            && (inside_link || self.element.is_link())\n        {\n            let visited_matching_results =\n                self.match_primary(VisitedHandlingMode::RelevantLinkVisited);\n            Some(visited_matching_results.rule_node)\n        } else {\n            None\n        };\n\n        self.cascade_primary_style(\n            CascadeInputs {\n                rules: Some(primary_results.rule_node),\n                visited_rules,\n                flags: primary_results.flags,\n                included_cascade_flags: RuleCascadeFlags::empty(),\n            },\n            parent_style,\n            layout_parent_style,\n        )\n    }\n\n    fn cascade_primary_style(\n        &mut self,\n        inputs: CascadeInputs,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n    ) -> PrimaryStyle {\n        // Before doing the cascade, check the sharing cache and see if we can\n        // reuse the style via rule node identity.\n        let may_reuse = self.element.matches_user_and_content_rules()\n            && parent_style.is_some()\n            && inputs.rules.is_some()\n            && inputs.included_cascade_flags.is_empty();\n\n        if may_reuse {\n            let cached = self.context.thread_local.sharing_cache.lookup_by_rules(\n                self.context.shared,\n                parent_style.unwrap(),\n                &inputs,\n                self.element,\n            );\n            if let Some(mut primary_style) = cached {\n                self.context.thread_local.statistics.styles_reused += 1;\n                primary_style.reused_via_rule_node |= true;\n                return primary_style;\n            }\n        }\n\n        // No style to reuse. Cascade the style, starting with visited style\n        // if necessary.\n        PrimaryStyle {\n            style: self.cascade_style_and_visited(\n                inputs,\n                parent_style,\n                layout_parent_style,\n                /* pseudo = */ None,\n            ),\n            reused_via_rule_node: false,\n        }\n    }\n\n    /// Resolve the style of a given element, and all its eager pseudo-elements.\n    pub fn resolve_style(\n        &mut self,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n    ) -> ResolvedElementStyles {\n        let primary_style = self.resolve_primary_style(parent_style, layout_parent_style);\n\n        let mut pseudo_styles = EagerPseudoStyles::default();\n\n        if !self\n            .element\n            .implemented_pseudo_element()\n            .is_some_and(|p| !p.is_element_backed())\n        {\n            let layout_parent_style_for_pseudo =\n                layout_parent_style_for_pseudo(&primary_style, layout_parent_style);\n            SelectorImpl::each_eagerly_cascaded_pseudo_element(|pseudo| {\n                let pseudo_style = self.resolve_pseudo_style(\n                    &pseudo,\n                    &primary_style,\n                    layout_parent_style_for_pseudo,\n                );\n\n                if let Some(style) = pseudo_style {\n                    if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)\n                        && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)\n                    {\n                        return;\n                    }\n                    pseudo_styles.set(&pseudo, style.0);\n                }\n            })\n        }\n\n        ResolvedElementStyles {\n            primary: primary_style,\n            pseudos: pseudo_styles,\n        }\n    }\n\n    /// Resolve an element's styles with the default inheritance parent/layout\n    /// parents.\n    pub fn resolve_style_with_default_parents(&mut self) -> ResolvedElementStyles {\n        with_default_parent_styles(self.element, |parent_style, layout_parent_style| {\n            self.resolve_style(parent_style, layout_parent_style)\n        })\n    }\n\n    /// Cascade a set of rules, using the default parent for inheritance.\n    pub fn cascade_style_and_visited_with_default_parents(\n        &mut self,\n        inputs: CascadeInputs,\n    ) -> ResolvedStyle {\n        with_default_parent_styles(self.element, |parent_style, layout_parent_style| {\n            self.cascade_style_and_visited(\n                inputs,\n                parent_style,\n                layout_parent_style,\n                /* pseudo = */ None,\n            )\n        })\n    }\n\n    /// Cascade a set of rules for pseudo element, using the default parent for inheritance.\n    pub fn cascade_style_and_visited_for_pseudo_with_default_parents(\n        &mut self,\n        inputs: CascadeInputs,\n        pseudo: &PseudoElement,\n        primary_style: &PrimaryStyle,\n    ) -> ResolvedStyle {\n        with_default_parent_styles(self.element, |_, layout_parent_style| {\n            let layout_parent_style_for_pseudo =\n                layout_parent_style_for_pseudo(primary_style, layout_parent_style);\n\n            self.cascade_style_and_visited(\n                inputs,\n                Some(primary_style.style()),\n                layout_parent_style_for_pseudo,\n                Some(pseudo),\n            )\n        })\n    }\n\n    fn cascade_style_and_visited(\n        &mut self,\n        inputs: CascadeInputs,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n        pseudo: Option<&PseudoElement>,\n    ) -> ResolvedStyle {\n        debug_assert!(pseudo.map_or(true, |p| p.is_eager()));\n\n        let mut conditions = Default::default();\n        let values = self.context.shared.stylist.cascade_style_and_visited(\n            Some(self.element),\n            pseudo,\n            &inputs,\n            &self.context.shared.guards,\n            parent_style,\n            layout_parent_style,\n            FirstLineReparenting::No,\n            /* try_tactic = */ &Default::default(),\n            Some(&self.context.thread_local.rule_cache),\n            &mut conditions,\n        );\n\n        self.context.thread_local.rule_cache.insert_if_possible(\n            &self.context.shared.guards,\n            &values,\n            pseudo,\n            &inputs,\n            &conditions,\n        );\n\n        ResolvedStyle(values)\n    }\n\n    /// Cascade the element and pseudo-element styles with the default parents.\n    pub fn cascade_styles_with_default_parents(\n        &mut self,\n        inputs: ElementCascadeInputs,\n    ) -> ResolvedElementStyles {\n        with_default_parent_styles(self.element, move |parent_style, layout_parent_style| {\n            let primary_style =\n                self.cascade_primary_style(inputs.primary, parent_style, layout_parent_style);\n\n            let mut pseudo_styles = EagerPseudoStyles::default();\n            if let Some(mut pseudo_array) = inputs.pseudos.into_array() {\n                let layout_parent_style_for_pseudo = if primary_style.style().is_display_contents()\n                {\n                    layout_parent_style\n                } else {\n                    Some(primary_style.style())\n                };\n\n                for (i, inputs) in pseudo_array.iter_mut().enumerate() {\n                    if let Some(inputs) = inputs.take() {\n                        let pseudo = PseudoElement::from_eager_index(i);\n\n                        let style = self.cascade_style_and_visited(\n                            inputs,\n                            Some(primary_style.style()),\n                            layout_parent_style_for_pseudo,\n                            Some(&pseudo),\n                        );\n\n                        if !matches!(self.pseudo_resolution, PseudoElementResolution::Force)\n                            && eager_pseudo_is_definitely_not_generated(&pseudo, &style.0)\n                        {\n                            continue;\n                        }\n\n                        pseudo_styles.set(&pseudo, style.0);\n                    }\n                }\n            }\n\n            ResolvedElementStyles {\n                primary: primary_style,\n                pseudos: pseudo_styles,\n            }\n        })\n    }\n\n    fn resolve_pseudo_style(\n        &mut self,\n        pseudo: &PseudoElement,\n        originating_element_style: &PrimaryStyle,\n        layout_parent_style: Option<&ComputedValues>,\n    ) -> Option<ResolvedStyle> {\n        let MatchingResults {\n            rule_node,\n            mut flags,\n        } = self.match_pseudo(\n            &originating_element_style.style.0,\n            pseudo,\n            VisitedHandlingMode::AllLinksUnvisited,\n        )?;\n\n        let mut visited_rules = None;\n        if originating_element_style.style().visited_style().is_some() {\n            visited_rules = self\n                .match_pseudo(\n                    &originating_element_style.style.0,\n                    pseudo,\n                    VisitedHandlingMode::RelevantLinkVisited,\n                )\n                .map(|results| {\n                    flags |= results.flags;\n                    results.rule_node\n                });\n        }\n\n        Some(self.cascade_style_and_visited(\n            CascadeInputs {\n                rules: Some(rule_node),\n                visited_rules,\n                flags,\n                included_cascade_flags: RuleCascadeFlags::empty(),\n            },\n            Some(originating_element_style.style()),\n            layout_parent_style,\n            Some(pseudo),\n        ))\n    }\n\n    fn match_primary(&mut self, visited_handling: VisitedHandlingMode) -> MatchingResults {\n        debug!(\n            \"Match primary for {:?}, visited: {:?}\",\n            self.element, visited_handling\n        );\n        let mut applicable_declarations = ApplicableDeclarationList::new();\n\n        let bloom_filter = self.context.thread_local.bloom_filter.filter();\n        let selector_caches = &mut self.context.thread_local.selector_caches;\n        let mut matching_context = MatchingContext::new_for_visited(\n            MatchingMode::Normal,\n            Some(bloom_filter),\n            selector_caches,\n            visited_handling,\n            self.context.shared.quirks_mode(),\n            NeedsSelectorFlags::Yes,\n            MatchingForInvalidation::No,\n        );\n\n        let stylist = &self.context.shared.stylist;\n        // Compute the primary rule node.\n        stylist.push_applicable_declarations(\n            self.element,\n            None,\n            self.element.style_attribute(),\n            self.element.smil_override(),\n            self.element.animation_declarations(self.context.shared),\n            self.rule_inclusion,\n            &mut applicable_declarations,\n            &mut matching_context,\n        );\n\n        // FIXME(emilio): This is a hack for animations, and should go away.\n        self.element.unset_dirty_style_attribute();\n\n        let rule_node = stylist\n            .rule_tree()\n            .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);\n\n        if log_enabled!(Trace) {\n            trace!(\"Matched rules for {:?}:\", self.element);\n            for rn in rule_node.self_and_ancestors() {\n                let source = rn.style_source();\n                if source.is_some() {\n                    trace!(\" > {:?}\", source);\n                }\n            }\n        }\n\n        MatchingResults {\n            rule_node,\n            flags: matching_context.extra_data.cascade_input_flags,\n        }\n    }\n\n    fn match_pseudo(\n        &mut self,\n        originating_element_style: &ComputedValues,\n        pseudo_element: &PseudoElement,\n        visited_handling: VisitedHandlingMode,\n    ) -> Option<MatchingResults> {\n        debug!(\n            \"Match pseudo {:?} for {:?}, visited: {:?}\",\n            self.element, pseudo_element, visited_handling\n        );\n        debug_assert!(pseudo_element.is_eager());\n\n        let mut applicable_declarations = ApplicableDeclarationList::new();\n\n        let stylist = &self.context.shared.stylist;\n\n        if !self\n            .element\n            .may_generate_pseudo(pseudo_element, originating_element_style)\n        {\n            return None;\n        }\n\n        let bloom_filter = self.context.thread_local.bloom_filter.filter();\n        let selector_caches = &mut self.context.thread_local.selector_caches;\n\n        let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(\n            MatchingMode::ForStatelessPseudoElement,\n            Some(bloom_filter),\n            selector_caches,\n            visited_handling,\n            self.context.shared.quirks_mode(),\n            NeedsSelectorFlags::Yes,\n            MatchingForInvalidation::No,\n        );\n        matching_context.extra_data.originating_element_style = Some(originating_element_style);\n\n        // NB: We handle animation rules for ::before and ::after when\n        // traversing them.\n        stylist.push_applicable_declarations(\n            self.element,\n            Some(pseudo_element),\n            None,\n            None,\n            /* animation_declarations = */ Default::default(),\n            self.rule_inclusion,\n            &mut applicable_declarations,\n            &mut matching_context,\n        );\n\n        if applicable_declarations.is_empty() {\n            return None;\n        }\n\n        let rule_node = stylist\n            .rule_tree()\n            .compute_rule_node(&mut applicable_declarations, &self.context.shared.guards);\n\n        Some(MatchingResults {\n            rule_node,\n            flags: matching_context.extra_data.cascade_input_flags,\n        })\n    }\n\n    /// Resolve the starting style by recascading with @starting-style rules included, similar to\n    /// how after_change_style works.\n    pub fn resolve_starting_style(\n        &mut self,\n        primary_style: &Arc<ComputedValues>,\n    ) -> Option<ResolvedStyle> {\n        if !RuleTree::has_starting_style(primary_style.rules()) {\n            return None;\n        }\n        let inputs = CascadeInputs {\n            rules: Some(primary_style.rules().clone()),\n            visited_rules: primary_style.visited_rules().cloned(),\n            flags: primary_style.flags.for_cascade_inputs(),\n            included_cascade_flags: RuleCascadeFlags::STARTING_STYLE,\n        };\n        Some(self.cascade_style_and_visited_with_default_parents(inputs))\n    }\n\n    /// If there is no transition rule in the ComputedValues, it returns None.\n    pub fn after_change_style(\n        &mut self,\n        primary_style: &Arc<ComputedValues>,\n    ) -> Option<Arc<ComputedValues>> {\n        let rule_node = primary_style.rules();\n        let without_transition_rules = RuleTree::remove_transition_rule_if_applicable(rule_node);\n        if without_transition_rules == *rule_node {\n            // We don't have transition rule in this case, so return None to let\n            // the caller use the original ComputedValues.\n            return None;\n        }\n\n        // FIXME(bug 868975): We probably need to transition visited style as well.\n        let inputs = CascadeInputs {\n            rules: Some(without_transition_rules),\n            visited_rules: primary_style.visited_rules().cloned(),\n            flags: primary_style.flags.for_cascade_inputs(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n        };\n\n        let style = self.cascade_style_and_visited_with_default_parents(inputs);\n        Some(style.0)\n    }\n}\n"
  },
  {
    "path": "style/stylesheet_set.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A centralized set of stylesheets for a document.\n\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};\nuse crate::shared_lock::SharedRwLockReadGuard;\nuse crate::stylesheets::{\n    CssRule, CssRuleRef, CustomMediaMap, Origin, OriginSet, PerOrigin, StylesheetInDocument,\n};\nuse std::mem;\n\n/// Entry for a StylesheetSet.\n#[derive(MallocSizeOf)]\nstruct StylesheetSetEntry<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The sheet.\n    sheet: S,\n\n    /// Whether this sheet has been part of at least one flush.\n    committed: bool,\n}\n\nimpl<S> StylesheetSetEntry<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    fn new(sheet: S) -> Self {\n        Self {\n            sheet,\n            committed: false,\n        }\n    }\n}\n\n/// The validity of the data in a given cascade origin.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]\npub enum DataValidity {\n    /// The origin is clean, all the data already there is valid, though we may\n    /// have new sheets at the end.\n    Valid = 0,\n\n    /// The cascade data is invalid, but not the invalidation data (which is\n    /// order-independent), and thus only the cascade data should be inserted.\n    CascadeInvalid = 1,\n\n    /// Everything needs to be rebuilt.\n    FullyInvalid = 2,\n}\n\nimpl Default for DataValidity {\n    fn default() -> Self {\n        DataValidity::Valid\n    }\n}\n\n/// A struct to iterate over the different stylesheets to be flushed.\npub struct DocumentStylesheetFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    collections: &'a mut PerOrigin<SheetCollection<S>>,\n}\n\n/// The type of rebuild that we need to do for a given stylesheet.\n#[derive(Clone, Copy, Debug)]\npub enum SheetRebuildKind {\n    /// A full rebuild, of both cascade data and invalidation data.\n    Full,\n    /// A partial rebuild, of only the cascade data.\n    CascadeOnly,\n}\n\nimpl SheetRebuildKind {\n    /// Whether the stylesheet invalidation data should be rebuilt.\n    pub fn should_rebuild_invalidation(&self) -> bool {\n        matches!(*self, SheetRebuildKind::Full)\n    }\n}\n\nimpl<'a, S> DocumentStylesheetFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Returns a flusher for `origin`.\n    pub fn flush_origin(&mut self, origin: Origin) -> SheetCollectionFlusher<'_, S> {\n        self.collections.borrow_mut_for_origin(&origin).flush()\n    }\n\n    /// Returns the list of stylesheets for `origin`.\n    ///\n    /// Only used for UA sheets.\n    pub fn origin_sheets(&self, origin: Origin) -> impl Iterator<Item = &S> {\n        self.collections.borrow_for_origin(&origin).iter()\n    }\n}\n\n/// A flusher struct for a given collection, that takes care of returning the\n/// appropriate stylesheets that need work.\npub struct SheetCollectionFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    // TODO: This can be made an iterator again once\n    // https://github.com/rust-lang/rust/pull/82771 lands on stable.\n    entries: &'a mut [StylesheetSetEntry<S>],\n    validity: DataValidity,\n    dirty: bool,\n}\n\nimpl<'a, S> SheetCollectionFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Whether the collection was originally dirty.\n    #[inline]\n    pub fn dirty(&self) -> bool {\n        self.dirty\n    }\n\n    /// What the state of the sheet data is.\n    #[inline]\n    pub fn data_validity(&self) -> DataValidity {\n        self.validity\n    }\n\n    /// Returns an iterator over the remaining list of sheets to consume.\n    pub fn sheets<'b>(&'b self) -> impl Iterator<Item = &'b S> {\n        self.entries.iter().map(|entry| &entry.sheet)\n    }\n}\n\nimpl<'a, S> SheetCollectionFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Iterates over all sheets and values that we have to invalidate.\n    ///\n    /// TODO(emilio): This would be nicer as an iterator but we can't do that\n    /// until https://github.com/rust-lang/rust/pull/82771 stabilizes.\n    ///\n    /// Since we don't have a good use-case for partial iteration, this does the\n    /// trick for now.\n    pub fn each(self, mut callback: impl FnMut(usize, &S, SheetRebuildKind) -> bool) {\n        for (index, potential_sheet) in self.entries.iter_mut().enumerate() {\n            let committed = mem::replace(&mut potential_sheet.committed, true);\n            let rebuild_kind = if !committed {\n                // If the sheet was uncommitted, we need to do a full rebuild\n                // anyway.\n                SheetRebuildKind::Full\n            } else {\n                match self.validity {\n                    DataValidity::Valid => continue,\n                    DataValidity::CascadeInvalid => SheetRebuildKind::CascadeOnly,\n                    DataValidity::FullyInvalid => SheetRebuildKind::Full,\n                }\n            };\n\n            if !callback(index, &potential_sheet.sheet, rebuild_kind) {\n                return;\n            }\n        }\n    }\n}\n\n#[derive(MallocSizeOf)]\nstruct SheetCollection<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The actual list of stylesheets.\n    ///\n    /// This is only a list of top-level stylesheets, and as such it doesn't\n    /// include recursive `@import` rules.\n    entries: Vec<StylesheetSetEntry<S>>,\n\n    /// The validity of the data that was already there for a given origin.\n    ///\n    /// Note that an origin may appear on `origins_dirty`, but still have\n    /// `DataValidity::Valid`, if only sheets have been appended into it (in\n    /// which case the existing data is valid, but the origin needs to be\n    /// rebuilt).\n    data_validity: DataValidity,\n\n    /// Whether anything in the collection has changed. Note that this is\n    /// different from `data_validity`, in the sense that after a sheet append,\n    /// the data validity is still `Valid`, but we need to be marked as dirty.\n    dirty: bool,\n}\n\nimpl<S> Default for SheetCollection<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    fn default() -> Self {\n        Self {\n            entries: vec![],\n            data_validity: DataValidity::Valid,\n            dirty: false,\n        }\n    }\n}\n\nimpl<S> SheetCollection<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Returns the number of stylesheets in the set.\n    fn len(&self) -> usize {\n        self.entries.len()\n    }\n\n    /// Returns the `index`th stylesheet in the set if present.\n    fn get(&self, index: usize) -> Option<&S> {\n        self.entries.get(index).map(|e| &e.sheet)\n    }\n\n    fn find_sheet_index(&self, sheet: &S) -> Option<usize> {\n        let rev_pos = self\n            .entries\n            .iter()\n            .rev()\n            .position(|entry| entry.sheet == *sheet);\n        rev_pos.map(|i| self.entries.len() - i - 1)\n    }\n\n    fn remove(&mut self, sheet: &S) {\n        let index = self.find_sheet_index(sheet);\n        if cfg!(feature = \"gecko\") && index.is_none() {\n            // FIXME(emilio): Make Gecko's PresShell::AddUserSheet not suck.\n            return;\n        }\n        let sheet = self.entries.remove(index.unwrap());\n        // Removing sheets makes us tear down the whole cascade and invalidation\n        // data, but only if the sheet has been involved in at least one flush.\n        // Checking whether the sheet has been committed allows us to avoid\n        // rebuilding the world when sites quickly append and remove a\n        // stylesheet.\n        //\n        // See bug 1434756.\n        if sheet.committed {\n            self.set_data_validity_at_least(DataValidity::FullyInvalid);\n        } else {\n            self.dirty = true;\n        }\n    }\n\n    fn contains(&self, sheet: &S) -> bool {\n        self.entries.iter().any(|e| e.sheet == *sheet)\n    }\n\n    /// Appends a given sheet into the collection.\n    fn append(&mut self, sheet: S) {\n        debug_assert!(!self.contains(&sheet));\n        self.entries.push(StylesheetSetEntry::new(sheet));\n        // Appending sheets doesn't alter the validity of the existing data, so\n        // we don't need to change `data_validity` here.\n        //\n        // But we need to be marked as dirty, otherwise we'll never add the new\n        // sheet!\n        self.dirty = true;\n    }\n\n    fn insert_before(&mut self, sheet: S, before_sheet: &S) {\n        debug_assert!(!self.contains(&sheet));\n\n        let index = self\n            .find_sheet_index(before_sheet)\n            .expect(\"`before_sheet` stylesheet not found\");\n\n        // Inserting stylesheets somewhere but at the end changes the validity\n        // of the cascade data, but not the invalidation data.\n        self.set_data_validity_at_least(DataValidity::CascadeInvalid);\n        self.entries.insert(index, StylesheetSetEntry::new(sheet));\n    }\n\n    fn set_data_validity_at_least(&mut self, validity: DataValidity) {\n        use std::cmp;\n\n        debug_assert_ne!(validity, DataValidity::Valid);\n\n        self.dirty = true;\n        self.data_validity = cmp::max(validity, self.data_validity);\n    }\n\n    /// Returns an iterator over the current list of stylesheets.\n    fn iter(&self) -> impl Iterator<Item = &S> {\n        self.entries.iter().map(|e| &e.sheet)\n    }\n\n    /// Returns a mutable iterator over the current list of stylesheets.\n    fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {\n        self.entries.iter_mut().map(|e| &mut e.sheet)\n    }\n\n    fn flush(&mut self) -> SheetCollectionFlusher<'_, S> {\n        let dirty = mem::replace(&mut self.dirty, false);\n        let validity = mem::replace(&mut self.data_validity, DataValidity::Valid);\n\n        SheetCollectionFlusher {\n            entries: &mut self.entries,\n            dirty,\n            validity,\n        }\n    }\n}\n\n/// The set of stylesheets effective for a given document.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct DocumentStylesheetSet<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The collections of sheets per each origin.\n    collections: PerOrigin<SheetCollection<S>>,\n\n    /// The invalidations for stylesheets added or removed from this document.\n    invalidations: StylesheetInvalidationSet,\n}\n\n/// This macro defines methods common to DocumentStylesheetSet and\n/// AuthorStylesheetSet.\n///\n/// We could simplify the setup moving invalidations to SheetCollection, but\n/// that would imply not sharing invalidations across origins of the same\n/// documents, which is slightly annoying.\nmacro_rules! sheet_set_methods {\n    ($set_name:expr) => {\n        fn collect_invalidations_for(\n            &mut self,\n            device: Option<&Device>,\n            custom_media: &CustomMediaMap,\n            sheet: &S,\n            guard: &SharedRwLockReadGuard,\n        ) {\n            if let Some(device) = device {\n                self.invalidations\n                    .collect_invalidations_for(device, custom_media, sheet, guard);\n            }\n        }\n\n        /// Appends a new stylesheet to the current set.\n        ///\n        /// No device implies not computing invalidations.\n        pub fn append_stylesheet(\n            &mut self,\n            device: Option<&Device>,\n            custom_media: &CustomMediaMap,\n            sheet: S,\n            guard: &SharedRwLockReadGuard,\n        ) {\n            debug!(concat!($set_name, \"::append_stylesheet\"));\n            self.collect_invalidations_for(device, custom_media, &sheet, guard);\n            let collection = self.collection_for(&sheet, guard);\n            collection.append(sheet);\n        }\n\n        /// Insert a given stylesheet before another stylesheet in the document.\n        pub fn insert_stylesheet_before(\n            &mut self,\n            device: Option<&Device>,\n            custom_media: &CustomMediaMap,\n            sheet: S,\n            before_sheet: S,\n            guard: &SharedRwLockReadGuard,\n        ) {\n            debug!(concat!($set_name, \"::insert_stylesheet_before\"));\n            self.collect_invalidations_for(device, custom_media, &sheet, guard);\n\n            let collection = self.collection_for(&sheet, guard);\n            collection.insert_before(sheet, &before_sheet);\n        }\n\n        /// Remove a given stylesheet from the set.\n        pub fn remove_stylesheet(\n            &mut self,\n            device: Option<&Device>,\n            custom_media: &CustomMediaMap,\n            sheet: S,\n            guard: &SharedRwLockReadGuard,\n        ) {\n            debug!(concat!($set_name, \"::remove_stylesheet\"));\n            self.collect_invalidations_for(device, custom_media, &sheet, guard);\n\n            let collection = self.collection_for(&sheet, guard);\n            collection.remove(&sheet)\n        }\n\n        /// Notify the set that a rule from a given stylesheet has changed\n        /// somehow.\n        pub fn rule_changed(\n            &mut self,\n            device: Option<&Device>,\n            custom_media: &CustomMediaMap,\n            sheet: &S,\n            rule: &CssRule,\n            guard: &SharedRwLockReadGuard,\n            change_kind: RuleChangeKind,\n            ancestors: &[CssRuleRef],\n        ) {\n            if let Some(device) = device {\n                let quirks_mode = device.quirks_mode();\n                self.invalidations.rule_changed(\n                    sheet,\n                    rule,\n                    guard,\n                    device,\n                    quirks_mode,\n                    custom_media,\n                    change_kind,\n                    ancestors,\n                );\n            }\n\n            let validity = match change_kind {\n                // Insertion / Removals need to rebuild both the cascade and\n                // invalidation data. For generic changes this is conservative,\n                // could be optimized on a per-case basis.\n                RuleChangeKind::Generic | RuleChangeKind::Insertion | RuleChangeKind::Removal => {\n                    DataValidity::FullyInvalid\n                },\n                // TODO(emilio): This, in theory, doesn't need to invalidate\n                // style data, if the rule we're modifying is actually in the\n                // CascadeData already.\n                //\n                // But this is actually a bit tricky to prove, because when we\n                // copy-on-write a stylesheet we don't bother doing a rebuild,\n                // so we may still have rules from the original stylesheet\n                // instead of the cloned one that we're modifying. So don't\n                // bother for now and unconditionally rebuild, it's no worse\n                // than what we were already doing anyway.\n                //\n                // Maybe we could record whether we saw a clone in this flush,\n                // and if so do the conservative thing, otherwise just\n                // early-return.\n                RuleChangeKind::PositionTryDeclarations | RuleChangeKind::StyleRuleDeclarations => {\n                    DataValidity::FullyInvalid\n                },\n            };\n\n            let collection = self.collection_for(&sheet, guard);\n            collection.set_data_validity_at_least(validity);\n        }\n    };\n}\n\nimpl<S> DocumentStylesheetSet<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Create a new empty DocumentStylesheetSet.\n    pub fn new() -> Self {\n        Self {\n            collections: Default::default(),\n            invalidations: StylesheetInvalidationSet::new(),\n        }\n    }\n\n    fn collection_for(\n        &mut self,\n        sheet: &S,\n        guard: &SharedRwLockReadGuard,\n    ) -> &mut SheetCollection<S> {\n        let origin = sheet.contents(guard).origin;\n        self.collections.borrow_mut_for_origin(&origin)\n    }\n\n    sheet_set_methods!(\"DocumentStylesheetSet\");\n\n    /// Returns the number of stylesheets in the set.\n    pub fn len(&self) -> usize {\n        self.collections\n            .iter_origins()\n            .fold(0, |s, (item, _)| s + item.len())\n    }\n\n    /// Returns the count of stylesheets for a given origin.\n    #[inline]\n    pub fn sheet_count(&self, origin: Origin) -> usize {\n        self.collections.borrow_for_origin(&origin).len()\n    }\n\n    /// Returns the `index`th stylesheet in the set for the given origin.\n    #[inline]\n    pub fn get(&self, origin: Origin, index: usize) -> Option<&S> {\n        self.collections.borrow_for_origin(&origin).get(index)\n    }\n\n    /// Returns whether the given set has changed from the last flush.\n    pub fn has_changed(&self) -> bool {\n        !self.invalidations.is_empty()\n            || self\n                .collections\n                .iter_origins()\n                .any(|(collection, _)| collection.dirty)\n    }\n\n    /// Flush the current set, unmarking it as dirty, and returns a `DocumentStylesheetFlusher` in\n    /// order to rebuild the stylist and the invalidation set.\n    pub fn flush(&mut self) -> (DocumentStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {\n        debug!(\"DocumentStylesheetSet::flush\");\n        (\n            DocumentStylesheetFlusher {\n                collections: &mut self.collections,\n            },\n            std::mem::take(&mut self.invalidations),\n        )\n    }\n\n    /// Flush stylesheets, but without running any of the invalidation passes.\n    #[cfg(feature = \"servo\")]\n    pub fn flush_without_invalidation(&mut self) -> OriginSet {\n        debug!(\"DocumentStylesheetSet::flush_without_invalidation\");\n\n        let mut origins = OriginSet::empty();\n        std::mem::take(&mut self.invalidations);\n\n        for (collection, origin) in self.collections.iter_mut_origins() {\n            if collection.flush().dirty() {\n                origins |= origin;\n            }\n        }\n\n        origins\n    }\n\n    /// Return an iterator over the flattened view of all the stylesheets.\n    pub fn iter(&self) -> impl Iterator<Item = (&S, Origin)> {\n        self.collections\n            .iter_origins()\n            .flat_map(|(c, o)| c.iter().map(move |s| (s, o)))\n    }\n\n    /// Return an iterator over the flattened view of all the stylesheets, mutably.\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&mut S, Origin)> {\n        self.collections\n            .iter_mut_origins()\n            .flat_map(|(c, o)| c.iter_mut().map(move |s| (s, o)))\n    }\n\n    /// Mark the stylesheets for the specified origin as dirty, because\n    /// something external may have invalidated it.\n    pub fn force_dirty(&mut self, origins: OriginSet) {\n        self.invalidations.invalidate_fully();\n        for origin in origins.iter_origins() {\n            // We don't know what happened, assume the worse.\n            self.collections\n                .borrow_mut_for_origin(&origin)\n                .set_data_validity_at_least(DataValidity::FullyInvalid);\n        }\n    }\n}\n\n/// The set of stylesheets effective for a given Shadow Root.\n#[derive(MallocSizeOf)]\npub struct AuthorStylesheetSet<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The actual style sheets.\n    collection: SheetCollection<S>,\n    /// The set of invalidations scheduled for this collection.\n    invalidations: StylesheetInvalidationSet,\n}\n\n/// A struct to flush an author style sheet collection.\npub struct AuthorStylesheetFlusher<'a, S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// The actual flusher for the collection.\n    pub sheets: SheetCollectionFlusher<'a, S>,\n}\n\nimpl<S> AuthorStylesheetSet<S>\nwhere\n    S: StylesheetInDocument + PartialEq + 'static,\n{\n    /// Create a new empty AuthorStylesheetSet.\n    #[inline]\n    pub fn new() -> Self {\n        Self {\n            collection: Default::default(),\n            invalidations: StylesheetInvalidationSet::new(),\n        }\n    }\n\n    /// Whether anything has changed since the last time this was flushed.\n    pub fn dirty(&self) -> bool {\n        self.collection.dirty\n    }\n\n    /// Whether the collection is empty.\n    pub fn is_empty(&self) -> bool {\n        self.collection.len() == 0\n    }\n\n    /// Returns the `index`th stylesheet in the collection of author styles if present.\n    pub fn get(&self, index: usize) -> Option<&S> {\n        self.collection.get(index)\n    }\n\n    /// Returns the number of author stylesheets.\n    pub fn len(&self) -> usize {\n        self.collection.len()\n    }\n\n    fn collection_for(&mut self, _: &S, _: &SharedRwLockReadGuard) -> &mut SheetCollection<S> {\n        &mut self.collection\n    }\n\n    sheet_set_methods!(\"AuthorStylesheetSet\");\n\n    /// Iterate over the list of stylesheets.\n    pub fn iter(&self) -> impl Iterator<Item = &S> {\n        self.collection.iter()\n    }\n\n    /// Returns a mutable iterator over the current list of stylesheets.\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut S> {\n        self.collection.iter_mut()\n    }\n\n    /// Mark the sheet set dirty, as appropriate.\n    pub fn force_dirty(&mut self) {\n        self.invalidations.invalidate_fully();\n        self.collection\n            .set_data_validity_at_least(DataValidity::FullyInvalid);\n    }\n\n    /// Flush the stylesheets for this author set.\n    pub fn flush(&mut self) -> (AuthorStylesheetFlusher<'_, S>, StylesheetInvalidationSet) {\n        (\n            AuthorStylesheetFlusher {\n                sheets: self.collection.flush(),\n            },\n            std::mem::take(&mut self.invalidations),\n        )\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/appearance_base_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The `@appearance-base` rule, for UA stylesheet appearance-dependent styles.\n\nuse crate::derives::*;\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::CssRules;\nuse cssparser::SourceLocation;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::CssStringWriter;\n\n/// An `@appearance-base` rule.\n#[derive(Debug, ToShmem)]\npub struct AppearanceBaseRule {\n    /// The nested rules inside this @appearance-base rule.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl AppearanceBaseRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl ToCssWithGuard for AppearanceBaseRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@appearance-base\")?;\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for AppearanceBaseRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        AppearanceBaseRule {\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/container_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@container`][container] rule.\n//!\n//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule\n\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::derives::*;\nuse crate::dom::{AttributeTracker, TElement};\nuse crate::logical_geometry::{LogicalSize, WritingMode};\nuse crate::parser::ParserContext;\nuse crate::properties::ComputedValues;\nuse crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};\nuse crate::queries::values::Orientation;\nuse crate::queries::{FeatureType, QueryCondition};\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse crate::stylesheets::{CssRules, CustomMediaEvaluator};\nuse crate::stylist::Stylist;\nuse crate::values::computed::{CSSPixelLength, ContainerType, Context, Ratio};\nuse crate::values::specified::ContainerName;\nuse app_units::Au;\nuse cssparser::{Parser, SourceLocation};\nuse euclid::default::Size2D;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse selectors::kleene_value::KleeneValue;\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::arc_slice::ArcSlice;\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// A container rule.\n#[derive(Debug, ToShmem)]\npub struct ContainerRule {\n    /// The container queries and name.\n    pub conditions: ContainerConditions,\n    /// The nested rules inside the block.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl ContainerRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl DeepCloneWithLock for ContainerRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        Self {\n            conditions: self.conditions.clone(),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for ContainerRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@container \")?;\n        {\n            let mut writer = CssWriter::new(dest);\n            self.conditions.to_css(&mut writer)?;\n        }\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\n/// Contains all container conditions for a container rule.\n///\n/// https://drafts.csswg.org/css-conditional-5/#container-rule\n#[derive(Clone, Debug, ToCss, ToShmem)]\n#[css(comma)]\npub struct ContainerConditions(#[css(iterable)] pub ArcSlice<ContainerCondition>);\n\n/// A container condition and filter, combined.\n#[derive(Debug, ToShmem, ToCss)]\npub struct ContainerCondition {\n    #[css(skip_if = \"ContainerName::is_none\")]\n    name: ContainerName,\n    condition: Option<QueryCondition>,\n    #[css(skip)]\n    flags: FeatureFlags,\n}\n\n/// The result of a successful container query lookup.\npub struct ContainerLookupResult<E> {\n    /// The relevant container.\n    pub element: E,\n    /// The sizing / writing-mode information of the container.\n    pub info: ContainerInfo,\n    /// The style of the element.\n    pub style: Arc<ComputedValues>,\n}\n\nfn container_type_axes(ty_: ContainerType, wm: WritingMode) -> FeatureFlags {\n    if ty_.intersects(ContainerType::SIZE) {\n        FeatureFlags::all_container_axes()\n    } else if ty_.intersects(ContainerType::INLINE_SIZE) {\n        let physical_axis = if wm.is_vertical() {\n            FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS\n        } else {\n            FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS\n        };\n        FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS | physical_axis\n    } else {\n        FeatureFlags::empty()\n    }\n}\n\nenum TraversalResult<T> {\n    InProgress,\n    StopTraversal,\n    Done(T),\n}\n\nfn traverse_container<E, F, R>(\n    mut e: E,\n    originating_element_style: Option<&ComputedValues>,\n    evaluator: F,\n) -> Option<(E, R)>\nwhere\n    E: TElement,\n    F: Fn(E, Option<&ComputedValues>) -> TraversalResult<R>,\n{\n    if originating_element_style.is_some() {\n        match evaluator(e, originating_element_style) {\n            TraversalResult::InProgress => {},\n            TraversalResult::StopTraversal => return None,\n            TraversalResult::Done(result) => return Some((e, result)),\n        }\n    }\n    while let Some(element) = e.traversal_parent() {\n        match evaluator(element, None) {\n            TraversalResult::InProgress => {},\n            TraversalResult::StopTraversal => return None,\n            TraversalResult::Done(result) => return Some((element, result)),\n        }\n        e = element;\n    }\n\n    None\n}\n\nimpl ContainerCondition {\n    /// Get the name of this condition.\n    #[inline]\n    pub fn name(&self) -> &ContainerName {\n        &self.name\n    }\n    /// Get the query condition of this condition\n    #[inline]\n    pub fn query_condition(&self) -> Option<&QueryCondition> {\n        self.condition.as_ref()\n    }\n    /// Parse a container condition.\n    pub fn parse<'a>(\n        context: &ParserContext,\n        input: &mut Parser<'a, '_>,\n    ) -> Result<Self, ParseError<'a>> {\n        let name = input\n            .try_parse(|input| ContainerName::parse_for_query(context, input))\n            .ok()\n            .unwrap_or_else(ContainerName::none);\n        let condition = input\n            .try_parse(|input| QueryCondition::parse(context, input, FeatureType::Container))\n            .ok();\n        if condition.is_none() && name.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let flags = condition\n            .as_ref()\n            .map_or(FeatureFlags::empty(), |c| c.cumulative_flags());\n        Ok(Self {\n            name,\n            condition,\n            flags,\n        })\n    }\n\n    fn valid_container_info<E>(\n        &self,\n        potential_container: E,\n        originating_element_style: Option<&ComputedValues>,\n    ) -> TraversalResult<ContainerLookupResult<E>>\n    where\n        E: TElement,\n    {\n        let data;\n        let style = match originating_element_style {\n            Some(s) => s,\n            None => {\n                data = match potential_container.borrow_data() {\n                    Some(d) => d,\n                    None => return TraversalResult::InProgress,\n                };\n                &**data.styles.primary()\n            },\n        };\n        let wm = style.writing_mode;\n        let box_style = style.get_box();\n\n        // Filter by container-type.\n        let container_type = box_style.clone_container_type();\n        let available_axes = container_type_axes(container_type, wm);\n        if !available_axes.contains(self.flags.container_axes()) {\n            return TraversalResult::InProgress;\n        }\n\n        // Filter by container-name.\n        let container_name = box_style.clone_container_name();\n        for filter_name in self.name.0.iter() {\n            if !container_name.0.contains(filter_name) {\n                return TraversalResult::InProgress;\n            }\n        }\n\n        let size = potential_container.query_container_size(&box_style.clone_display());\n        let style = style.to_arc();\n        TraversalResult::Done(ContainerLookupResult {\n            element: potential_container,\n            info: ContainerInfo {\n                size,\n                wm,\n                inherited_style: {\n                    potential_container.traversal_parent().and_then(|parent| {\n                        parent\n                            .borrow_data()\n                            .and_then(|data| data.styles.get_primary().cloned())\n                    })\n                },\n            },\n            style,\n        })\n    }\n\n    /// Performs container lookup for a given element.\n    pub fn find_container<E>(\n        &self,\n        e: E,\n        originating_element_style: Option<&ComputedValues>,\n    ) -> Option<ContainerLookupResult<E>>\n    where\n        E: TElement,\n    {\n        match traverse_container(\n            e,\n            originating_element_style,\n            |element, originating_element_style| {\n                self.valid_container_info(element, originating_element_style)\n            },\n        ) {\n            Some((_, result)) => Some(result),\n            None => None,\n        }\n    }\n\n    /// Tries to match a container query condition for a given element.\n    pub fn matches<E>(\n        &self,\n        stylist: &Stylist,\n        element: E,\n        originating_element_style: Option<&ComputedValues>,\n        invalidation_flags: &mut ComputedValueFlags,\n    ) -> KleeneValue\n    where\n        E: TElement,\n    {\n        let result = self.find_container(element, originating_element_style);\n        let condition = match self.condition {\n            Some(ref c) => c,\n            None => {\n                // Condition-less container query (name only): matches if a\n                // named container was found.\n                return KleeneValue::from(result.is_some());\n            },\n        };\n        // We have to tag the invalidation flags here because style container\n        // query matching may return early if we cannot find a suitable\n        // container element right now. However, we must also consider the case\n        // when an ancestor becomes a container and we have to invalidate this\n        // element from not matching to matching.\n        if self.flags.contains(FeatureFlags::STYLE) {\n            invalidation_flags.insert(ComputedValueFlags::DEPENDS_ON_CONTAINER_STYLE_QUERY);\n        }\n        let (container, info) = match result {\n            Some(r) => (r.element, (r.info, r.style)),\n            None => {\n                // If we did not find the named (or any) container,\n                // the query must fail to match.\n                return KleeneValue::False;\n            },\n        };\n        // Set up the lookup for the container in question, as the condition may be using container\n        // query lengths.\n        let size_query_container_lookup = ContainerSizeQuery::for_element(\n            container, /* known_parent_style = */ None, /* is_pseudo = */ false,\n        );\n        let mut attribute_tracker = AttributeTracker::new(&container);\n        Context::for_container_query_evaluation(\n            stylist.device(),\n            Some(stylist),\n            Some(info),\n            size_query_container_lookup,\n            |context| {\n                let matches = condition.matches(\n                    context,\n                    &mut CustomMediaEvaluator::none(),\n                    &mut attribute_tracker,\n                );\n                let flags = context.style().flags();\n                if flags.contains(ComputedValueFlags::USES_VIEWPORT_UNITS) {\n                    // TODO(emilio): Might need something similar to improve\n                    // invalidation of font relative container-query lengths.\n                    invalidation_flags\n                        .insert(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES);\n                }\n                if flags.contains(ComputedValueFlags::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY) {\n                    invalidation_flags\n                        .insert(ComputedValueFlags::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY)\n                }\n                matches\n            },\n        )\n    }\n}\n\n/// Information needed to evaluate an individual container query.\n#[derive(Clone)]\npub struct ContainerInfo {\n    size: Size2D<Option<Au>>,\n    wm: WritingMode,\n    inherited_style: Option<Arc<ComputedValues>>,\n}\n\nimpl ContainerInfo {\n    fn size(&self) -> Option<Size2D<Au>> {\n        Some(Size2D::new(self.size.width?, self.size.height?))\n    }\n\n    /// Get a reference to the container's inherited style, if any.\n    pub fn inherited_style(&self) -> Option<&ComputedValues> {\n        self.inherited_style.as_deref()\n    }\n}\n\nfn eval_width(context: &Context) -> Option<CSSPixelLength> {\n    let info = context.container_info.as_ref()?;\n    Some(CSSPixelLength::new(info.size.width?.to_f32_px()))\n}\n\nfn eval_height(context: &Context) -> Option<CSSPixelLength> {\n    let info = context.container_info.as_ref()?;\n    Some(CSSPixelLength::new(info.size.height?.to_f32_px()))\n}\n\nfn eval_inline_size(context: &Context) -> Option<CSSPixelLength> {\n    let info = context.container_info.as_ref()?;\n    Some(CSSPixelLength::new(\n        LogicalSize::from_physical(info.wm, info.size)\n            .inline?\n            .to_f32_px(),\n    ))\n}\n\nfn eval_block_size(context: &Context) -> Option<CSSPixelLength> {\n    let info = context.container_info.as_ref()?;\n    Some(CSSPixelLength::new(\n        LogicalSize::from_physical(info.wm, info.size)\n            .block?\n            .to_f32_px(),\n    ))\n}\n\nfn eval_aspect_ratio(context: &Context) -> Option<Ratio> {\n    let info = context.container_info.as_ref()?;\n    Some(Ratio::new(\n        info.size.width?.0 as f32,\n        info.size.height?.0 as f32,\n    ))\n}\n\nfn eval_orientation(context: &Context, value: Option<Orientation>) -> KleeneValue {\n    let size = match context.container_info.as_ref().and_then(|info| info.size()) {\n        Some(size) => size,\n        None => return KleeneValue::Unknown,\n    };\n    KleeneValue::from(Orientation::eval(size, value))\n}\n\n/// https://drafts.csswg.org/css-contain-3/#container-features\n///\n/// TODO: Support style queries, perhaps.\npub static CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [\n    feature!(\n        atom!(\"width\"),\n        AllowsRanges::Yes,\n        Evaluator::OptionalLength(eval_width),\n        FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS,\n    ),\n    feature!(\n        atom!(\"height\"),\n        AllowsRanges::Yes,\n        Evaluator::OptionalLength(eval_height),\n        FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS,\n    ),\n    feature!(\n        atom!(\"inline-size\"),\n        AllowsRanges::Yes,\n        Evaluator::OptionalLength(eval_inline_size),\n        FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS,\n    ),\n    feature!(\n        atom!(\"block-size\"),\n        AllowsRanges::Yes,\n        Evaluator::OptionalLength(eval_block_size),\n        FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS,\n    ),\n    feature!(\n        atom!(\"aspect-ratio\"),\n        AllowsRanges::Yes,\n        Evaluator::OptionalNumberRatio(eval_aspect_ratio),\n        // XXX from_bits_truncate is const, but the pipe operator isn't, so this\n        // works around it.\n        FeatureFlags::from_bits_truncate(\n            FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits()\n                | FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()\n        ),\n    ),\n    feature!(\n        atom!(\"orientation\"),\n        AllowsRanges::No,\n        keyword_evaluator!(eval_orientation, Orientation),\n        FeatureFlags::from_bits_truncate(\n            FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits()\n                | FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()\n        ),\n    ),\n];\n\n/// Result of a container size query, signifying the hypothetical containment boundary in terms of physical axes.\n/// Defined by up to two size containers. Queries on logical axes are resolved with respect to the querying\n/// element's writing mode.\n#[derive(Copy, Clone, Default)]\npub struct ContainerSizeQueryResult {\n    width: Option<Au>,\n    height: Option<Au>,\n}\n\nimpl ContainerSizeQueryResult {\n    fn get_viewport_size(context: &Context) -> Size2D<Au> {\n        use crate::values::specified::ViewportVariant;\n        context.viewport_size_for_viewport_unit_resolution(ViewportVariant::Small)\n    }\n\n    fn get_logical_viewport_size(context: &Context) -> LogicalSize<Au> {\n        LogicalSize::from_physical(\n            context.builder.writing_mode,\n            Self::get_viewport_size(context),\n        )\n    }\n\n    /// Get the inline-size of the query container.\n    pub fn get_container_inline_size(&self, context: &Context) -> Au {\n        if context.builder.writing_mode.is_horizontal() {\n            if let Some(w) = self.width {\n                return w;\n            }\n        } else {\n            if let Some(h) = self.height {\n                return h;\n            }\n        }\n        Self::get_logical_viewport_size(context).inline\n    }\n\n    /// Get the block-size of the query container.\n    pub fn get_container_block_size(&self, context: &Context) -> Au {\n        if context.builder.writing_mode.is_horizontal() {\n            self.get_container_height(context)\n        } else {\n            self.get_container_width(context)\n        }\n    }\n\n    /// Get the width of the query container.\n    pub fn get_container_width(&self, context: &Context) -> Au {\n        if let Some(w) = self.width {\n            return w;\n        }\n        Self::get_viewport_size(context).width\n    }\n\n    /// Get the height of the query container.\n    pub fn get_container_height(&self, context: &Context) -> Au {\n        if let Some(h) = self.height {\n            return h;\n        }\n        Self::get_viewport_size(context).height\n    }\n\n    // Merge the result of a subsequent lookup, preferring the initial result.\n    fn merge(self, new_result: Self) -> Self {\n        let mut result = self;\n        if let Some(width) = new_result.width {\n            result.width.get_or_insert(width);\n        }\n        if let Some(height) = new_result.height {\n            result.height.get_or_insert(height);\n        }\n        result\n    }\n\n    fn is_complete(&self) -> bool {\n        self.width.is_some() && self.height.is_some()\n    }\n}\n\n/// Unevaluated lazy container size query.\npub enum ContainerSizeQuery<'a> {\n    /// Query prior to evaluation.\n    NotEvaluated(Box<dyn Fn() -> ContainerSizeQueryResult + 'a>),\n    /// Cached evaluated result.\n    Evaluated(ContainerSizeQueryResult),\n}\n\nimpl<'a> ContainerSizeQuery<'a> {\n    fn evaluate_potential_size_container<E>(\n        e: E,\n        originating_element_style: Option<&ComputedValues>,\n    ) -> TraversalResult<ContainerSizeQueryResult>\n    where\n        E: TElement,\n    {\n        let data;\n        let style = match originating_element_style {\n            Some(s) => s,\n            None => {\n                data = match e.borrow_data() {\n                    Some(d) => d,\n                    None => return TraversalResult::InProgress,\n                };\n                &**data.styles.primary()\n            },\n        };\n        if !style\n            .flags\n            .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)\n        {\n            // We know we won't find a size container.\n            return TraversalResult::StopTraversal;\n        }\n\n        let wm = style.writing_mode;\n        let box_style = style.get_box();\n\n        let container_type = box_style.clone_container_type();\n        let size = e.query_container_size(&box_style.clone_display());\n        if container_type.intersects(ContainerType::SIZE) {\n            TraversalResult::Done(ContainerSizeQueryResult {\n                width: size.width,\n                height: size.height,\n            })\n        } else if container_type.intersects(ContainerType::INLINE_SIZE) {\n            if wm.is_horizontal() {\n                TraversalResult::Done(ContainerSizeQueryResult {\n                    width: size.width,\n                    height: None,\n                })\n            } else {\n                TraversalResult::Done(ContainerSizeQueryResult {\n                    width: None,\n                    height: size.height,\n                })\n            }\n        } else {\n            TraversalResult::InProgress\n        }\n    }\n\n    /// Find the query container size for a given element. Meant to be used as a callback for new().\n    fn lookup<E>(\n        element: E,\n        originating_element_style: Option<&ComputedValues>,\n    ) -> ContainerSizeQueryResult\n    where\n        E: TElement + 'a,\n    {\n        match traverse_container(\n            element,\n            originating_element_style,\n            |e, originating_element_style| {\n                Self::evaluate_potential_size_container(e, originating_element_style)\n            },\n        ) {\n            Some((container, result)) => {\n                if result.is_complete() {\n                    result\n                } else {\n                    // Traverse up from the found size container to see if we can get a complete containment.\n                    result.merge(Self::lookup(container, None))\n                }\n            },\n            None => ContainerSizeQueryResult::default(),\n        }\n    }\n\n    /// Create a new instance of the container size query for given element, with a deferred lookup callback.\n    pub fn for_element<E>(\n        element: E,\n        known_parent_style: Option<&'a ComputedValues>,\n        is_pseudo: bool,\n    ) -> Self\n    where\n        E: TElement + 'a,\n    {\n        let parent;\n        let data;\n        let parent_style = match known_parent_style {\n            Some(s) => Some(s),\n            None => {\n                // No need to bother if we're the top element.\n                parent = match element.traversal_parent() {\n                    Some(parent) => parent,\n                    None => return Self::none(),\n                };\n                data = parent.borrow_data();\n                data.as_ref().map(|data| &**data.styles.primary())\n            },\n        };\n\n        // If there's no style, such as being `display: none` or so, we still want to show a\n        // correct computed value, so give it a try.\n        let should_traverse = parent_style.map_or(true, |s| {\n            s.flags\n                .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE)\n        });\n        if !should_traverse {\n            return Self::none();\n        }\n        return Self::NotEvaluated(Box::new(move || {\n            Self::lookup(element, if is_pseudo { known_parent_style } else { None })\n        }));\n    }\n\n    /// Create a new instance, but with optional element.\n    pub fn for_option_element<E>(\n        element: Option<E>,\n        known_parent_style: Option<&'a ComputedValues>,\n        is_pseudo: bool,\n    ) -> Self\n    where\n        E: TElement + 'a,\n    {\n        if let Some(e) = element {\n            Self::for_element(e, known_parent_style, is_pseudo)\n        } else {\n            Self::none()\n        }\n    }\n\n    /// Create a query that evaluates to empty, for cases where container size query is not required.\n    pub fn none() -> Self {\n        ContainerSizeQuery::Evaluated(ContainerSizeQueryResult::default())\n    }\n\n    /// Get the result of the container size query, doing the lookup if called for the first time.\n    pub fn get(&mut self) -> ContainerSizeQueryResult {\n        match self {\n            Self::NotEvaluated(lookup) => {\n                *self = Self::Evaluated((lookup)());\n                match self {\n                    Self::Evaluated(info) => *info,\n                    _ => unreachable!(\"Just evaluated but not set?\"),\n                }\n            },\n            Self::Evaluated(info) => *info,\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/counter_style_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(missing_docs)]\n\npub use crate::counter_style::CounterStyleRule;\n"
  },
  {
    "path": "style/stylesheets/document_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)\n//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.\n//! We implement the prefixed `@-moz-document`.\n\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::CssRules;\nuse crate::values::CssUrl;\nuse cssparser::{match_ignore_ascii_case, BasicParseErrorKind, Parser, SourceLocation};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n#[derive(Debug, ToShmem)]\n/// A @-moz-document rule\npub struct DocumentRule {\n    /// The parsed condition\n    pub condition: DocumentCondition,\n    /// Child rules\n    pub rules: Arc<Locked<CssRules>>,\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl DocumentRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl ToCssWithGuard for DocumentRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@-moz-document \")?;\n        self.condition.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" {\")?;\n        for rule in self.rules.read_with(guard).0.iter() {\n            dest.write_char(' ')?;\n            rule.to_css(guard, dest)?;\n        }\n        dest.write_str(\" }\")\n    }\n}\n\nimpl DeepCloneWithLock for DocumentRule {\n    /// Deep clones this DocumentRule.\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        DocumentRule {\n            condition: self.condition.clone(),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// The kind of media document that the rule will match.\n#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss, ToShmem)]\n#[allow(missing_docs)]\npub enum MediaDocumentKind {\n    All,\n    Image,\n    Video,\n}\n\n/// A matching function for a `@document` rule's condition.\n#[derive(Clone, Debug, ToCss, ToShmem)]\npub enum DocumentMatchingFunction {\n    /// Exact URL matching function. It evaluates to true whenever the\n    /// URL of the document being styled is exactly the URL given.\n    Url(CssUrl),\n    /// URL prefix matching function. It evaluates to true whenever the\n    /// URL of the document being styled has the argument to the\n    /// function as an initial substring (which is true when the two\n    /// strings are equal). When the argument is the empty string,\n    /// it evaluates to true for all documents.\n    #[css(function)]\n    UrlPrefix(String),\n    /// Domain matching function. It evaluates to true whenever the URL\n    /// of the document being styled has a host subcomponent and that\n    /// host subcomponent is exactly the argument to the ‘domain()’\n    /// function or a final substring of the host component is a\n    /// period (U+002E) immediately followed by the argument to the\n    /// ‘domain()’ function.\n    #[css(function)]\n    Domain(String),\n    /// Regular expression matching function. It evaluates to true\n    /// whenever the regular expression matches the entirety of the URL\n    /// of the document being styled.\n    #[css(function)]\n    Regexp(String),\n    /// Matching function for a media document.\n    #[css(function)]\n    MediaDocument(MediaDocumentKind),\n    /// Matching function for a plain-text document.\n    #[css(function)]\n    PlainTextDocument(()),\n    /// Matching function for a document that can be observed by other content\n    /// documents.\n    #[css(function)]\n    UnobservableDocument(()),\n}\n\nmacro_rules! parse_quoted_or_unquoted_string {\n    ($input:ident, $url_matching_function:expr) => {\n        $input.parse_nested_block(|input| {\n            let start = input.position();\n            input\n                .parse_entirely(|input| {\n                    let string = input.expect_string()?;\n                    Ok($url_matching_function(string.as_ref().to_owned()))\n                })\n                .or_else(|_: ParseError| {\n                    while let Ok(_) = input.next() {}\n                    Ok($url_matching_function(input.slice_from(start).to_string()))\n                })\n        })\n    };\n}\n\nimpl DocumentMatchingFunction {\n    /// Parse a URL matching function for a`@document` rule's condition.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(url) = input.try_parse(|input| CssUrl::parse(context, input)) {\n            return Ok(DocumentMatchingFunction::Url(url));\n        }\n\n        let location = input.current_source_location();\n        let function = input.expect_function()?.clone();\n        match_ignore_ascii_case! { &function,\n            \"url-prefix\" => {\n                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix)\n            },\n            \"domain\" => {\n                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain)\n            },\n            \"regexp\" => {\n                input.parse_nested_block(|input| {\n                    Ok(DocumentMatchingFunction::Regexp(\n                        input.expect_string()?.as_ref().to_owned(),\n                    ))\n                })\n            },\n            \"media-document\" => {\n                input.parse_nested_block(|input| {\n                    let kind = MediaDocumentKind::parse(input)?;\n                    Ok(DocumentMatchingFunction::MediaDocument(kind))\n                })\n            },\n\n            \"plain-text-document\" => {\n                input.parse_nested_block(|input| {\n                    input.expect_exhausted()?;\n                    Ok(DocumentMatchingFunction::PlainTextDocument(()))\n                })\n            },\n\n            \"unobservable-document\" => {\n                input.parse_nested_block(|input| {\n                    input.expect_exhausted()?;\n                    Ok(DocumentMatchingFunction::UnobservableDocument(()))\n                })\n            },\n\n            _ => {\n                Err(location.new_custom_error(\n                    StyleParseErrorKind::UnexpectedFunction(function.clone())\n                ))\n            },\n        }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    /// Evaluate a URL matching function.\n    pub fn evaluate(&self, device: &Device) -> bool {\n        use crate::gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;\n        use crate::gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction;\n        use nsstring::nsCStr;\n\n        let func = match *self {\n            DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL,\n            DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix,\n            DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain,\n            DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp,\n            DocumentMatchingFunction::MediaDocument(_) => {\n                GeckoDocumentMatchingFunction::MediaDocument\n            },\n            DocumentMatchingFunction::PlainTextDocument(..) => {\n                GeckoDocumentMatchingFunction::PlainTextDocument\n            },\n            DocumentMatchingFunction::UnobservableDocument(..) => {\n                GeckoDocumentMatchingFunction::UnobservableDocument\n            },\n        };\n\n        let pattern = nsCStr::from(match *self {\n            DocumentMatchingFunction::Url(ref url) => url.as_str(),\n            DocumentMatchingFunction::UrlPrefix(ref pat)\n            | DocumentMatchingFunction::Domain(ref pat)\n            | DocumentMatchingFunction::Regexp(ref pat) => pat,\n            DocumentMatchingFunction::MediaDocument(kind) => match kind {\n                MediaDocumentKind::All => \"all\",\n                MediaDocumentKind::Image => \"image\",\n                MediaDocumentKind::Video => \"video\",\n            },\n            DocumentMatchingFunction::PlainTextDocument(())\n            | DocumentMatchingFunction::UnobservableDocument(()) => \"\",\n        });\n        unsafe { Gecko_DocumentRule_UseForPresentation(device.document(), &*pattern, func) }\n    }\n\n    #[cfg(not(feature = \"gecko\"))]\n    /// Evaluate a URL matching function.\n    pub fn evaluate(&self, _: &Device) -> bool {\n        false\n    }\n}\n\n/// A `@document` rule's condition.\n///\n/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>\n///\n/// The `@document` rule's condition is written as a comma-separated list of\n/// URL matching functions, and the condition evaluates to true whenever any\n/// one of those functions evaluates to true.\n#[derive(Clone, Debug, ToCss, ToShmem)]\n#[css(comma)]\npub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>);\n\nimpl DocumentCondition {\n    /// Parse a document condition.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let conditions =\n            input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?;\n\n        let condition = DocumentCondition(conditions);\n        if !condition.allowed_in(context) {\n            return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(\"-moz-document\".into())));\n        }\n        Ok(condition)\n    }\n\n    /// Evaluate a document condition.\n    pub fn evaluate(&self, device: &Device) -> bool {\n        self.0\n            .iter()\n            .any(|url_matching_function| url_matching_function.evaluate(device))\n    }\n\n    #[cfg(feature = \"servo\")]\n    fn allowed_in(&self, _: &ParserContext) -> bool {\n        false\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn allowed_in(&self, context: &ParserContext) -> bool {\n        if context.chrome_rules_enabled() {\n            return true;\n        }\n\n        // Allow a single url-prefix() for compatibility.\n        //\n        // See bug 1446470 and dependencies.\n        if self.0.len() != 1 {\n            return false;\n        }\n\n        // NOTE(emilio): This technically allows url-prefix(\"\") too, but...\n        match self.0[0] {\n            DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),\n            _ => false,\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/font_face_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(missing_docs)]\n\npub use crate::font_face::FontFaceRule;\n"
  },
  {
    "path": "style/stylesheets/font_feature_values_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@font-feature-values`][font-feature-values] at-rule.\n//!\n//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet};\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::CssRuleType;\nuse crate::values::computed::font::FamilyName;\nuse crate::values::serialize_atom_identifier;\nuse crate::Atom;\nuse cssparser::{\n    match_ignore_ascii_case, AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser,\n    Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,\n    Token,\n};\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n#[cfg(feature = \"gecko\")]\nuse thin_vec::ThinVec;\n\n/// A @font-feature-values block declaration.\n/// It is `<ident>: <integer>+`.\n/// This struct can take 3 value types.\n/// - `SingleValue` is to keep just one unsigned integer value.\n/// - `PairValues` is to keep one or two unsigned integer values.\n/// - `VectorValues` is to keep a list of unsigned integer values.\n#[derive(Clone, Debug, PartialEq, ToShmem)]\npub struct FFVDeclaration<T> {\n    /// An `<ident>` for declaration name.\n    pub name: Atom,\n    /// An `<integer>+` for declaration value.\n    pub value: T,\n}\n\nimpl<T: ToCss> ToCss for FFVDeclaration<T> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.name, dest)?;\n        dest.write_str(\": \")?;\n        self.value.to_css(dest)?;\n        dest.write_char(';')\n    }\n}\n\n/// A trait for @font-feature-values rule to gecko values conversion.\n#[cfg(feature = \"gecko\")]\npub trait ToGeckoFontFeatureValues {\n    /// Sets the equivalent of declaration to gecko `ThinVec<u32>` array.\n    fn to_gecko_font_feature_values(&self) -> ThinVec<u32>;\n}\n\n/// A @font-feature-values block declaration value that keeps one value.\n#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]\npub struct SingleValue(pub u32);\n\nimpl Parse for SingleValue {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<SingleValue, ParseError<'i>> {\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::Number {\n                int_value: Some(v), ..\n            } if v >= 0 => Ok(SingleValue(v as u32)),\n            ref t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ToGeckoFontFeatureValues for SingleValue {\n    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {\n        thin_vec::thin_vec![self.0 as u32]\n    }\n}\n\n/// A @font-feature-values block declaration value that keeps one or two values.\n#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]\npub struct PairValues(pub u32, pub Option<u32>);\n\nimpl Parse for PairValues {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<PairValues, ParseError<'i>> {\n        let location = input.current_source_location();\n        let first = match *input.next()? {\n            Token::Number {\n                int_value: Some(a), ..\n            } if a >= 0 => a as u32,\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        };\n        let location = input.current_source_location();\n        match input.next() {\n            Ok(&Token::Number {\n                int_value: Some(b), ..\n            }) if b >= 0 => Ok(PairValues(first, Some(b as u32))),\n            // It can't be anything other than number.\n            Ok(t) => Err(location.new_unexpected_token_error(t.clone())),\n            // It can be just one value.\n            Err(_) => Ok(PairValues(first, None)),\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ToGeckoFontFeatureValues for PairValues {\n    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {\n        let mut result = thin_vec::thin_vec![self.0 as u32];\n        if let Some(second) = self.1 {\n            result.push(second as u32);\n        }\n        result\n    }\n}\n\n/// A @font-feature-values block declaration value that keeps a list of values.\n#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)]\npub struct VectorValues(#[css(iterable)] pub Vec<u32>);\n\nimpl Parse for VectorValues {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<VectorValues, ParseError<'i>> {\n        let mut vec = vec![];\n        loop {\n            let location = input.current_source_location();\n            match input.next() {\n                Ok(&Token::Number {\n                    int_value: Some(a), ..\n                }) if a >= 0 => {\n                    vec.push(a as u32);\n                },\n                // It can't be anything other than number.\n                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),\n                Err(_) => break,\n            }\n        }\n\n        if vec.len() == 0 {\n            return Err(input.new_error(BasicParseErrorKind::EndOfInput));\n        }\n\n        Ok(VectorValues(vec))\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ToGeckoFontFeatureValues for VectorValues {\n    fn to_gecko_font_feature_values(&self) -> ThinVec<u32> {\n        self.0.iter().copied().collect()\n    }\n}\n\n/// Parses a list of `FamilyName`s.\npub fn parse_family_name_list<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<Vec<FamilyName>, ParseError<'i>> {\n    input\n        .parse_comma_separated(|i| FamilyName::parse(context, i))\n        .map_err(|e| e.into())\n}\n\n/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`.\n/// (`<ident>: <integer>+`)\nstruct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> {\n    context: &'a ParserContext<'b>,\n    declarations: &'a mut Vec<FFVDeclaration<T>>,\n}\n\n/// Default methods reject all at rules.\nimpl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {\n    type Prelude = ();\n    type AtRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i, T> QualifiedRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> {\n    type Prelude = ();\n    type QualifiedRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T>\nwhere\n    T: Parse,\n{\n    type Declaration = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_value<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        _declaration_start: &ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        let value = input.parse_entirely(|i| T::parse(self.context, i))?;\n        let new = FFVDeclaration {\n            name: Atom::from(&*name),\n            value,\n        };\n        update_or_push(&mut self.declarations, new);\n        Ok(())\n    }\n}\n\nimpl<'a, 'b, 'i, T> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>\n    for FFVDeclarationsParser<'a, 'b, T>\nwhere\n    T: Parse,\n{\n    fn parse_declarations(&self) -> bool {\n        true\n    }\n    fn parse_qualified(&self) -> bool {\n        false\n    }\n}\n\nmacro_rules! font_feature_values_blocks {\n    (\n        blocks = [\n            $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )*\n        ]\n    ) => {\n        /// The [`@font-feature-values`][font-feature-values] at-rule.\n        ///\n        /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule\n        #[derive(Clone, Debug, PartialEq, ToShmem)]\n        pub struct FontFeatureValuesRule {\n            /// Font family list for @font-feature-values rule.\n            /// Family names cannot contain generic families. FamilyName\n            /// also accepts only non-generic names.\n            pub family_names: Vec<FamilyName>,\n            $(\n                #[$doc]\n                pub $ident: Vec<FFVDeclaration<$ty>>,\n            )*\n            /// The line and column of the rule's source code.\n            pub source_location: SourceLocation,\n        }\n\n        impl FontFeatureValuesRule {\n            /// Creates an empty FontFeatureValuesRule with given location and family name list.\n            fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self {\n                FontFeatureValuesRule {\n                    family_names: family_names,\n                    $(\n                        $ident: vec![],\n                    )*\n                    source_location: location,\n                }\n            }\n\n            /// Parses a `FontFeatureValuesRule`.\n            pub fn parse(\n                context: &ParserContext,\n                input: &mut Parser,\n                family_names: Vec<FamilyName>,\n                location: SourceLocation,\n            ) -> Self {\n                let mut rule = FontFeatureValuesRule::new(family_names, location);\n                let mut parser = FontFeatureValuesRuleParser {\n                    context,\n                    rule: &mut rule,\n                };\n                let mut iter = RuleBodyParser::new(input, &mut parser);\n                while let Some(result) = iter.next() {\n                    if let Err((error, slice)) = result {\n                        let location = error.location;\n                        let error = ContextualParseError::UnsupportedRule(slice, error);\n                        context.log_css_error(location, error);\n                    }\n                }\n                rule\n            }\n\n            /// Prints inside of `@font-feature-values` block.\n            pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n            where\n                W: Write,\n            {\n                $(\n                    if self.$ident.len() > 0 {\n                        dest.write_str(concat!(\"@\", $name, \" {\\n\"))?;\n                        let iter = self.$ident.iter();\n                        for val in iter {\n                            val.to_css(dest)?;\n                            dest.write_str(\"\\n\")?\n                        }\n                        dest.write_str(\"}\\n\")?\n                    }\n                )*\n                Ok(())\n            }\n\n            /// Returns length of all at-rules.\n            pub fn len(&self) -> usize {\n                let mut len = 0;\n                $(\n                    len += self.$ident.len();\n                )*\n                len\n            }\n\n            /// Convert to Gecko gfxFontFeatureValueSet.\n            #[cfg(feature = \"gecko\")]\n            pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) {\n                for ref family in self.family_names.iter() {\n                    let family = family.name.to_ascii_lowercase();\n                    $(\n                        if self.$ident.len() > 0 {\n                            for val in self.$ident.iter() {\n                                let array = unsafe {\n                                    Gecko_AppendFeatureValueHashEntry(\n                                        dest,\n                                        family.as_ptr(),\n                                        structs::$gecko_enum,\n                                        val.name.as_ptr()\n                                    )\n                                };\n                                unsafe {\n                                    *array = val.value.to_gecko_font_feature_values();\n                                }\n                            }\n                        }\n                    )*\n                }\n            }\n        }\n\n        impl ToCssWithGuard for FontFeatureValuesRule {\n            fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n                dest.write_str(\"@font-feature-values \")?;\n                self.family_names.to_css(&mut CssWriter::new(dest))?;\n                dest.write_str(\" {\\n\")?;\n                self.value_to_css(&mut CssWriter::new(dest))?;\n                dest.write_char('}')\n            }\n        }\n\n        /// Updates with new value if same `ident` exists, otherwise pushes to the vector.\n        fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) {\n            if let Some(item) = vec.iter_mut().find(|item| item.name == element.name) {\n                item.value = element.value;\n            } else {\n                vec.push(element);\n            }\n        }\n\n        /// Keeps the information about block type like @swash, @styleset etc.\n        enum BlockType {\n            $(\n                $ident_camel,\n            )*\n        }\n\n        /// Parser for `FontFeatureValuesRule`. Parses all blocks\n        /// <feature-type> {\n        ///   <feature-value-declaration-list>\n        /// }\n        /// <feature-type> = @stylistic | @historical-forms | @styleset |\n        /// @character-variant | @swash | @ornaments | @annotation\n        struct FontFeatureValuesRuleParser<'a> {\n            context: &'a ParserContext<'a>,\n            rule: &'a mut FontFeatureValuesRule,\n        }\n\n        /// Default methods reject all qualified rules.\n        impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> {\n            type Prelude = ();\n            type QualifiedRule = ();\n            type Error = StyleParseErrorKind<'i>;\n        }\n\n        impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> {\n            type Prelude = BlockType;\n            type AtRule = ();\n            type Error = StyleParseErrorKind<'i>;\n\n            fn parse_prelude<'t>(\n                &mut self,\n                name: CowRcStr<'i>,\n                input: &mut Parser<'i, 't>,\n            ) -> Result<BlockType, ParseError<'i>> {\n                match_ignore_ascii_case! { &*name,\n                    $(\n                        $name => Ok(BlockType::$ident_camel),\n                    )*\n                    _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),\n                }\n            }\n\n            fn parse_block<'t>(\n                &mut self,\n                prelude: BlockType,\n                _: &ParserState,\n                input: &mut Parser<'i, 't>\n            ) -> Result<Self::AtRule, ParseError<'i>> {\n                debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues));\n                match prelude {\n                    $(\n                        BlockType::$ident_camel => {\n                            let mut parser = FFVDeclarationsParser {\n                                context: &self.context,\n                                declarations: &mut self.rule.$ident,\n                            };\n\n                            let mut iter = RuleBodyParser::new(input, &mut parser);\n                            while let Some(declaration) = iter.next() {\n                                if let Err((error, slice)) = declaration {\n                                    let location = error.location;\n                                    // TODO(emilio): Maybe add a more specific error kind for\n                                    // font-feature-values descriptors.\n                                    let error = ContextualParseError::UnsupportedPropertyDeclaration(slice, error, &[]);\n                                    self.context.log_css_error(location, error);\n                                }\n                            }\n                        },\n                    )*\n                }\n\n                Ok(())\n            }\n        }\n\n        impl<'a, 'i> DeclarationParser<'i> for FontFeatureValuesRuleParser<'a> {\n            type Declaration = ();\n            type Error = StyleParseErrorKind<'i>;\n        }\n\n        impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for FontFeatureValuesRuleParser<'a> {\n            fn parse_declarations(&self) -> bool { false }\n            fn parse_qualified(&self) -> bool { true }\n        }\n    }\n}\n\nfont_feature_values_blocks! {\n    blocks = [\n        #[doc = \"A @swash blocksck. \\\n                 Specifies a feature name that will work with the swash() \\\n                 functional notation of font-variant-alternates.\"]\n        \"swash\" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue,\n\n        #[doc = \"A @stylistic block. \\\n                 Specifies a feature name that will work with the annotation() \\\n                 functional notation of font-variant-alternates.\"]\n        \"stylistic\" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue,\n\n        #[doc = \"A @ornaments block. \\\n                 Specifies a feature name that will work with the ornaments() ] \\\n                 functional notation of font-variant-alternates.\"]\n        \"ornaments\" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue,\n\n        #[doc = \"A @annotation block. \\\n                 Specifies a feature name that will work with the stylistic() \\\n                 functional notation of font-variant-alternates.\"]\n        \"annotation\" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue,\n\n        #[doc = \"A @character-variant block. \\\n                 Specifies a feature name that will work with the styleset() \\\n                 functional notation of font-variant-alternates. The value can be a pair.\"]\n        \"character-variant\" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT:\n            PairValues,\n\n        #[doc = \"A @styleset block. \\\n                 Specifies a feature name that will work with the character-variant() \\\n                 functional notation of font-variant-alternates. The value can be a list.\"]\n        \"styleset\" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues,\n    ]\n}\n"
  },
  {
    "path": "style/stylesheets/font_palette_values_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@font-palette-values`][font-palette-values] at-rule.\n//!\n//! [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::{\n    bindings::Gecko_AppendPaletteValueHashEntry,\n    bindings::{Gecko_SetFontPaletteBase, Gecko_SetFontPaletteOverride},\n    structs::gfx::FontPaletteValueSet,\n    structs::gfx::FontPaletteValueSet_PaletteValues_kDark,\n    structs::gfx::FontPaletteValueSet_PaletteValues_kLight,\n};\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::font_feature_values_rule::parse_family_name_list;\nuse crate::values::computed::font::FamilyName;\nuse crate::values::specified::Color as SpecifiedColor;\nuse crate::values::specified::NonNegativeInteger;\nuse crate::values::DashedIdent;\nuse cssparser::{\n    match_ignore_ascii_case, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserState,\n    QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation,\n};\nuse selectors::parser::SelectorParseErrorKind;\nuse std::fmt::{self, Write};\nuse style_traits::{Comma, OneOrMoreSeparated};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n#[allow(missing_docs)]\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct FontPaletteOverrideColor {\n    index: NonNegativeInteger,\n    color: SpecifiedColor,\n}\n\nimpl Parse for FontPaletteOverrideColor {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontPaletteOverrideColor, ParseError<'i>> {\n        let index = NonNegativeInteger::parse(context, input)?;\n        let location = input.current_source_location();\n        let color = SpecifiedColor::parse(context, input)?;\n        // Only absolute colors are accepted here:\n        //   https://drafts.csswg.org/css-fonts/#override-color\n        //   https://drafts.csswg.org/css-color-5/#absolute-color\n        // so check that the specified color can be resolved without a context\n        // or currentColor value.\n        if color.resolve_to_absolute().is_ok() {\n            // We store the specified color (not the resolved absolute color)\n            // because that is what the rule exposes to authors.\n            return Ok(FontPaletteOverrideColor { index, color });\n        }\n        Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\nimpl ToCss for FontPaletteOverrideColor {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.index.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.color.to_css(dest)\n    }\n}\n\nimpl OneOrMoreSeparated for FontPaletteOverrideColor {\n    type S = Comma;\n}\n\nimpl OneOrMoreSeparated for FamilyName {\n    type S = Comma;\n}\n\n#[allow(missing_docs)]\n#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\npub enum FontPaletteBase {\n    Light,\n    Dark,\n    Index(NonNegativeInteger),\n}\n\n/// The [`@font-palette-values`][font-palette-values] at-rule.\n///\n/// [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values\n#[derive(Clone, Debug, PartialEq, ToShmem)]\npub struct FontPaletteValuesRule {\n    /// Palette name.\n    pub name: DashedIdent,\n    /// Font family list for @font-palette-values rule.\n    /// Family names cannot contain generic families. FamilyName\n    /// also accepts only non-generic names.\n    pub family_names: Vec<FamilyName>,\n    /// The base palette.\n    pub base_palette: Option<FontPaletteBase>,\n    /// The list of override colors.\n    pub override_colors: Vec<FontPaletteOverrideColor>,\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl FontPaletteValuesRule {\n    /// Creates an empty FontPaletteValuesRule with given location and name.\n    fn new(name: DashedIdent, location: SourceLocation) -> Self {\n        FontPaletteValuesRule {\n            name,\n            family_names: vec![],\n            base_palette: None,\n            override_colors: vec![],\n            source_location: location,\n        }\n    }\n\n    /// Parses a `FontPaletteValuesRule`.\n    pub fn parse(\n        context: &ParserContext,\n        input: &mut Parser,\n        name: DashedIdent,\n        location: SourceLocation,\n    ) -> Self {\n        let mut rule = FontPaletteValuesRule::new(name, location);\n        let mut parser = FontPaletteValuesDeclarationParser {\n            context,\n            rule: &mut rule,\n        };\n        let mut iter = RuleBodyParser::new(input, &mut parser);\n        while let Some(declaration) = iter.next() {\n            if let Err((error, slice)) = declaration {\n                let location = error.location;\n                let error =\n                    ContextualParseError::UnsupportedFontPaletteValuesDescriptor(slice, error);\n                context.log_css_error(location, error);\n            }\n        }\n        rule\n    }\n\n    /// Prints inside of `@font-palette-values` block.\n    fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if !self.family_names.is_empty() {\n            dest.write_str(\"font-family: \")?;\n            self.family_names.to_css(dest)?;\n            dest.write_str(\"; \")?;\n        }\n        if let Some(base) = &self.base_palette {\n            dest.write_str(\"base-palette: \")?;\n            base.to_css(dest)?;\n            dest.write_str(\"; \")?;\n        }\n        if !self.override_colors.is_empty() {\n            dest.write_str(\"override-colors: \")?;\n            self.override_colors.to_css(dest)?;\n            dest.write_str(\"; \")?;\n        }\n        Ok(())\n    }\n\n    /// Convert to Gecko FontPaletteValueSet.\n    #[cfg(feature = \"gecko\")]\n    pub fn to_gecko_palette_value_set(&self, dest: *mut FontPaletteValueSet) {\n        for ref family in self.family_names.iter() {\n            let family = family.name.to_ascii_lowercase();\n            let palette_values = unsafe {\n                Gecko_AppendPaletteValueHashEntry(dest, family.as_ptr(), self.name.0.as_ptr())\n            };\n            if let Some(base_palette) = &self.base_palette {\n                unsafe {\n                    Gecko_SetFontPaletteBase(\n                        palette_values,\n                        match &base_palette {\n                            FontPaletteBase::Light => FontPaletteValueSet_PaletteValues_kLight,\n                            FontPaletteBase::Dark => FontPaletteValueSet_PaletteValues_kDark,\n                            FontPaletteBase::Index(i) => i.0.value() as i32,\n                        },\n                    );\n                }\n            }\n            for c in &self.override_colors {\n                // We checked at parse time that the specified color can be resolved\n                // in this way, so the unwrap() here will succeed.\n                let absolute = c.color.resolve_to_absolute().unwrap();\n                unsafe {\n                    Gecko_SetFontPaletteOverride(\n                        palette_values,\n                        c.index.0.value(),\n                        (&absolute) as *const _ as *mut _,\n                    );\n                }\n            }\n        }\n    }\n}\n\nimpl ToCssWithGuard for FontPaletteValuesRule {\n    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@font-palette-values \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" { \")?;\n        self.value_to_css(&mut CssWriter::new(dest))?;\n        dest.write_char('}')\n    }\n}\n\n/// Parser for declarations in `FontPaletteValuesRule`.\nstruct FontPaletteValuesDeclarationParser<'a> {\n    context: &'a ParserContext<'a>,\n    rule: &'a mut FontPaletteValuesRule,\n}\n\nimpl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {\n    type Prelude = ();\n    type AtRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> {\n    type Prelude = ();\n    type QualifiedRule = ();\n    type Error = StyleParseErrorKind<'i>;\n}\n\nfn parse_override_colors<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<Vec<FontPaletteOverrideColor>, ParseError<'i>> {\n    input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i))\n}\n\nimpl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> {\n    type Declaration = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_value<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        _declaration_start: &ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        match_ignore_ascii_case! { &*name,\n            \"font-family\" => {\n                self.rule.family_names = parse_family_name_list(self.context, input)?\n            },\n            \"base-palette\" => {\n                self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?)\n            },\n            \"override-colors\" => {\n                self.rule.override_colors = parse_override_colors(self.context, input)?\n            },\n            _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),\n        }\n        Ok(())\n    }\n}\n\nimpl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>>\n    for FontPaletteValuesDeclarationParser<'a>\n{\n    fn parse_declarations(&self) -> bool {\n        true\n    }\n    fn parse_qualified(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/import_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The [`@import`][import] at-rule.\n//!\n//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import\n\nuse crate::media_queries::MediaList;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::{\n    layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType,\n    StylesheetInDocument,\n};\nuse crate::values::CssUrl;\nuse cssparser::{Parser, SourceLocation};\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ToCss};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\n#[cfg(feature = \"gecko\")]\ntype StyleSheet = crate::gecko::data::GeckoStyleSheet;\n#[cfg(feature = \"servo\")]\ntype StyleSheet = ::servo_arc::Arc<crate::stylesheets::Stylesheet>;\n\n/// A sheet that is held from an import rule.\n#[derive(Debug)]\npub enum ImportSheet {\n    /// A bonafide stylesheet.\n    Sheet(StyleSheet),\n\n    /// An @import created while parsing off-main-thread, whose Gecko sheet has\n    /// yet to be created and attached.\n    Pending,\n\n    /// An @import created with a false <supports-condition>, so will never be fetched.\n    Refused,\n}\n\nimpl ImportSheet {\n    /// Creates a new ImportSheet from a stylesheet.\n    pub fn new(sheet: StyleSheet) -> Self {\n        ImportSheet::Sheet(sheet)\n    }\n\n    /// Creates a pending ImportSheet for a load that has not started yet.\n    pub fn new_pending() -> Self {\n        ImportSheet::Pending\n    }\n\n    /// Creates a refused ImportSheet for a load that will not happen.\n    pub fn new_refused() -> Self {\n        ImportSheet::Refused\n    }\n\n    /// Returns a reference to the stylesheet in this ImportSheet, if it exists.\n    pub fn as_sheet(&self) -> Option<&StyleSheet> {\n        match *self {\n            #[cfg(feature = \"gecko\")]\n            ImportSheet::Sheet(ref s) => {\n                debug_assert!(!s.hack_is_null());\n                if s.hack_is_null() {\n                    return None;\n                }\n                Some(s)\n            },\n            #[cfg(feature = \"servo\")]\n            ImportSheet::Sheet(ref s) => Some(s),\n            ImportSheet::Refused | ImportSheet::Pending => None,\n        }\n    }\n\n    /// Returns the media list for this import rule.\n    pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {\n        self.as_sheet().and_then(|s| s.media(guard))\n    }\n\n    /// Returns the rule list for this import rule.\n    pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {\n        match self.as_sheet() {\n            Some(s) => s.contents(guard).rules(guard),\n            None => &[],\n        }\n    }\n}\n\nimpl DeepCloneWithLock for ImportSheet {\n    fn deep_clone_with_lock(&self, _lock: &SharedRwLock, _guard: &SharedRwLockReadGuard) -> Self {\n        match *self {\n            #[cfg(feature = \"gecko\")]\n            ImportSheet::Sheet(ref s) => {\n                use crate::gecko_bindings::bindings;\n                let clone = unsafe { bindings::Gecko_StyleSheet_Clone(s.raw() as *const _) };\n                ImportSheet::Sheet(unsafe { StyleSheet::from_addrefed(clone) })\n            },\n            #[cfg(feature = \"servo\")]\n            ImportSheet::Sheet(ref s) => {\n                use servo_arc::Arc;\n                ImportSheet::Sheet(Arc::new((&**s).clone()))\n            },\n            ImportSheet::Pending => ImportSheet::Pending,\n            ImportSheet::Refused => ImportSheet::Refused,\n        }\n    }\n}\n\n/// The layer specified in an import rule (can be none, anonymous, or named).\n#[derive(Debug, Clone)]\npub enum ImportLayer {\n    /// No layer specified\n    None,\n\n    /// Anonymous layer (`layer`)\n    Anonymous,\n\n    /// Named layer (`layer(name)`)\n    Named(LayerName),\n}\n\n/// The supports condition in an import rule.\n#[derive(Debug, Clone)]\npub struct ImportSupportsCondition {\n    /// The supports condition.\n    pub condition: SupportsCondition,\n\n    /// If the import is enabled, from the result of the import condition.\n    pub enabled: bool,\n}\n\nimpl ToCss for ImportLayer {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            ImportLayer::None => Ok(()),\n            ImportLayer::Anonymous => dest.write_str(\"layer\"),\n            ImportLayer::Named(ref name) => {\n                dest.write_str(\"layer(\")?;\n                name.to_css(dest)?;\n                dest.write_char(')')\n            },\n        }\n    }\n}\n\n/// The [`@import`][import] at-rule.\n///\n/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import\n#[derive(Debug)]\npub struct ImportRule {\n    /// The `<url>` this `@import` rule is loading.\n    pub url: CssUrl,\n\n    /// The stylesheet is always present. However, in the case of gecko async\n    /// parsing, we don't actually have a Gecko sheet at first, and so the\n    /// ImportSheet just has stub behavior until it appears.\n    pub stylesheet: ImportSheet,\n\n    /// A <supports-condition> for the rule.\n    pub supports: Option<ImportSupportsCondition>,\n\n    /// A `layer()` function name.\n    pub layer: ImportLayer,\n\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl ImportRule {\n    /// Parses the layer() / layer / supports() part of the import header, as per\n    /// https://drafts.csswg.org/css-cascade-5/#at-import:\n    ///\n    ///     [ layer | layer(<layer-name>) ]?\n    ///     [ supports([ <supports-condition> | <declaration> ]) ]?\n    ///\n    /// We do this here so that the import preloader can look at this without having to parse the\n    /// whole import rule or parse the media query list or what not.\n    pub fn parse_layer_and_supports<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        context: &mut ParserContext,\n    ) -> (ImportLayer, Option<ImportSupportsCondition>) {\n        let layer = if input\n            .try_parse(|input| input.expect_ident_matching(\"layer\"))\n            .is_ok()\n        {\n            ImportLayer::Anonymous\n        } else {\n            input\n                .try_parse(|input| {\n                    input.expect_function_matching(\"layer\")?;\n                    input\n                        .parse_nested_block(|input| LayerName::parse(context, input))\n                        .map(|name| ImportLayer::Named(name))\n                })\n                .ok()\n                .unwrap_or(ImportLayer::None)\n        };\n\n        let supports = input\n            .try_parse(SupportsCondition::parse_for_import)\n            .map(|condition| {\n                let enabled =\n                    context.nest_for_rule(CssRuleType::Style, |context| condition.eval(context));\n                ImportSupportsCondition { condition, enabled }\n            })\n            .ok();\n\n        (layer, supports)\n    }\n}\n\nimpl ToShmem for ImportRule {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        Err(String::from(\n            \"ToShmem failed for ImportRule: cannot handle imported style sheets\",\n        ))\n    }\n}\n\nimpl DeepCloneWithLock for ImportRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        ImportRule {\n            url: self.url.clone(),\n            stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard),\n            supports: self.supports.clone(),\n            layer: self.layer.clone(),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for ImportRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@import \")?;\n        self.url.to_css(&mut CssWriter::new(dest))?;\n\n        if !matches!(self.layer, ImportLayer::None) {\n            dest.write_char(' ')?;\n            self.layer.to_css(&mut CssWriter::new(dest))?;\n        }\n\n        if let Some(ref supports) = self.supports {\n            dest.write_str(\" supports(\")?;\n            supports.condition.to_css(&mut CssWriter::new(dest))?;\n            dest.write_char(')')?;\n        }\n\n        if let Some(media) = self.stylesheet.media(guard) {\n            if !media.is_empty() {\n                dest.write_char(' ')?;\n                media.to_css(&mut CssWriter::new(dest))?;\n            }\n        }\n\n        dest.write_char(';')\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/keyframes_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::ParserContext;\nuse crate::properties::{\n    longhands::{\n        animation_composition::single_value::SpecifiedValue as SpecifiedComposition,\n        transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction,\n    },\n    parse_property_declaration_list, LonghandId, PropertyDeclaration, PropertyDeclarationBlock,\n    PropertyDeclarationId, PropertyDeclarationIdSet,\n};\nuse crate::shared_lock::{DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard};\nuse crate::shared_lock::{Locked, ToCssWithGuard};\nuse crate::stylesheets::rule_parser::VendorPrefix;\nuse crate::stylesheets::{CssRuleType, StylesheetContents};\nuse crate::values::specified::animation::TimelineRangeName;\nuse crate::values::{serialize_percentage, KeyframesName};\nuse cssparser::{\n    parse_one_rule, AtRuleParser, DeclarationParser, Parser, ParserInput, ParserState,\n    QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token,\n};\nuse servo_arc::Arc;\nuse std::borrow::Cow;\nuse std::fmt::{self, Write};\nuse style_traits::{\n    CssStringWriter, CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss,\n};\n\n/// A [`@keyframes`][keyframes] rule.\n///\n/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes\n#[derive(Debug, ToShmem)]\npub struct KeyframesRule {\n    /// The name of the current animation.\n    pub name: KeyframesName,\n    /// The keyframes specified for this CSS rule.\n    pub keyframes: Vec<Arc<Locked<Keyframe>>>,\n    /// Vendor prefix type the @keyframes has.\n    pub vendor_prefix: Option<VendorPrefix>,\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl ToCssWithGuard for KeyframesRule {\n    // Serialization of KeyframesRule is not specced.\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@keyframes \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" {\")?;\n        let iter = self.keyframes.iter();\n        for lock in iter {\n            dest.write_str(\"\\n\")?;\n            let keyframe = lock.read_with(&guard);\n            keyframe.to_css(guard, dest)?;\n        }\n        dest.write_str(\"\\n}\")\n    }\n}\n\nimpl KeyframesRule {\n    /// Returns the index of the last keyframe that matches the given selector.\n    /// If the selector is not valid, or no keyframe is found, returns None.\n    ///\n    /// Related spec:\n    /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule>\n    pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> {\n        let mut input = ParserInput::new(selector);\n        if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelectors::parse) {\n            for (i, keyframe) in self.keyframes.iter().enumerate().rev() {\n                if keyframe.read_with(guard).selector == selector {\n                    return Some(i);\n                }\n            }\n        }\n        None\n    }\n}\n\nimpl DeepCloneWithLock for KeyframesRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        KeyframesRule {\n            name: self.name.clone(),\n            keyframes: self\n                .keyframes\n                .iter()\n                .map(|x| Arc::new(lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard))))\n                .collect(),\n            vendor_prefix: self.vendor_prefix.clone(),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// A number from 0 to 1, indicating the percentage of the animation when this\n/// keyframe should run.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)]\npub struct KeyframePercentage(pub f32);\n\nimpl ::std::cmp::Ord for KeyframePercentage {\n    #[inline]\n    fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {\n        // We know we have a number from 0 to 1, so unwrap() here is safe.\n        self.0.partial_cmp(&other.0).unwrap()\n    }\n}\n\nimpl ::std::cmp::Eq for KeyframePercentage {}\n\nimpl ToCss for KeyframePercentage {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_percentage(self.0, dest)\n    }\n}\n\nimpl KeyframePercentage {\n    /// Trivially constructs a new `KeyframePercentage`.\n    #[inline]\n    pub fn new(value: f32) -> KeyframePercentage {\n        debug_assert!(value >= 0. && value <= 1.);\n        KeyframePercentage(value)\n    }\n\n    fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> {\n        let token = input.next()?.clone();\n        match token {\n            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case(\"from\") => {\n                Ok(KeyframePercentage::new(0.))\n            },\n            Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case(\"to\") => {\n                Ok(KeyframePercentage::new(1.))\n            },\n            Token::Percentage {\n                unit_value: percentage,\n                ..\n            } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)),\n            _ => Err(input.new_unexpected_token_error(token)),\n        }\n    }\n}\n\n/// A single `<keyframe-selector>`:\n/// `<keyframe-selector> = from | to | <percentage [0,100]> | <timeline-range-name> <percentage>`\n/// It could be a percentage, from/to, or a timeline range name together with a percentage.\n/// https://drafts.csswg.org/scroll-animations-1/#named-range-keyframes\n#[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss, ToShmem)]\npub struct KeyframeSelector {\n    /// The named timeline range name component of the selector. If it is omitted, we use\n    /// `TimelineRangeName::None`. Note that `TimelineRangeName::Normal` is not used for the\n    /// selector.\n    range_name: TimelineRangeName,\n    /// The percentage component of the selector. It is a percentage or a from/to symbol, which is\n    /// converted at parse time to percentage.\n    percentage: KeyframePercentage,\n}\n\nimpl KeyframeSelector {\n    /// Returns Self as a percentage, for unit testing.\n    fn new_for_unit_testing(percentage: KeyframePercentage) -> Self {\n        KeyframeSelector {\n            range_name: TimelineRangeName::None,\n            percentage,\n        }\n    }\n\n    /// Parse a keyframe selector from CSS input.\n    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        // `from | to | <percentage [0,100]>`\n        if let Ok(percentage) = input.try_parse(KeyframePercentage::parse) {\n            return Ok(Self {\n                range_name: TimelineRangeName::None,\n                percentage,\n            });\n        }\n\n        // We parse the the extension of keyframe selector for scroll-driven animation.\n        if !static_prefs::pref!(\"layout.css.scroll-driven-animations.enabled\") {\n            let location = input.current_source_location();\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // `<timeline-range-name> <percentage>`\n        // Note that <percentage> could be out of [0,100].\n        Ok(Self {\n            range_name: TimelineRangeName::parse(input)?,\n            percentage: KeyframePercentage::new(input.expect_percentage()?),\n        })\n    }\n}\n\n/// A list of `<keyframe-selector>`s.\n#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)]\n#[css(comma)]\npub struct KeyframeSelectors(#[css(iterable)] Vec<KeyframeSelector>);\n\nimpl KeyframeSelectors {\n    /// A dummy public function so we can write a unit test for this.\n    pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelectors {\n        KeyframeSelectors(\n            percentages\n                .into_iter()\n                .map(KeyframeSelector::new_for_unit_testing)\n                .collect(),\n        )\n    }\n\n    /// Parse the keyframe selectors from CSS input.\n    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        input\n            .parse_comma_separated(KeyframeSelector::parse)\n            .map(KeyframeSelectors)\n    }\n}\n\n/// A keyframe.\n#[derive(Debug, ToShmem)]\npub struct Keyframe {\n    /// The selector this keyframe was specified from.\n    pub selector: KeyframeSelectors,\n\n    /// The declaration block that was declared inside this keyframe.\n    ///\n    /// Note that `!important` rules in keyframes don't apply, but we keep this\n    /// `Arc` just for convenience.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl ToCssWithGuard for Keyframe {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        self.selector.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" { \")?;\n        self.block.read_with(guard).to_css(dest)?;\n        dest.write_str(\" }\")?;\n        Ok(())\n    }\n}\n\nimpl Keyframe {\n    /// Parse a CSS keyframe.\n    pub fn parse<'i>(\n        css: &'i str,\n        parent_stylesheet_contents: &StylesheetContents,\n        lock: &SharedRwLock,\n    ) -> Result<Arc<Locked<Self>>, ParseError<'i>> {\n        let url_data = &parent_stylesheet_contents.url_data;\n        let namespaces = &parent_stylesheet_contents.namespaces;\n        let mut context = ParserContext::new(\n            parent_stylesheet_contents.origin,\n            &url_data,\n            Some(CssRuleType::Keyframe),\n            ParsingMode::DEFAULT,\n            parent_stylesheet_contents.quirks_mode,\n            Cow::Borrowed(&*namespaces),\n            None,\n            None,\n            /* attr_taint */ Default::default(),\n        );\n        let mut input = ParserInput::new(css);\n        let mut input = Parser::new(&mut input);\n\n        let mut rule_parser = KeyframeListParser {\n            context: &mut context,\n            shared_lock: &lock,\n        };\n        parse_one_rule(&mut input, &mut rule_parser)\n    }\n}\n\nimpl DeepCloneWithLock for Keyframe {\n    /// Deep clones this Keyframe.\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Keyframe {\n        Keyframe {\n            selector: self.selector.clone(),\n            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// A keyframes step value. This can be a synthetised keyframes animation, that\n/// is, one autogenerated from the current computed values, or a list of\n/// declarations to apply.\n///\n/// TODO: Find a better name for this?\n#[derive(Clone, Debug, MallocSizeOf)]\npub enum KeyframesStepValue {\n    /// A step formed by a declaration block specified by the CSS.\n    Declarations {\n        /// The declaration block per se.\n        #[cfg_attr(\n            feature = \"gecko\",\n            ignore_malloc_size_of = \"XXX: Primary ref, measure if DMD says it's worthwhile\"\n        )]\n        #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n        block: Arc<Locked<PropertyDeclarationBlock>>,\n    },\n    /// A synthetic step computed from the current computed values at the time\n    /// of the animation.\n    ComputedValues,\n}\n\n/// A single step from a keyframe animation.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct KeyframesStep {\n    /// The percentage of the animation duration when this step starts.\n    pub start_percentage: KeyframePercentage,\n    /// Declarations that will determine the final style during the step, or\n    /// `ComputedValues` if this is an autogenerated step.\n    pub value: KeyframesStepValue,\n    /// Whether an animation-timing-function declaration exists in the list of\n    /// declarations.\n    ///\n    /// This is used to know when to override the keyframe animation style.\n    pub declared_timing_function: bool,\n    /// Whether an animation-composition declaration exists in the list of\n    /// declarations.\n    ///\n    /// This is used to know when to override the keyframe animation style.\n    pub declared_composition: bool,\n}\n\nimpl KeyframesStep {\n    #[inline]\n    fn new(\n        start_percentage: KeyframePercentage,\n        value: KeyframesStepValue,\n        guard: &SharedRwLockReadGuard,\n    ) -> Self {\n        let mut declared_timing_function = false;\n        let mut declared_composition = false;\n        if let KeyframesStepValue::Declarations { ref block } = value {\n            for prop_decl in block.read_with(guard).declarations().iter() {\n                match *prop_decl {\n                    PropertyDeclaration::AnimationTimingFunction(..) => {\n                        declared_timing_function = true;\n                    },\n                    PropertyDeclaration::AnimationComposition(..) => {\n                        declared_composition = true;\n                    },\n                    _ => continue,\n                }\n                // Don't need to continue the loop if both are found.\n                if declared_timing_function && declared_composition {\n                    break;\n                }\n            }\n        }\n\n        KeyframesStep {\n            start_percentage,\n            value,\n            declared_timing_function,\n            declared_composition,\n        }\n    }\n\n    /// Return specified PropertyDeclaration.\n    #[inline]\n    fn get_declared_property<'a>(\n        &'a self,\n        guard: &'a SharedRwLockReadGuard,\n        property: LonghandId,\n    ) -> Option<&'a PropertyDeclaration> {\n        match self.value {\n            KeyframesStepValue::Declarations { ref block } => {\n                let guard = block.read_with(guard);\n                let (declaration, _) = guard\n                    .get(PropertyDeclarationId::Longhand(property))\n                    .unwrap();\n                match *declaration {\n                    PropertyDeclaration::CSSWideKeyword(..) => None,\n                    // FIXME: Bug 1710735: Support css variable in @keyframes rule.\n                    PropertyDeclaration::WithVariables(..) => None,\n                    _ => Some(declaration),\n                }\n            },\n            KeyframesStepValue::ComputedValues => {\n                panic!(\"Shouldn't happen to set this property in missing keyframes\")\n            },\n        }\n    }\n\n    /// Return specified TransitionTimingFunction if this KeyframesSteps has\n    /// 'animation-timing-function'.\n    pub fn get_animation_timing_function(\n        &self,\n        guard: &SharedRwLockReadGuard,\n    ) -> Option<SpecifiedTimingFunction> {\n        if !self.declared_timing_function {\n            return None;\n        }\n\n        self.get_declared_property(guard, LonghandId::AnimationTimingFunction)\n            .map(|decl| {\n                match *decl {\n                    PropertyDeclaration::AnimationTimingFunction(ref value) => {\n                        // Use the first value\n                        value.0[0].clone()\n                    },\n                    _ => unreachable!(\"Unexpected PropertyDeclaration\"),\n                }\n            })\n    }\n\n    /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'.\n    pub fn get_animation_composition(\n        &self,\n        guard: &SharedRwLockReadGuard,\n    ) -> Option<SpecifiedComposition> {\n        if !self.declared_composition {\n            return None;\n        }\n\n        self.get_declared_property(guard, LonghandId::AnimationComposition)\n            .map(|decl| {\n                match *decl {\n                    PropertyDeclaration::AnimationComposition(ref value) => {\n                        // Use the first value\n                        value.0[0].clone()\n                    },\n                    _ => unreachable!(\"Unexpected PropertyDeclaration\"),\n                }\n            })\n    }\n}\n\n/// This structure represents a list of animation steps computed from the list\n/// of keyframes, in order.\n///\n/// It only takes into account animable properties.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct KeyframesAnimation {\n    /// The difference steps of the animation.\n    pub steps: Vec<KeyframesStep>,\n    /// The properties that change in this animation.\n    pub properties_changed: PropertyDeclarationIdSet,\n    /// Vendor prefix type the @keyframes has.\n    pub vendor_prefix: Option<VendorPrefix>,\n}\n\n/// Get all the animated properties in a keyframes animation.\nfn get_animated_properties(\n    keyframes: &[Arc<Locked<Keyframe>>],\n    guard: &SharedRwLockReadGuard,\n) -> PropertyDeclarationIdSet {\n    let mut ret = PropertyDeclarationIdSet::default();\n    // NB: declarations are already deduplicated, so we don't have to check for\n    // it here.\n    for keyframe in keyframes {\n        let keyframe = keyframe.read_with(&guard);\n        let block = keyframe.block.read_with(guard);\n        // CSS Animations spec clearly defines that properties with !important\n        // in keyframe rules are invalid and ignored, but it's still ambiguous\n        // whether we should drop the !important properties or retain the\n        // properties when they are set via CSSOM. So we assume there might\n        // be properties with !important in keyframe rules here.\n        // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824\n        for declaration in block.normal_declaration_iter() {\n            let declaration_id = declaration.id();\n\n            if declaration_id == PropertyDeclarationId::Longhand(LonghandId::Display) {\n                continue;\n            }\n\n            if !declaration_id.is_animatable() {\n                continue;\n            }\n\n            ret.insert(declaration_id);\n        }\n    }\n\n    ret\n}\n\nimpl KeyframesAnimation {\n    /// Create a keyframes animation from a given list of keyframes.\n    ///\n    /// This will return a keyframe animation with empty steps and\n    /// properties_changed if the list of keyframes is empty, or there are no\n    /// animated properties obtained from the keyframes.\n    ///\n    /// Otherwise, this will compute and sort the steps used for the animation,\n    /// and return the animation object.\n    pub fn from_keyframes(\n        keyframes: &[Arc<Locked<Keyframe>>],\n        vendor_prefix: Option<VendorPrefix>,\n        guard: &SharedRwLockReadGuard,\n    ) -> Self {\n        let mut result = KeyframesAnimation {\n            steps: vec![],\n            properties_changed: PropertyDeclarationIdSet::default(),\n            vendor_prefix,\n        };\n\n        if keyframes.is_empty() {\n            return result;\n        }\n\n        result.properties_changed = get_animated_properties(keyframes, guard);\n        if result.properties_changed.is_empty() {\n            return result;\n        }\n\n        for keyframe in keyframes {\n            let keyframe = keyframe.read_with(&guard);\n            for selector in keyframe.selector.0.iter() {\n                // TODO: Bug 1824875. Handle range names.\n                if !selector.range_name.is_none() {\n                    continue;\n                }\n                result.steps.push(KeyframesStep::new(\n                    selector.percentage,\n                    KeyframesStepValue::Declarations {\n                        block: keyframe.block.clone(),\n                    },\n                    guard,\n                ));\n            }\n        }\n\n        // TODO: Bug 1824875. We have to rewrite the entire KeyframeAnimation structure because\n        // range name is layout-dependant. For now we skip them.\n        //\n        // Note: The early return is necessary because we access the vector below.\n        if result.steps.is_empty() {\n            // Roll back.\n            result.properties_changed = PropertyDeclarationIdSet::default();\n            return result;\n        }\n\n        // Sort by the start percentage, so we can easily find a frame.\n        result.steps.sort_by_key(|step| step.start_percentage);\n\n        // Prepend autogenerated keyframes if appropriate.\n        if result.steps[0].start_percentage.0 != 0. {\n            result.steps.insert(\n                0,\n                KeyframesStep::new(\n                    KeyframePercentage::new(0.),\n                    KeyframesStepValue::ComputedValues,\n                    guard,\n                ),\n            );\n        }\n\n        if result.steps.last().unwrap().start_percentage.0 != 1. {\n            result.steps.push(KeyframesStep::new(\n                KeyframePercentage::new(1.),\n                KeyframesStepValue::ComputedValues,\n                guard,\n            ));\n        }\n\n        result\n    }\n}\n\n/// Parses a keyframes list, like:\n/// 0%, 50% {\n///     width: 50%;\n/// }\n///\n/// 40%, 60%, 100% {\n///     width: 100%;\n/// }\nstruct KeyframeListParser<'a, 'b> {\n    context: &'a mut ParserContext<'b>,\n    shared_lock: &'a SharedRwLock,\n}\n\n/// Parses a keyframe list from CSS input.\npub fn parse_keyframe_list<'a>(\n    context: &mut ParserContext<'a>,\n    input: &mut Parser,\n    shared_lock: &SharedRwLock,\n) -> Vec<Arc<Locked<Keyframe>>> {\n    let mut parser = KeyframeListParser {\n        context,\n        shared_lock,\n    };\n    RuleBodyParser::new(input, &mut parser)\n        .filter_map(Result::ok)\n        .collect()\n}\n\nimpl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> {\n    type Prelude = ();\n    type AtRule = Arc<Locked<Keyframe>>;\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> {\n    type Declaration = Arc<Locked<Keyframe>>;\n    type Error = StyleParseErrorKind<'i>;\n}\n\nimpl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> {\n    type Prelude = KeyframeSelectors;\n    type QualifiedRule = Arc<Locked<Keyframe>>;\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_prelude<'t>(\n        &mut self,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::Prelude, ParseError<'i>> {\n        let start_position = input.position();\n        KeyframeSelectors::parse(input).map_err(|e| {\n            let location = e.location;\n            let error = ContextualParseError::InvalidKeyframeRule(\n                input.slice_from(start_position),\n                e.clone(),\n            );\n            self.context.log_css_error(location, error);\n            e\n        })\n    }\n\n    fn parse_block<'t>(\n        &mut self,\n        selector: Self::Prelude,\n        start: &ParserState,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::QualifiedRule, ParseError<'i>> {\n        let block = self.context.nest_for_rule(CssRuleType::Keyframe, |p| {\n            parse_property_declaration_list(&p, input, &[])\n        });\n        Ok(Arc::new(self.shared_lock.wrap(Keyframe {\n            selector,\n            block: Arc::new(self.shared_lock.wrap(block)),\n            source_location: start.source_location(),\n        })))\n    }\n}\n\nimpl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>>\n    for KeyframeListParser<'a, 'b>\n{\n    fn parse_qualified(&self) -> bool {\n        true\n    }\n    fn parse_declarations(&self) -> bool {\n        false\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/layer_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@layer`][layer] rule.\n//!\n//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::values::AtomIdent;\n\nuse super::CssRules;\n\nuse cssparser::{Parser, SourceLocation, Token};\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// The order of a given layer. We use 16 bits so that we can pack LayerOrder\n/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go\n/// back to packing CascadeLevel in a single byte as we did before.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)]\npub struct LayerOrder(u16);\n\nimpl LayerOrder {\n    /// The order of the root layer.\n    pub const fn root() -> Self {\n        Self(std::u16::MAX - 1)\n    }\n\n    /// The order of the style attribute layer.\n    pub const fn style_attribute() -> Self {\n        Self(std::u16::MAX)\n    }\n\n    /// Returns whether this layer is for the style attribute, which behaves\n    /// differently in terms of !important, see\n    /// https://github.com/w3c/csswg-drafts/issues/6872\n    ///\n    /// (This is a bit silly, mind-you, but it's needed so that revert-layer\n    /// behaves correctly).\n    #[inline]\n    pub fn is_style_attribute_layer(&self) -> bool {\n        *self == Self::style_attribute()\n    }\n\n    /// The first cascade layer order.\n    pub const fn first() -> Self {\n        Self(0)\n    }\n\n    /// Increment the cascade layer order.\n    #[inline]\n    pub fn inc(&mut self) {\n        if self.0 != std::u16::MAX - 1 {\n            self.0 += 1;\n        }\n    }\n}\n\n/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name\n#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]\npub struct LayerName(pub SmallVec<[AtomIdent; 1]>);\n\nimpl LayerName {\n    /// Returns an empty layer name (which isn't a valid final state, so caller\n    /// is responsible to fill up the name before use).\n    pub fn new_empty() -> Self {\n        Self(Default::default())\n    }\n\n    /// Returns a synthesized name for an anonymous layer.\n    pub fn new_anonymous() -> Self {\n        use std::sync::atomic::{AtomicUsize, Ordering};\n        static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0);\n\n        let mut name = SmallVec::new();\n        let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed);\n        // The parens don't _technically_ prevent conflicts with authors, as\n        // authors could write escaped parens as part of the identifier, I\n        // think, but highly reduces the possibility.\n        name.push(AtomIdent::from(&*format!(\"-moz-anon-layer({})\", next_id)));\n\n        LayerName(name)\n    }\n\n    /// Returns the names of the layers. That is, for a layer like `foo.bar`,\n    /// it'd return [foo, bar].\n    pub fn layer_names(&self) -> &[AtomIdent] {\n        &self.0\n    }\n}\n\nimpl Parse for LayerName {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut result = SmallVec::new();\n        result.push(AtomIdent::from(&**input.expect_ident()?));\n        loop {\n            let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> {\n                match input.next_including_whitespace()? {\n                    Token::Delim('.') => {},\n                    other => {\n                        let t = other.clone();\n                        return Err(input.new_unexpected_token_error(t));\n                    },\n                }\n\n                let name = match input.next_including_whitespace()? {\n                    Token::Ident(ref ident) => ident,\n                    other => {\n                        let t = other.clone();\n                        return Err(input.new_unexpected_token_error(t));\n                    },\n                };\n\n                Ok(AtomIdent::from(&**name))\n            });\n\n            match next_name {\n                Ok(name) => result.push(name),\n                Err(..) => break,\n            }\n        }\n        Ok(LayerName(result))\n    }\n}\n\nimpl ToCss for LayerName {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let mut first = true;\n        for name in self.0.iter() {\n            if !first {\n                dest.write_char('.')?;\n            }\n            first = false;\n            name.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n#[derive(Debug, ToShmem)]\n/// A block `@layer <name>? { ... }`\n/// https://drafts.csswg.org/css-cascade-5/#layer-block\npub struct LayerBlockRule {\n    /// The layer name, or `None` if anonymous.\n    pub name: Option<LayerName>,\n    /// The nested rules.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl ToCssWithGuard for LayerBlockRule {\n    fn to_css(\n        &self,\n        guard: &SharedRwLockReadGuard,\n        dest: &mut style_traits::CssStringWriter,\n    ) -> fmt::Result {\n        dest.write_str(\"@layer\")?;\n        if let Some(ref name) = self.name {\n            dest.write_char(' ')?;\n            name.to_css(&mut CssWriter::new(dest))?;\n        }\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for LayerBlockRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        Self {\n            name: self.name.clone(),\n            rules: Arc::new(\n                lock.wrap(\n                    self.rules\n                        .read_with(guard)\n                        .deep_clone_with_lock(lock, guard),\n                ),\n            ),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// A statement `@layer <name>, <name>, <name>;`\n///\n/// https://drafts.csswg.org/css-cascade-5/#layer-empty\n#[derive(Clone, Debug, ToShmem)]\npub struct LayerStatementRule {\n    /// The list of layers to sort.\n    pub names: Vec<LayerName>,\n    /// The source position where this rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl ToCssWithGuard for LayerStatementRule {\n    fn to_css(\n        &self,\n        _: &SharedRwLockReadGuard,\n        dest: &mut style_traits::CssStringWriter,\n    ) -> fmt::Result {\n        let mut writer = CssWriter::new(dest);\n        writer.write_str(\"@layer \")?;\n        let mut first = true;\n        for name in &*self.names {\n            if !first {\n                writer.write_str(\", \")?;\n            }\n            first = false;\n            name.to_css(&mut writer)?;\n        }\n        writer.write_char(';')\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/loader.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The stylesheet loader is the abstraction used to trigger network requests\n//! for `@import` rules.\n\nuse crate::media_queries::MediaList;\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};\nuse crate::values::CssUrl;\nuse cssparser::SourceLocation;\nuse servo_arc::Arc;\n\n/// The stylesheet loader is the abstraction used to trigger network requests\n/// for `@import` rules.\npub trait StylesheetLoader {\n    /// Request a stylesheet after parsing a given `@import` rule, and return\n    /// the constructed `@import` rule.\n    fn request_stylesheet(\n        &self,\n        url: CssUrl,\n        location: SourceLocation,\n        lock: &SharedRwLock,\n        media: Arc<Locked<MediaList>>,\n        supports: Option<ImportSupportsCondition>,\n        layer: ImportLayer,\n    ) -> Arc<Locked<ImportRule>>;\n}\n"
  },
  {
    "path": "style/stylesheets/margin_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@margin`][margin] rule.\n//!\n//! [margin]: https://drafts.csswg.org/css-page-3/#margin-boxes\n\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse cssparser::{match_ignore_ascii_case, SourceLocation};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::CssStringWriter;\n\nmacro_rules! margin_rule_types {\n    ($($(#[$($meta:tt)+])* $id:ident => $val:literal,)+) => {\n        /// [`@margin`][margin] rule names.\n        ///\n        /// https://drafts.csswg.org/css-page-3/#margin-at-rules\n        #[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, ToShmem)]\n        #[repr(u8)]\n        pub enum MarginRuleType {\n            $($(#[$($meta)+])* $id,)+\n        }\n\n        /// All [`@margin`][margin] rule names, with a preceding '@'.\n        ///\n        /// This array lets us have just one single memory region used for\n        /// to_str, name, and the Debug implementation.\n        const MARGIN_RULE_AT_NAMES:&[&'static str] = &[\n            $( concat!('@', $val), )+\n        ];\n\n        impl MarginRuleType {\n            /// Matches the rule type for this name. This does not expect a\n            /// leading '@'.\n            pub fn match_name(name: &str) -> Option<Self> {\n                Some(match_ignore_ascii_case! { name,\n                    $( $val => MarginRuleType::$id, )+\n                    _ => return None,\n                })\n            }\n        }\n    }\n}\n\nmargin_rule_types! {\n    /// [`@top-left-corner`][top-left-corner] margin rule\n    ///\n    /// [top-left-corner] https://drafts.csswg.org/css-page-3/#top-left-corner-box-def\n    TopLeftCorner => \"top-left-corner\",\n    /// [`@top-left`][top-left] margin rule\n    ///\n    /// [top-left] https://drafts.csswg.org/css-page-3/#top-left-box-def\n    TopLeft => \"top-left\",\n    /// [`@top-center`][top-center] margin rule\n    ///\n    /// [top-center] https://drafts.csswg.org/css-page-3/#top-center-box-def\n    TopCenter => \"top-center\",\n    /// [`@top-right`][top-right] margin rule\n    ///\n    /// [top-right] https://drafts.csswg.org/css-page-3/#top-right-box-def\n    TopRight => \"top-right\",\n    /// [`@top-right-corner`][top-right-corner] margin rule\n    ///\n    /// [top-right-corner] https://drafts.csswg.org/css-page-3/#top-right-corner-box-def\n    TopRightCorner => \"top-right-corner\",\n    /// [`@bottom-left-corner`][bottom-left-corner] margin rule\n    ///\n    /// [bottom-left-corner] https://drafts.csswg.org/css-page-3/#bottom-left-corner-box-def\n    BottomLeftCorner => \"bottom-left-corner\",\n    /// [`@bottom-left`][bottom-left] margin rule\n    ///\n    /// [bottom-left] https://drafts.csswg.org/css-page-3/#bottom-left-box-def\n    BottomLeft => \"bottom-left\",\n    /// [`@bottom-center`][bottom-center] margin rule\n    ///\n    /// [bottom-center] https://drafts.csswg.org/css-page-3/#bottom-center-box-def\n    BottomCenter => \"bottom-center\",\n    /// [`@bottom-right`][bottom-right] margin rule\n    ///\n    /// [bottom-right] https://drafts.csswg.org/css-page-3/#bottom-right-box-def\n    BottomRight => \"bottom-right\",\n    /// [`@bottom-right-corner`][bottom-right-corner] margin rule\n    ///\n    /// [bottom-right-corner] https://drafts.csswg.org/css-page-3/#bottom-right-corner-box-def\n    BottomRightCorner => \"bottom-right-corner\",\n    /// [`@left-top`][left-top] margin rule\n    ///\n    /// [left-top] https://drafts.csswg.org/css-page-3/#left-top-box-def\n    LeftTop => \"left-top\",\n    /// [`@left-middle`][left-middle] margin rule\n    ///\n    /// [left-middle] https://drafts.csswg.org/css-page-3/#left-middle-box-def\n    LeftMiddle => \"left-middle\",\n    /// [`@left-bottom`][left-bottom] margin rule\n    ///\n    /// [left-bottom] https://drafts.csswg.org/css-page-3/#left-bottom-box-def\n    LeftBottom => \"left-bottom\",\n    /// [`@right-top`][right-top] margin rule\n    ///\n    /// [right-top] https://drafts.csswg.org/css-page-3/#right-top-box-def\n    RightTop => \"right-top\",\n    /// [`@right-middle`][right-middle] margin rule\n    ///\n    /// [right-middle] https://drafts.csswg.org/css-page-3/#right-middle-box-def\n    RightMiddle => \"right-middle\",\n    /// [`@right-bottom`][right-bottom] margin rule\n    ///\n    /// [right-bottom] https://drafts.csswg.org/css-page-3/#right-bottom-box-def\n    RightBottom => \"right-bottom\",\n}\n\nimpl MarginRuleType {\n    #[inline]\n    fn to_str(&self) -> &'static str {\n        &MARGIN_RULE_AT_NAMES[*self as usize]\n    }\n    #[inline]\n    fn name(&self) -> &'static str {\n        // Use the at-name array, skipping the first character to get\n        // the name without the @ sign.\n        &MARGIN_RULE_AT_NAMES[*self as usize][1..]\n    }\n}\n\n// Implement Debug manually so that it will share the same string memory as\n// MarginRuleType::name and MarginRuleType::to_str.\nimpl fmt::Debug for MarginRuleType {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {\n        f.write_str(self.name())\n    }\n}\n\n/// A [`@margin`][margin] rule.\n///\n/// [margin]: https://drafts.csswg.org/css-page-3/#margin-at-rules\n#[derive(Clone, Debug, ToShmem)]\npub struct MarginRule {\n    /// Type of this margin rule.\n    pub rule_type: MarginRuleType,\n    /// The declaration block this margin rule contains.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n    /// The source position this rule was found at.\n    pub source_location: SourceLocation,\n}\n\nimpl MarginRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)\n    }\n    /// Gets the name for this margin rule.\n    #[inline]\n    pub fn name(&self) -> &'static str {\n        self.rule_type.name()\n    }\n}\n\nimpl ToCssWithGuard for MarginRule {\n    /// Serialization of a margin-rule is not specced, this is adapted from how\n    /// page-rules and style-rules are serialized.\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(self.rule_type.to_str())?;\n        dest.write_str(\" { \")?;\n        let declaration_block = self.block.read_with(guard);\n        declaration_block.to_css(dest)?;\n        if !declaration_block.declarations().is_empty() {\n            dest.write_char(' ')?;\n        }\n        dest.write_char('}')\n    }\n}\n\nimpl DeepCloneWithLock for MarginRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        MarginRule {\n            rule_type: self.rule_type,\n            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/media_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! An [`@media`][media] rule.\n//!\n//! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media\n\nuse crate::derives::*;\nuse crate::media_queries::MediaList;\nuse crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::CssRules;\nuse crate::values::{computed, DashedIdent};\nuse crate::Atom;\nuse cssparser::Parser;\nuse cssparser::SourceLocation;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse selectors::kleene_value::KleeneValue;\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, ToCss};\n\n/// An [`@media`][media] rule.\n///\n/// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media\n#[derive(Debug, ToShmem)]\npub struct MediaRule {\n    /// The list of media queries used by this media rule.\n    pub media_queries: Arc<Locked<MediaList>>,\n    /// The nested rules to this media rule.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this media rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl MediaRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl ToCssWithGuard for MediaRule {\n    // Serialization of MediaRule is not specced.\n    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@media \")?;\n        self.media_queries\n            .read_with(guard)\n            .to_css(&mut CssWriter::new(dest))?;\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for MediaRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let media_queries = self.media_queries.read_with(guard);\n        let rules = self.rules.read_with(guard);\n        MediaRule {\n            media_queries: Arc::new(lock.wrap(media_queries.clone())),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// The condition associated to a custom-media query.\n#[derive(Debug, ToShmem, Clone, MallocSizeOf)]\npub enum CustomMediaCondition {\n    /// Unconditionally true.\n    True,\n    /// Unconditionally false.\n    False,\n    /// A MediaList.\n    MediaList(#[ignore_malloc_size_of = \"Arc\"] Arc<Locked<MediaList>>),\n}\n\nimpl CustomMediaCondition {\n    /// Parses the possible keywords for this condition.\n    pub(crate) fn parse_keyword<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"true\" => Self::True,\n            \"false\" => Self::False,\n        })\n    }\n}\n\nimpl DeepCloneWithLock for CustomMediaCondition {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        match self {\n            Self::True => Self::True,\n            Self::False => Self::False,\n            Self::MediaList(ref m) => {\n                Self::MediaList(Arc::new(lock.wrap(m.read_with(guard).clone())))\n            },\n        }\n    }\n}\n\n/// A `@custom-media` rule.\n/// https://drafts.csswg.org/mediaqueries-5/#custom-mq\n#[derive(Debug, ToShmem)]\npub struct CustomMediaRule {\n    /// The name of the custom media rule.\n    pub name: DashedIdent,\n    /// The list of media conditions used by this media rule.\n    pub condition: CustomMediaCondition,\n    /// The source position where this media rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl DeepCloneWithLock for CustomMediaRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        Self {\n            name: self.name.clone(),\n            condition: self.condition.deep_clone_with_lock(lock, guard),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for CustomMediaRule {\n    // Serialization of MediaRule is not specced.\n    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@custom-media \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_char(' ')?;\n        match self.condition {\n            CustomMediaCondition::True => dest.write_str(\"true\"),\n            CustomMediaCondition::False => dest.write_str(\"false\"),\n            CustomMediaCondition::MediaList(ref m) => {\n                m.read_with(guard).to_css(&mut CssWriter::new(dest))\n            },\n        }\n    }\n}\n\n/// The currently effective @custom-media conditions.\npub type CustomMediaMap = PrecomputedHashMap<Atom, CustomMediaCondition>;\n\n/// A struct that can evaluate custom media conditions.\npub struct CustomMediaEvaluator<'a> {\n    map: Option<(&'a CustomMediaMap, &'a SharedRwLockReadGuard<'a>)>,\n    /// Set of media queries we're currently evaluating, needed for cycle detection.\n    currently_evaluating: PrecomputedHashSet<Atom>,\n}\n\nimpl<'a> CustomMediaEvaluator<'a> {\n    /// Construct a new custom media evaluator with the given map and guard.\n    pub fn new(map: &'a CustomMediaMap, guard: &'a SharedRwLockReadGuard<'a>) -> Self {\n        Self {\n            map: Some((map, guard)),\n            currently_evaluating: Default::default(),\n        }\n    }\n\n    /// Returns an evaluator that can't evaluate custom queries.\n    pub fn none() -> Self {\n        Self {\n            map: None,\n            currently_evaluating: Default::default(),\n        }\n    }\n\n    /// Evaluates a custom media query.\n    pub fn matches(&mut self, ident: &DashedIdent, context: &computed::Context) -> KleeneValue {\n        let Some((map, guard)) = self.map else {\n            return KleeneValue::Unknown;\n        };\n        let Some(condition) = map.get(&ident.0) else {\n            return KleeneValue::Unknown;\n        };\n        let media = match condition {\n            CustomMediaCondition::True => return KleeneValue::True,\n            CustomMediaCondition::False => return KleeneValue::False,\n            CustomMediaCondition::MediaList(ref m) => m,\n        };\n        if !self.currently_evaluating.insert(ident.0.clone()) {\n            // Found a cycle while evaluating this rule.\n            return KleeneValue::False;\n        }\n        let result = media.read_with(guard).matches(context, self);\n        self.currently_evaluating.remove(&ident.0);\n        result.into()\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Style sheets and their CSS rules.\n\nmod appearance_base_rule;\npub mod container_rule;\nmod counter_style_rule;\nmod document_rule;\nmod font_face_rule;\npub mod font_feature_values_rule;\npub mod font_palette_values_rule;\npub mod import_rule;\npub mod keyframes_rule;\npub mod layer_rule;\nmod loader;\nmod margin_rule;\nmod media_rule;\nmod namespace_rule;\nmod nested_declarations_rule;\npub mod origin;\nmod page_rule;\npub mod position_try_rule;\nmod property_rule;\nmod rule_list;\nmod rule_parser;\nmod rules_iterator;\npub mod scope_rule;\nmod starting_style_rule;\nmod style_rule;\nmod stylesheet;\npub mod supports_rule;\npub mod view_transition_rule;\n\nuse crate::derives::*;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::sugar::refptr::RefCounted;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::{bindings, structs};\nuse crate::parser::{NestingContext, ParserContext};\nuse crate::properties::{parse_property_declaration_list, PropertyDeclarationBlock};\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse cssparser::{parse_one_rule, Parser, ParserInput};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse std::borrow::Cow;\nuse std::fmt::{self, Write};\n#[cfg(feature = \"gecko\")]\nuse std::mem::{self, ManuallyDrop};\nuse style_traits::{CssStringWriter, ParsingMode};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\npub use self::appearance_base_rule::AppearanceBaseRule;\npub use self::container_rule::ContainerRule;\npub use self::counter_style_rule::CounterStyleRule;\npub use self::document_rule::DocumentRule;\npub use self::font_face_rule::FontFaceRule;\npub use self::font_feature_values_rule::FontFeatureValuesRule;\npub use self::font_palette_values_rule::FontPaletteValuesRule;\npub use self::import_rule::ImportRule;\npub use self::keyframes_rule::KeyframesRule;\npub use self::layer_rule::{LayerBlockRule, LayerStatementRule};\npub use self::loader::StylesheetLoader;\npub use self::margin_rule::{MarginRule, MarginRuleType};\npub use self::media_rule::{\n    CustomMediaCondition, CustomMediaEvaluator, CustomMediaMap, CustomMediaRule, MediaRule,\n};\npub use self::namespace_rule::NamespaceRule;\npub use self::nested_declarations_rule::NestedDeclarationsRule;\npub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};\npub use self::page_rule::{PagePseudoClassFlags, PageRule, PageSelector, PageSelectors};\npub use self::position_try_rule::PositionTryRule;\npub use self::property_rule::PropertyRule;\npub use self::rule_list::CssRules;\npub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser};\npub use self::rules_iterator::{AllRules, EffectiveRules};\npub use self::rules_iterator::{\n    EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator,\n};\npub use self::scope_rule::ScopeRule;\npub use self::starting_style_rule::StartingStyleRule;\npub use self::style_rule::StyleRule;\npub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};\npub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};\npub use self::stylesheet::{StylesheetContents, StylesheetInDocument};\npub use self::supports_rule::SupportsRule;\npub use self::view_transition_rule::{NavigationType, ViewTransitionRule};\n\n/// The CORS mode used for a CSS load.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]\npub enum CorsMode {\n    /// No CORS mode, so cross-origin loads can be done.\n    None,\n    /// Anonymous CORS request.\n    Anonymous,\n}\n\n/// Extra data that the backend may need to resolve url values.\n///\n/// If the usize's lowest bit is 0, then this is a strong reference to a\n/// structs::URLExtraData object.\n///\n/// Otherwise, shifting the usize's bits the right by one gives the\n/// UserAgentStyleSheetID value corresponding to the style sheet whose\n/// URLExtraData this is, which is stored in URLExtraData_sShared.  We don't\n/// hold a strong reference to that object from here, but we rely on that\n/// array's objects being held alive until shutdown.\n///\n/// We use this packed representation rather than an enum so that\n/// `from_ptr_ref` can work.\n#[cfg(feature = \"gecko\")]\n// Although deriving MallocSizeOf means it always returns 0, that is fine because UrlExtraData\n// objects are reference-counted.\n#[derive(MallocSizeOf, PartialEq)]\n#[repr(C)]\npub struct UrlExtraData(usize);\n\n/// Extra data that the backend may need to resolve url values.\n#[cfg(feature = \"servo\")]\n#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]\npub struct UrlExtraData(#[ignore_malloc_size_of = \"Arc\"] pub Arc<::url::Url>);\n\n#[cfg(feature = \"servo\")]\nimpl UrlExtraData {\n    /// True if this URL scheme is chrome.\n    pub fn chrome_rules_enabled(&self) -> bool {\n        self.0.scheme() == \"chrome\"\n    }\n\n    /// Get the interior Url as a string.\n    pub fn as_str(&self) -> &str {\n        self.0.as_str()\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl From<::url::Url> for UrlExtraData {\n    fn from(url: ::url::Url) -> Self {\n        Self(Arc::new(url))\n    }\n}\n\n#[cfg(not(feature = \"gecko\"))]\nimpl ToShmem for UrlExtraData {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        unimplemented!(\"If servo wants to share stylesheets across processes, ToShmem for Url must be implemented\");\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl Clone for UrlExtraData {\n    fn clone(&self) -> UrlExtraData {\n        UrlExtraData::new(self.ptr())\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl Drop for UrlExtraData {\n    fn drop(&mut self) {\n        // No need to release when we have an index into URLExtraData_sShared.\n        if self.0 & 1 == 0 {\n            unsafe {\n                self.as_ref().release();\n            }\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl ToShmem for UrlExtraData {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        if self.0 & 1 == 0 {\n            let shared_extra_datas = unsafe {\n                std::ptr::addr_of!(structs::URLExtraData_sShared)\n                    .as_ref()\n                    .unwrap()\n            };\n            let self_ptr = self.as_ref() as *const _ as *mut _;\n            let sheet_id = shared_extra_datas\n                .iter()\n                .position(|r| r.mRawPtr == self_ptr);\n            let sheet_id = match sheet_id {\n                Some(id) => id,\n                None => {\n                    return Err(String::from(\n                        \"ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \\\n                         URLExtraData::sShared\",\n                    ));\n                },\n            };\n            Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1)))\n        } else {\n            Ok(ManuallyDrop::new(UrlExtraData(self.0)))\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl UrlExtraData {\n    /// Create a new UrlExtraData wrapping a pointer to the specified Gecko\n    /// URLExtraData object.\n    pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData {\n        unsafe {\n            (*ptr).addref();\n        }\n        UrlExtraData(ptr as usize)\n    }\n\n    /// True if this URL scheme is chrome.\n    #[inline]\n    pub fn chrome_rules_enabled(&self) -> bool {\n        self.as_ref().mChromeRulesEnabled\n    }\n\n    /// Create a reference to this `UrlExtraData` from a reference to pointer.\n    ///\n    /// The pointer must be valid and non null.\n    ///\n    /// This method doesn't touch refcount.\n    #[inline]\n    pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self {\n        mem::transmute(ptr)\n    }\n\n    /// Returns a pointer to the Gecko URLExtraData object.\n    pub fn ptr(&self) -> *mut structs::URLExtraData {\n        if self.0 & 1 == 0 {\n            self.0 as *mut structs::URLExtraData\n        } else {\n            unsafe {\n                let sheet_id = self.0 >> 1;\n                structs::URLExtraData_sShared[sheet_id].mRawPtr\n            }\n        }\n    }\n\n    fn as_ref(&self) -> &structs::URLExtraData {\n        unsafe { &*(self.ptr() as *const structs::URLExtraData) }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl fmt::Debug for UrlExtraData {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        macro_rules! define_debug_struct {\n            ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => {\n                struct $struct_name(*mut structs::$gecko_class);\n                impl fmt::Debug for $struct_name {\n                    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n                        use nsstring::nsCString;\n                        let mut spec = nsCString::new();\n                        unsafe {\n                            bindings::$debug_fn(self.0, &mut spec);\n                        }\n                        spec.fmt(formatter)\n                    }\n                }\n            };\n        }\n\n        define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug);\n        define_debug_struct!(\n            DebugReferrerInfo,\n            nsIReferrerInfo,\n            Gecko_nsIReferrerInfo_Debug\n        );\n\n        formatter\n            .debug_struct(\"URLExtraData\")\n            .field(\"chrome_rules_enabled\", &self.chrome_rules_enabled())\n            .field(\"base\", &DebugURI(self.as_ref().mBaseURI.raw()))\n            .field(\n                \"referrer\",\n                &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()),\n            )\n            .finish()\n    }\n}\n\n// XXX We probably need to figure out whether we should mark Eq here.\n// It is currently marked so because properties::UnparsedValue wants Eq.\n#[cfg(feature = \"gecko\")]\nimpl Eq for UrlExtraData {}\n\n/// Serialize a page or style rule, starting with the opening brace.\n///\n/// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule\n///\n/// This is not properly specified for page-rules, but we will apply the\n/// same process.\nfn style_or_page_rule_to_css(\n    rules: Option<&Arc<Locked<CssRules>>>,\n    block: &Locked<PropertyDeclarationBlock>,\n    guard: &SharedRwLockReadGuard,\n    dest: &mut CssStringWriter,\n) -> fmt::Result {\n    // Write the opening brace. The caller needs to serialize up to this point.\n    dest.write_char('{')?;\n\n    // Step 2\n    let declaration_block = block.read_with(guard);\n    let has_declarations = !declaration_block.declarations().is_empty();\n\n    // Step 3\n    if let Some(ref rules) = rules {\n        let rules = rules.read_with(guard);\n        // Step 6 (here because it's more convenient)\n        if !rules.is_empty() {\n            if has_declarations {\n                dest.write_str(\"\\n  \")?;\n                declaration_block.to_css(dest)?;\n            }\n            return rules.to_css_block_without_opening(guard, dest);\n        }\n    }\n\n    // Steps 4 & 5\n    if has_declarations {\n        dest.write_char(' ')?;\n        declaration_block.to_css(dest)?;\n    }\n    dest.write_str(\" }\")\n}\n\n/// A CSS rule.\n///\n/// TODO(emilio): Lots of spec links should be around.\n#[derive(Clone, Debug, ToShmem)]\n#[allow(missing_docs)]\npub enum CssRule {\n    Style(Arc<Locked<StyleRule>>),\n    // No Charset here, CSSCharsetRule has been removed from CSSOM\n    // https://drafts.csswg.org/cssom/#changes-from-5-december-2013\n    Namespace(Arc<NamespaceRule>),\n    Import(Arc<Locked<ImportRule>>),\n    Media(Arc<MediaRule>),\n    CustomMedia(Arc<CustomMediaRule>),\n    Container(Arc<ContainerRule>),\n    FontFace(Arc<Locked<FontFaceRule>>),\n    FontFeatureValues(Arc<FontFeatureValuesRule>),\n    FontPaletteValues(Arc<FontPaletteValuesRule>),\n    CounterStyle(Arc<Locked<CounterStyleRule>>),\n    Keyframes(Arc<Locked<KeyframesRule>>),\n    Margin(Arc<MarginRule>),\n    Supports(Arc<SupportsRule>),\n    Page(Arc<Locked<PageRule>>),\n    Property(Arc<PropertyRule>),\n    Document(Arc<DocumentRule>),\n    LayerBlock(Arc<LayerBlockRule>),\n    LayerStatement(Arc<LayerStatementRule>),\n    Scope(Arc<ScopeRule>),\n    StartingStyle(Arc<StartingStyleRule>),\n    AppearanceBase(Arc<AppearanceBaseRule>),\n    PositionTry(Arc<Locked<PositionTryRule>>),\n    NestedDeclarations(Arc<Locked<NestedDeclarationsRule>>),\n    ViewTransition(Arc<ViewTransitionRule>),\n}\n\nimpl CssRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        match *self {\n            // Not all fields are currently fully measured. Extra measurement\n            // may be added later.\n            CssRule::Namespace(_) => 0,\n\n            // We don't need to measure ImportRule::stylesheet because we measure\n            // it on the C++ side in the child list of the ServoStyleSheet.\n            CssRule::Import(_) => 0,\n\n            CssRule::Style(ref lock) => {\n                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)\n            },\n            CssRule::Media(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::CustomMedia(ref arc) => {\n                // Measurement of other fields might be added later.\n                arc.unconditional_shallow_size_of(ops)\n            },\n            CssRule::Container(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::FontFace(_) => 0,\n            CssRule::FontFeatureValues(_) => 0,\n            CssRule::FontPaletteValues(_) => 0,\n            CssRule::CounterStyle(_) => 0,\n            CssRule::Keyframes(_) => 0,\n            CssRule::Margin(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::Supports(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::Page(ref lock) => {\n                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)\n            },\n            CssRule::Property(ref rule) => {\n                rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)\n            },\n            CssRule::Document(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::StartingStyle(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            CssRule::AppearanceBase(ref arc) => {\n                arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)\n            },\n            // TODO(emilio): Add memory reporting for these rules.\n            CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,\n            CssRule::Scope(ref rule) => {\n                rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)\n            },\n            CssRule::PositionTry(ref lock) => {\n                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)\n            },\n            CssRule::NestedDeclarations(ref lock) => {\n                lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops)\n            },\n            CssRule::ViewTransition(ref rule) => {\n                use malloc_size_of::MallocSizeOf;\n\n                rule.unconditional_shallow_size_of(ops) + rule.size_of(ops)\n            },\n        }\n    }\n\n    fn is_empty_nested_declarations(&self, guard: &SharedRwLockReadGuard) -> bool {\n        match *self {\n            CssRule::NestedDeclarations(ref lock) => {\n                lock.read_with(guard).block.read_with(guard).is_empty()\n            },\n            _ => false,\n        }\n    }\n\n    /// Returns the children rules of this rule, if any.\n    pub fn children<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] {\n        match *self {\n            CssRule::Namespace(_)\n            | CssRule::FontFace(_)\n            | CssRule::CounterStyle(_)\n            | CssRule::CustomMedia(_)\n            | CssRule::Keyframes(_)\n            | CssRule::Margin(_)\n            | CssRule::Property(_)\n            | CssRule::LayerStatement(_)\n            | CssRule::FontFeatureValues(_)\n            | CssRule::FontPaletteValues(_)\n            | CssRule::NestedDeclarations(_)\n            | CssRule::PositionTry(_)\n            | CssRule::ViewTransition(_) => &[],\n            CssRule::Page(ref page_rule) => {\n                let page_rule = page_rule.read_with(guard);\n                let rules = page_rule.rules.read_with(guard);\n                rules.0.as_slice()\n            },\n            CssRule::Style(ref style_rule) => {\n                let style_rule = style_rule.read_with(guard);\n                match style_rule.rules.as_ref() {\n                    Some(r) => r.read_with(guard).0.as_slice(),\n                    None => &[],\n                }\n            },\n            CssRule::Import(ref import_rule) => {\n                let import_rule = import_rule.read_with(guard);\n                import_rule.stylesheet.rules(guard)\n            },\n            CssRule::Document(ref doc_rule) => doc_rule.rules.read_with(guard).0.as_slice(),\n            CssRule::Container(ref container_rule) => {\n                container_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::Media(ref media_rule) => media_rule.rules.read_with(guard).0.as_slice(),\n            CssRule::Supports(ref supports_rule) => {\n                supports_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::LayerBlock(ref layer_rule) => layer_rule.rules.read_with(guard).0.as_slice(),\n            CssRule::Scope(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n            CssRule::StartingStyle(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n            CssRule::AppearanceBase(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n        }\n    }\n}\n\n// These aliases are required on Gecko side to avoid generating bindings for `Locked`.\n/// Alias for a locked style rule.\npub type LockedStyleRule = Locked<StyleRule>;\n/// Alias for a locked import rule.\npub type LockedImportRule = Locked<ImportRule>;\n/// Alias for a locked font-face rule.\npub type LockedFontFaceRule = Locked<FontFaceRule>;\n/// Alias for a locked counter-style rule.\npub type LockedCounterStyleRule = Locked<CounterStyleRule>;\n/// Alias for a locked keyframes rule.\npub type LockedKeyframesRule = Locked<KeyframesRule>;\n/// Alias for a locked page rule.\npub type LockedPageRule = Locked<PageRule>;\n/// Alias for a locked position-try rule.\npub type LockedPositionTryRule = Locked<PositionTryRule>;\n/// Alias for a locked nested declarations rule.\npub type LockedNestedDeclarationsRule = Locked<NestedDeclarationsRule>;\n\n/// A CSS rule reference. Should mirror `CssRule`.\n#[repr(C)]\n#[allow(missing_docs)]\npub enum CssRuleRef<'a> {\n    Style(&'a LockedStyleRule),\n    Namespace(&'a NamespaceRule),\n    Import(&'a LockedImportRule),\n    Media(&'a MediaRule),\n    CustomMedia(&'a CustomMediaRule),\n    Container(&'a ContainerRule),\n    FontFace(&'a LockedFontFaceRule),\n    FontFeatureValues(&'a FontFeatureValuesRule),\n    FontPaletteValues(&'a FontPaletteValuesRule),\n    CounterStyle(&'a LockedCounterStyleRule),\n    Keyframes(&'a LockedKeyframesRule),\n    Margin(&'a MarginRule),\n    Supports(&'a SupportsRule),\n    Page(&'a LockedPageRule),\n    Property(&'a PropertyRule),\n    Document(&'a DocumentRule),\n    LayerBlock(&'a LayerBlockRule),\n    LayerStatement(&'a LayerStatementRule),\n    Scope(&'a ScopeRule),\n    StartingStyle(&'a StartingStyleRule),\n    AppearanceBase(&'a AppearanceBaseRule),\n    PositionTry(&'a LockedPositionTryRule),\n    NestedDeclarations(&'a LockedNestedDeclarationsRule),\n    ViewTransition(&'a ViewTransitionRule),\n}\n\nimpl<'a> From<&'a CssRule> for CssRuleRef<'a> {\n    fn from(value: &'a CssRule) -> Self {\n        match value {\n            CssRule::Style(r) => CssRuleRef::Style(r.as_ref()),\n            CssRule::Namespace(r) => CssRuleRef::Namespace(r.as_ref()),\n            CssRule::Import(r) => CssRuleRef::Import(r.as_ref()),\n            CssRule::Media(r) => CssRuleRef::Media(r.as_ref()),\n            CssRule::CustomMedia(r) => CssRuleRef::CustomMedia(r.as_ref()),\n            CssRule::Container(r) => CssRuleRef::Container(r.as_ref()),\n            CssRule::FontFace(r) => CssRuleRef::FontFace(r.as_ref()),\n            CssRule::FontFeatureValues(r) => CssRuleRef::FontFeatureValues(r.as_ref()),\n            CssRule::FontPaletteValues(r) => CssRuleRef::FontPaletteValues(r.as_ref()),\n            CssRule::CounterStyle(r) => CssRuleRef::CounterStyle(r.as_ref()),\n            CssRule::Keyframes(r) => CssRuleRef::Keyframes(r.as_ref()),\n            CssRule::Margin(r) => CssRuleRef::Margin(r.as_ref()),\n            CssRule::Supports(r) => CssRuleRef::Supports(r.as_ref()),\n            CssRule::Page(r) => CssRuleRef::Page(r.as_ref()),\n            CssRule::Property(r) => CssRuleRef::Property(r.as_ref()),\n            CssRule::Document(r) => CssRuleRef::Document(r.as_ref()),\n            CssRule::LayerBlock(r) => CssRuleRef::LayerBlock(r.as_ref()),\n            CssRule::LayerStatement(r) => CssRuleRef::LayerStatement(r.as_ref()),\n            CssRule::Scope(r) => CssRuleRef::Scope(r.as_ref()),\n            CssRule::StartingStyle(r) => CssRuleRef::StartingStyle(r.as_ref()),\n            CssRule::AppearanceBase(r) => CssRuleRef::AppearanceBase(r.as_ref()),\n            CssRule::PositionTry(r) => CssRuleRef::PositionTry(r.as_ref()),\n            CssRule::NestedDeclarations(r) => CssRuleRef::NestedDeclarations(r.as_ref()),\n            CssRule::ViewTransition(r) => CssRuleRef::ViewTransition(r.as_ref()),\n        }\n    }\n}\n\n/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)]\n#[repr(u8)]\npub enum CssRuleType {\n    // https://drafts.csswg.org/cssom/#the-cssrule-interface\n    Style = 1,\n    // Charset = 2, // Historical\n    Import = 3,\n    Media = 4,\n    FontFace = 5,\n    Page = 6,\n    // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl\n    Keyframes = 7,\n    Keyframe = 8,\n    // https://drafts.csswg.org/cssom/#the-cssrule-interface\n    Margin = 9,\n    Namespace = 10,\n    // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface\n    CounterStyle = 11,\n    // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface\n    Supports = 12,\n    // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface\n    Document = 13,\n    // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues\n    FontFeatureValues = 14,\n    // After viewport, all rules should return 0 from the API, but we still need\n    // a constant somewhere.\n    LayerBlock = 16,\n    LayerStatement = 17,\n    Container = 18,\n    FontPaletteValues = 19,\n    // 20 is an arbitrary number to use for Property.\n    Property = 20,\n    Scope = 21,\n    // https://drafts.csswg.org/css-transitions-2/#the-cssstartingstylerule-interface\n    StartingStyle = 22,\n    // https://drafts.csswg.org/css-anchor-position-1/#om-position-try\n    PositionTry = 23,\n    // https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule\n    NestedDeclarations = 24,\n    CustomMedia = 25,\n    // Internal rule for UA stylesheet appearance-dependent styles.\n    AppearanceBase = 26,\n    // https://drafts.csswg.org/css-view-transitions-2/#view-transition-rule\n    ViewTransition = 27,\n}\n\nimpl CssRuleType {\n    /// Returns a bit that identifies this rule type.\n    #[inline]\n    pub const fn bit(self) -> u32 {\n        1 << self as u32\n    }\n}\n\n/// Set of rule types.\n#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]\npub struct CssRuleTypes(u32);\n\nimpl From<CssRuleType> for CssRuleTypes {\n    fn from(ty: CssRuleType) -> Self {\n        Self(ty.bit())\n    }\n}\n\nimpl CssRuleTypes {\n    /// Rules where !important declarations are forbidden.\n    pub const IMPORTANT_FORBIDDEN: Self =\n        Self(CssRuleType::PositionTry.bit() | CssRuleType::Keyframe.bit());\n\n    /// Returns whether the rule is in the current set.\n    #[inline]\n    pub fn contains(self, ty: CssRuleType) -> bool {\n        self.0 & ty.bit() != 0\n    }\n\n    /// Returns all the rules specified in the set.\n    #[inline]\n    pub fn bits(self) -> u32 {\n        self.0\n    }\n\n    /// Creates a raw CssRuleTypes bitfield.\n    #[inline]\n    pub fn from_bits(bits: u32) -> Self {\n        Self(bits)\n    }\n\n    /// Returns whether the rule set is empty.\n    #[inline]\n    pub fn is_empty(self) -> bool {\n        self.0 == 0\n    }\n\n    /// Inserts a rule type into the set.\n    #[inline]\n    pub fn insert(&mut self, ty: CssRuleType) {\n        self.0 |= ty.bit()\n    }\n\n    /// Returns whether any of the types intersect.\n    #[inline]\n    pub fn intersects(self, other: Self) -> bool {\n        self.0 & other.0 != 0\n    }\n}\n\n#[allow(missing_docs)]\npub enum RulesMutateError {\n    Syntax,\n    IndexSize,\n    HierarchyRequest,\n    InvalidState,\n}\n\nimpl CssRule {\n    /// Returns the CSSOM rule type of this rule.\n    pub fn rule_type(&self) -> CssRuleType {\n        match *self {\n            CssRule::Style(_) => CssRuleType::Style,\n            CssRule::Import(_) => CssRuleType::Import,\n            CssRule::Media(_) => CssRuleType::Media,\n            CssRule::CustomMedia(_) => CssRuleType::CustomMedia,\n            CssRule::FontFace(_) => CssRuleType::FontFace,\n            CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues,\n            CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues,\n            CssRule::CounterStyle(_) => CssRuleType::CounterStyle,\n            CssRule::Keyframes(_) => CssRuleType::Keyframes,\n            CssRule::Margin(_) => CssRuleType::Margin,\n            CssRule::Namespace(_) => CssRuleType::Namespace,\n            CssRule::Supports(_) => CssRuleType::Supports,\n            CssRule::Page(_) => CssRuleType::Page,\n            CssRule::Property(_) => CssRuleType::Property,\n            CssRule::Document(_) => CssRuleType::Document,\n            CssRule::LayerBlock(_) => CssRuleType::LayerBlock,\n            CssRule::LayerStatement(_) => CssRuleType::LayerStatement,\n            CssRule::Container(_) => CssRuleType::Container,\n            CssRule::Scope(_) => CssRuleType::Scope,\n            CssRule::StartingStyle(_) => CssRuleType::StartingStyle,\n            CssRule::AppearanceBase(_) => CssRuleType::AppearanceBase,\n            CssRule::PositionTry(_) => CssRuleType::PositionTry,\n            CssRule::NestedDeclarations(_) => CssRuleType::NestedDeclarations,\n            CssRule::ViewTransition(_) => CssRuleType::ViewTransition,\n        }\n    }\n\n    /// Parse a CSS rule.\n    ///\n    /// This mostly implements steps 3..7 of https://drafts.csswg.org/cssom/#insert-a-css-rule\n    pub fn parse(\n        css: &str,\n        insert_rule_context: InsertRuleContext,\n        parent_stylesheet_contents: &StylesheetContents,\n        shared_lock: &SharedRwLock,\n        loader: Option<&dyn StylesheetLoader>,\n        allow_import_rules: AllowImportRules,\n    ) -> Result<Self, RulesMutateError> {\n        let url_data = &parent_stylesheet_contents.url_data;\n        let namespaces = &parent_stylesheet_contents.namespaces;\n        let mut context = ParserContext::new(\n            parent_stylesheet_contents.origin,\n            &url_data,\n            None,\n            ParsingMode::DEFAULT,\n            parent_stylesheet_contents.quirks_mode,\n            Cow::Borrowed(&*namespaces),\n            None,\n            None,\n            /* attr_taint */ Default::default(),\n        );\n        // Override the nesting context with existing data.\n        context.nesting_context = NestingContext::new(\n            insert_rule_context.containing_rule_types,\n            insert_rule_context.parse_relative_rule_type,\n        );\n\n        let state = if !insert_rule_context.containing_rule_types.is_empty() {\n            State::Body\n        } else if insert_rule_context.index == 0 {\n            State::Start\n        } else {\n            let index = insert_rule_context.index;\n            insert_rule_context.max_rule_state_at_index(index - 1)\n        };\n\n        let mut input = ParserInput::new(css);\n        let mut input = Parser::new(&mut input);\n\n        // nested rules are in the body state\n        let mut parser = TopLevelRuleParser {\n            context,\n            shared_lock: &shared_lock,\n            loader,\n            state,\n            dom_error: None,\n            insert_rule_context: Some(insert_rule_context),\n            allow_import_rules,\n            declaration_parser_state: Default::default(),\n            first_declaration_block: Default::default(),\n            wants_first_declaration_block: false,\n            error_reporting_state: Default::default(),\n            rules: Default::default(),\n        };\n\n        if input\n            .try_parse(|input| parse_one_rule(input, &mut parser))\n            .is_ok()\n        {\n            return Ok(parser.rules.pop().unwrap());\n        }\n\n        let error = parser.dom_error.take().unwrap_or(RulesMutateError::Syntax);\n        // If new rule is a syntax error, and nested is set, perform the following substeps:\n        if matches!(error, RulesMutateError::Syntax) && parser.can_parse_declarations() {\n            let declarations = parse_property_declaration_list(&parser.context, &mut input, &[]);\n            if !declarations.is_empty() {\n                return Ok(CssRule::NestedDeclarations(Arc::new(\n                    parser.shared_lock.wrap(NestedDeclarationsRule {\n                        block: Arc::new(parser.shared_lock.wrap(declarations)),\n                        source_location: input.current_source_location(),\n                    }),\n                )));\n            }\n        }\n        Err(error)\n    }\n}\n\nimpl DeepCloneWithLock for CssRule {\n    /// Deep clones this CssRule.\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> CssRule {\n        match *self {\n            CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()),\n            CssRule::Import(ref arc) => {\n                let rule = arc.read_with(guard).deep_clone_with_lock(lock, guard);\n                CssRule::Import(Arc::new(lock.wrap(rule)))\n            },\n            CssRule::Style(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::Style(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))\n            },\n            CssRule::Container(ref arc) => {\n                CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::Media(ref arc) => {\n                CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::CustomMedia(ref arc) => {\n                CssRule::CustomMedia(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::FontFace(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::FontFace(Arc::new(lock.wrap(rule.clone())))\n            },\n            CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()),\n            CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()),\n            CssRule::CounterStyle(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone())))\n            },\n            CssRule::Keyframes(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::Keyframes(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))\n            },\n            CssRule::Margin(ref arc) => {\n                CssRule::Margin(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::Supports(ref arc) => {\n                CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::Page(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::Page(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))\n            },\n            CssRule::Property(ref arc) => {\n                // @property rules are immutable, so we don't need any of the `Locked`\n                // shenanigans, actually, and can just share the rule.\n                CssRule::Property(arc.clone())\n            },\n            CssRule::Document(ref arc) => {\n                CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()),\n            CssRule::LayerBlock(ref arc) => {\n                CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::Scope(ref arc) => {\n                CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::StartingStyle(ref arc) => {\n                CssRule::StartingStyle(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::AppearanceBase(ref arc) => {\n                CssRule::AppearanceBase(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n            CssRule::PositionTry(ref arc) => {\n                let rule = arc.read_with(guard);\n                CssRule::PositionTry(Arc::new(lock.wrap(rule.deep_clone_with_lock(lock, guard))))\n            },\n            CssRule::NestedDeclarations(ref arc) => {\n                let decls = arc.read_with(guard);\n                CssRule::NestedDeclarations(Arc::new(lock.wrap(decls.clone())))\n            },\n            CssRule::ViewTransition(ref arc) => {\n                CssRule::ViewTransition(Arc::new(arc.deep_clone_with_lock(lock, guard)))\n            },\n        }\n    }\n}\n\nimpl ToCssWithGuard for CssRule {\n    // https://drafts.csswg.org/cssom/#serialize-a-css-rule\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        match *self {\n            CssRule::Namespace(ref rule) => rule.to_css(guard, dest),\n            CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest),\n            CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest),\n            CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::Margin(ref rule) => rule.to_css(guard, dest),\n            CssRule::Media(ref rule) => rule.to_css(guard, dest),\n            CssRule::CustomMedia(ref rule) => rule.to_css(guard, dest),\n            CssRule::Supports(ref rule) => rule.to_css(guard, dest),\n            CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::Property(ref rule) => rule.to_css(guard, dest),\n            CssRule::Document(ref rule) => rule.to_css(guard, dest),\n            CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest),\n            CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest),\n            CssRule::Container(ref rule) => rule.to_css(guard, dest),\n            CssRule::Scope(ref rule) => rule.to_css(guard, dest),\n            CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest),\n            CssRule::AppearanceBase(ref rule) => rule.to_css(guard, dest),\n            CssRule::PositionTry(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::NestedDeclarations(ref lock) => lock.read_with(guard).to_css(guard, dest),\n            CssRule::ViewTransition(ref rule) => rule.to_css(guard, dest),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/namespace_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! The `@namespace` at-rule.\n\nuse crate::derives::*;\nuse crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::{Namespace, Prefix};\nuse cssparser::SourceLocation;\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ToCss};\n\n/// A `@namespace` rule.\n#[derive(Clone, Debug, PartialEq, ToShmem)]\n#[allow(missing_docs)]\npub struct NamespaceRule {\n    /// The namespace prefix, and `None` if it's the default Namespace\n    pub prefix: Option<Prefix>,\n    /// The actual namespace url.\n    pub url: Namespace,\n    /// The source location this rule was found at.\n    pub source_location: SourceLocation,\n}\n\nimpl ToCssWithGuard for NamespaceRule {\n    // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule\n    fn to_css(\n        &self,\n        _guard: &SharedRwLockReadGuard,\n        dest_str: &mut CssStringWriter,\n    ) -> fmt::Result {\n        let mut dest = CssWriter::new(dest_str);\n        dest.write_str(\"@namespace \")?;\n        if let Some(ref prefix) = self.prefix {\n            prefix.to_css(&mut dest)?;\n            dest.write_char(' ')?;\n        }\n        dest.write_str(\"url(\")?;\n        self.url.to_string().to_css(&mut dest)?;\n        dest.write_str(\");\")\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/nested_declarations_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A nested declarations rule.\n//! https://drafts.csswg.org/css-nesting-1/#nested-declarations-rule\n\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse cssparser::SourceLocation;\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse style_traits::CssStringWriter;\n\n/// A nested declarations rule.\n#[derive(Clone, Debug, ToShmem)]\npub struct NestedDeclarationsRule {\n    /// The declarations.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n    /// The source position this rule was found at.\n    pub source_location: SourceLocation,\n}\n\nimpl NestedDeclarationsRule {\n    /// Measure heap usage.\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)\n    }\n}\n\nimpl DeepCloneWithLock for NestedDeclarationsRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        Self {\n            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for NestedDeclarationsRule {\n    fn to_css(\n        &self,\n        guard: &SharedRwLockReadGuard,\n        dest: &mut CssStringWriter,\n    ) -> std::fmt::Result {\n        self.block.read_with(guard).to_css(dest)\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/origin.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins).\n\nuse crate::derives::*;\nuse std::marker::PhantomData;\nuse std::ops::BitOrAssign;\n\n/// Each style rule has an origin, which determines where it enters the cascade.\n///\n/// <https://drafts.csswg.org/css-cascade/#cascading-origins>\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)]\n#[repr(u8)]\npub enum Origin {\n    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>\n    UserAgent = 0x1,\n\n    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>\n    User = 0x2,\n\n    /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>\n    Author = 0x4,\n}\n\nimpl Origin {\n    /// Returns an origin that goes in order for `index`.\n    ///\n    /// This is used for iterating across origins.\n    fn from_index(index: i8) -> Option<Self> {\n        Some(match index {\n            0 => Origin::Author,\n            1 => Origin::User,\n            2 => Origin::UserAgent,\n            _ => return None,\n        })\n    }\n\n    fn to_index(self) -> i8 {\n        match self {\n            Origin::Author => 0,\n            Origin::User => 1,\n            Origin::UserAgent => 2,\n        }\n    }\n\n    /// Returns an iterator from this origin, towards all the less specific\n    /// origins. So for `UserAgent`, it'd iterate through all origins.\n    #[inline]\n    pub fn following_including(self) -> OriginSetIterator {\n        OriginSetIterator {\n            set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT,\n            cur: self.to_index(),\n            rev: true,\n        }\n    }\n}\n\n/// A set of origins. This is equivalent to Gecko's OriginFlags.\n#[derive(Clone, Copy, PartialEq, MallocSizeOf)]\npub struct OriginSet(u8);\nbitflags! {\n    impl OriginSet: u8 {\n        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent>\n        const ORIGIN_USER_AGENT = Origin::UserAgent as u8;\n        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user>\n        const ORIGIN_USER = Origin::User as u8;\n        /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author>\n        const ORIGIN_AUTHOR = Origin::Author as u8;\n    }\n}\n\nimpl OriginSet {\n    /// Returns an iterator over the origins present in this `OriginSet`.\n    ///\n    /// See the `OriginSet` documentation for information about the order\n    /// origins are iterated.\n    pub fn iter_origins(&self) -> OriginSetIterator {\n        OriginSetIterator {\n            set: *self,\n            cur: 0,\n            rev: false,\n        }\n    }\n}\n\nimpl From<Origin> for OriginSet {\n    fn from(origin: Origin) -> Self {\n        Self::from_bits_retain(origin as u8)\n    }\n}\n\nimpl BitOrAssign<Origin> for OriginSet {\n    fn bitor_assign(&mut self, origin: Origin) {\n        *self |= OriginSet::from(origin);\n    }\n}\n\n/// Iterates over the origins present in an `OriginSet`, in order from\n/// highest priority (author) to lower (user agent).\n#[derive(Clone)]\npub struct OriginSetIterator {\n    set: OriginSet,\n    cur: i8,\n    rev: bool,\n}\n\nimpl Iterator for OriginSetIterator {\n    type Item = Origin;\n\n    fn next(&mut self) -> Option<Origin> {\n        loop {\n            let origin = Origin::from_index(self.cur)?;\n\n            if self.rev {\n                self.cur -= 1;\n            } else {\n                self.cur += 1;\n            }\n\n            if self.set.contains(origin.into()) {\n                return Some(origin);\n            }\n        }\n    }\n}\n\n/// An object that stores a `T` for each origin of the CSS cascade.\n#[derive(Debug, Default, MallocSizeOf)]\npub struct PerOrigin<T> {\n    /// Data for `Origin::UserAgent`.\n    pub user_agent: T,\n\n    /// Data for `Origin::User`.\n    pub user: T,\n\n    /// Data for `Origin::Author`.\n    pub author: T,\n}\n\nimpl<T> PerOrigin<T> {\n    /// Returns a reference to the per-origin data for the specified origin.\n    #[inline]\n    pub fn borrow_for_origin(&self, origin: &Origin) -> &T {\n        match *origin {\n            Origin::UserAgent => &self.user_agent,\n            Origin::User => &self.user,\n            Origin::Author => &self.author,\n        }\n    }\n\n    /// Returns a mutable reference to the per-origin data for the specified\n    /// origin.\n    #[inline]\n    pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T {\n        match *origin {\n            Origin::UserAgent => &mut self.user_agent,\n            Origin::User => &mut self.user,\n            Origin::Author => &mut self.author,\n        }\n    }\n\n    /// Iterates over references to per-origin extra style data, from highest\n    /// level (author) to lowest (user agent).\n    pub fn iter_origins(&self) -> PerOriginIter<'_, T> {\n        PerOriginIter {\n            data: &self,\n            cur: 0,\n            rev: false,\n        }\n    }\n\n    /// Iterates over references to per-origin extra style data, from lowest\n    /// level (user agent) to highest (author).\n    pub fn iter_origins_rev(&self) -> PerOriginIter<'_, T> {\n        PerOriginIter {\n            data: &self,\n            cur: 2,\n            rev: true,\n        }\n    }\n\n    /// Iterates over mutable references to per-origin extra style data, from\n    /// highest level (author) to lowest (user agent).\n    pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<'_, T> {\n        PerOriginIterMut {\n            data: self,\n            cur: 0,\n            _marker: PhantomData,\n        }\n    }\n}\n\n/// Iterator over `PerOrigin<T>`, from highest level (author) to lowest\n/// (user agent).\n///\n/// We rely on this specific order for correctly looking up @font-face,\n/// @counter-style and @keyframes rules.\npub struct PerOriginIter<'a, T: 'a> {\n    data: &'a PerOrigin<T>,\n    cur: i8,\n    rev: bool,\n}\n\nimpl<'a, T> Iterator for PerOriginIter<'a, T>\nwhere\n    T: 'a,\n{\n    type Item = (&'a T, Origin);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let origin = Origin::from_index(self.cur)?;\n\n        self.cur += if self.rev { -1 } else { 1 };\n\n        Some((self.data.borrow_for_origin(&origin), origin))\n    }\n}\n\n/// Like `PerOriginIter<T>`, but iterates over mutable references to the\n/// per-origin data.\n///\n/// We must use unsafe code here since it's not possible for the borrow\n/// checker to know that we are safely returning a different reference\n/// each time from `next()`.\npub struct PerOriginIterMut<'a, T: 'a> {\n    data: *mut PerOrigin<T>,\n    cur: i8,\n    _marker: PhantomData<&'a mut PerOrigin<T>>,\n}\n\nimpl<'a, T> Iterator for PerOriginIterMut<'a, T>\nwhere\n    T: 'a,\n{\n    type Item = (&'a mut T, Origin);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let origin = Origin::from_index(self.cur)?;\n\n        self.cur += 1;\n\n        Some((\n            unsafe { (*self.data).borrow_mut_for_origin(&origin) },\n            origin,\n        ))\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/page_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@page`][page] rule.\n//!\n//! [page]: https://drafts.csswg.org/css2/page.html#page-box\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse crate::stylesheets::{style_or_page_rule_to_css, CssRules};\nuse crate::values::{AtomIdent, CustomIdent};\nuse cssparser::{match_ignore_ascii_case, Parser, SourceLocation, Token};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError, ToCss};\n\nmacro_rules! page_pseudo_classes {\n    ($($(#[$($meta:tt)+])* $id:ident => $val:literal,)+) => {\n        /// [`@page`][page] rule pseudo-classes.\n        ///\n        /// https://drafts.csswg.org/css-page-3/#page-selectors\n        #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\n        #[repr(u8)]\n        pub enum PagePseudoClass {\n            $($(#[$($meta)+])* $id,)+\n        }\n        impl PagePseudoClass {\n            fn parse<'i, 't>(\n                input: &mut Parser<'i, 't>,\n            ) -> Result<Self, ParseError<'i>> {\n                let loc = input.current_source_location();\n                let colon = input.next_including_whitespace()?;\n                if *colon != Token::Colon {\n                    return Err(loc.new_unexpected_token_error(colon.clone()));\n                }\n\n                let ident = input.next_including_whitespace()?;\n                if let Token::Ident(s) = ident {\n                    return match_ignore_ascii_case! { &**s,\n                        $($val => Ok(PagePseudoClass::$id),)+\n                        _ => Err(loc.new_unexpected_token_error(Token::Ident(s.clone()))),\n                    };\n                }\n                Err(loc.new_unexpected_token_error(ident.clone()))\n            }\n            #[inline]\n            fn to_str(&self) -> &'static str {\n                match *self {\n                    $(PagePseudoClass::$id => concat!(':', $val),)+\n                }\n            }\n        }\n    }\n}\n\npage_pseudo_classes! {\n    /// [`:first`][first] pseudo-class\n    ///\n    /// [first] https://drafts.csswg.org/css-page-3/#first-pseudo\n    First => \"first\",\n    /// [`:blank`][blank] pseudo-class\n    ///\n    /// [blank] https://drafts.csswg.org/css-page-3/#blank-pseudo\n    Blank => \"blank\",\n    /// [`:left`][left] pseudo-class\n    ///\n    /// [left]: https://drafts.csswg.org/css-page-3/#spread-pseudos\n    Left => \"left\",\n    /// [`:right`][right] pseudo-class\n    ///\n    /// [right]: https://drafts.csswg.org/css-page-3/#spread-pseudos\n    Right => \"right\",\n}\n\nbitflags! {\n    /// Bit-flags for pseudo-class. This should only be used for querying if a\n    /// page-rule applies.\n    ///\n    /// https://drafts.csswg.org/css-page-3/#page-selectors\n    #[derive(Clone, Copy)]\n    #[repr(C)]\n    pub struct PagePseudoClassFlags : u8 {\n        /// No pseudo-classes\n        const NONE = 0;\n        /// Flag for PagePseudoClass::First\n        const FIRST = 1 << 0;\n        /// Flag for PagePseudoClass::Blank\n        const BLANK = 1 << 1;\n        /// Flag for PagePseudoClass::Left\n        const LEFT = 1 << 2;\n        /// Flag for PagePseudoClass::Right\n        const RIGHT = 1 << 3;\n    }\n}\n\nimpl PagePseudoClassFlags {\n    /// Creates a pseudo-class flags object with a single pseudo-class.\n    #[inline]\n    pub fn new(other: &PagePseudoClass) -> Self {\n        match *other {\n            PagePseudoClass::First => PagePseudoClassFlags::FIRST,\n            PagePseudoClass::Blank => PagePseudoClassFlags::BLANK,\n            PagePseudoClass::Left => PagePseudoClassFlags::LEFT,\n            PagePseudoClass::Right => PagePseudoClassFlags::RIGHT,\n        }\n    }\n    /// Checks if the given pseudo class applies to this set of flags.\n    #[inline]\n    pub fn contains_class(self, other: &PagePseudoClass) -> bool {\n        self.intersects(PagePseudoClassFlags::new(other))\n    }\n}\n\ntype PagePseudoClasses = SmallVec<[PagePseudoClass; 4]>;\n\n/// Type of a single [`@page`][page selector]\n///\n/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors\n#[derive(Clone, Debug, MallocSizeOf, ToShmem)]\npub struct PageSelector {\n    /// Page name\n    ///\n    /// https://drafts.csswg.org/css-page-3/#page-type-selector\n    pub name: AtomIdent,\n    /// Pseudo-classes for [`@page`][page-selectors]\n    ///\n    /// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors\n    pub pseudos: PagePseudoClasses,\n}\n\n/// Computes the [specificity] given the g, h, and f values as in the spec.\n///\n/// g is number of `:first` or `:blank`, h is number of `:left` or `:right`,\n/// f is if the selector includes a page-name (selectors can only include one\n/// or zero page-names).\n///\n/// This places hard limits of 65535 on h and 32767 on g, at which point all\n/// higher values are treated as those limits respectively.\n///\n/// [specificity]: https://drafts.csswg.org/css-page/#specificity\n#[inline]\nfn selector_specificity(g: usize, h: usize, f: bool) -> u32 {\n    let h = h.min(0xFFFF) as u32;\n    let g = (g.min(0x7FFF) as u32) << 16;\n    let f = if f { 0x80000000 } else { 0 };\n    h + g + f\n}\n\nimpl PageSelector {\n    /// Checks if the ident matches a page-name's ident.\n    ///\n    /// This does not take pseudo selectors into account.\n    #[inline]\n    pub fn ident_matches(&self, other: &CustomIdent) -> bool {\n        self.name.0 == other.0\n    }\n\n    /// Checks that this selector matches the ident and all pseudo classes are\n    /// present in the provided flags.\n    #[inline]\n    pub fn matches(&self, name: &CustomIdent, flags: PagePseudoClassFlags) -> bool {\n        self.ident_matches(name) && self.flags_match(flags)\n    }\n\n    /// Checks that all pseudo classes in this selector are present in the\n    /// provided flags.\n    ///\n    /// Equivalent to, but may be more efficient than:\n    ///\n    /// ```\n    /// match_specificity(flags).is_some()\n    /// ```\n    pub fn flags_match(&self, flags: PagePseudoClassFlags) -> bool {\n        self.pseudos.iter().all(|pc| flags.contains_class(pc))\n    }\n\n    /// Implements specificity calculation for a page selector given a set of\n    /// page pseudo-classes to match with.\n    /// If this selector includes any pseudo-classes that are not in the flags,\n    /// then this will return None.\n    ///\n    /// To fit the specificity calculation into a 32-bit value, this limits the\n    /// maximum count of :first and :blank to 32767, and the maximum count of\n    /// :left and :right to 65535.\n    ///\n    /// https://drafts.csswg.org/css-page-3/#cascading-and-page-context\n    pub fn match_specificity(&self, flags: PagePseudoClassFlags) -> Option<u32> {\n        let mut g: usize = 0;\n        let mut h: usize = 0;\n        for pc in self.pseudos.iter() {\n            if !flags.contains_class(pc) {\n                return None;\n            }\n            match pc {\n                PagePseudoClass::First | PagePseudoClass::Blank => g += 1,\n                PagePseudoClass::Left | PagePseudoClass::Right => h += 1,\n            }\n        }\n        Some(selector_specificity(g, h, !self.name.0.is_empty()))\n    }\n}\n\nimpl ToCss for PageSelector {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.name.to_css(dest)?;\n        for pc in self.pseudos.iter() {\n            dest.write_str(pc.to_str())?;\n        }\n        Ok(())\n    }\n}\n\nfn parse_page_name<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AtomIdent, ParseError<'i>> {\n    let s = input.expect_ident()?;\n    Ok(AtomIdent::from(&**s))\n}\n\nimpl Parse for PageSelector {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let name = input.try_parse(parse_page_name);\n        let mut pseudos = PagePseudoClasses::default();\n        while let Ok(pc) = input.try_parse(PagePseudoClass::parse) {\n            pseudos.push(pc);\n        }\n        // If the result was empty, then we didn't get a selector.\n        let name = match name {\n            Ok(name) => name,\n            Err(..) if !pseudos.is_empty() => AtomIdent::new(atom!(\"\")),\n            Err(err) => return Err(err),\n        };\n        Ok(PageSelector { name, pseudos })\n    }\n}\n\n/// A list of [`@page`][page selectors]\n///\n/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors\n#[derive(Clone, Debug, Default, MallocSizeOf, ToCss, ToShmem)]\n#[css(comma)]\npub struct PageSelectors(#[css(iterable)] pub Box<[PageSelector]>);\n\nimpl PageSelectors {\n    /// Creates a new PageSelectors from a Vec, as from parse_comma_separated\n    #[inline]\n    pub fn new(s: Vec<PageSelector>) -> Self {\n        PageSelectors(s.into())\n    }\n    /// Returns true iff there are any page selectors\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.as_slice().is_empty()\n    }\n    /// Get the underlying PageSelector data as a slice\n    #[inline]\n    pub fn as_slice(&self) -> &[PageSelector] {\n        &*self.0\n    }\n}\n\nimpl Parse for PageSelectors {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(PageSelectors::new(input.parse_comma_separated(|i| {\n            PageSelector::parse(context, i)\n        })?))\n    }\n}\n\n/// A [`@page`][page] rule.\n///\n/// This implements only a limited subset of the CSS\n/// 2.2 syntax.\n///\n/// [page]: https://drafts.csswg.org/css2/page.html#page-box\n/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors\n#[derive(Clone, Debug, ToShmem)]\npub struct PageRule {\n    /// Selectors of the page-rule\n    pub selectors: PageSelectors,\n    /// Nested rules.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The declaration block this page rule contains.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n    /// The source position this rule was found at.\n    pub source_location: SourceLocation,\n}\n\nimpl PageRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n            + self.block.unconditional_shallow_size_of(ops)\n            + self.block.read_with(guard).size_of(ops)\n            + self.selectors.size_of(ops)\n    }\n    /// Computes the specificity of this page rule when matched with flags.\n    ///\n    /// Computing this value has linear-complexity with the size of the\n    /// selectors, so the caller should usually call this once and cache the\n    /// result.\n    ///\n    /// Returns None if the flags do not match this page rule.\n    ///\n    /// The return type is ordered by page-rule specificity.\n    pub fn match_specificity(&self, flags: PagePseudoClassFlags) -> Option<u32> {\n        if self.selectors.is_empty() {\n            // A page-rule with no selectors matches all pages, but with the\n            // lowest possible specificity.\n            return Some(selector_specificity(0, 0, false));\n        }\n        let mut specificity = None;\n        for s in self.selectors.0.iter().map(|s| s.match_specificity(flags)) {\n            specificity = s.max(specificity);\n        }\n        specificity\n    }\n}\n\nimpl ToCssWithGuard for PageRule {\n    /// Serialization of PageRule is not specced, adapted from steps for StyleRule.\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        // https://drafts.csswg.org/cssom/#serialize-a-css-rule\n        dest.write_str(\"@page \")?;\n        if !self.selectors.is_empty() {\n            self.selectors.to_css(&mut CssWriter::new(dest))?;\n            dest.write_char(' ')?;\n        }\n        style_or_page_rule_to_css(Some(&self.rules), &self.block, guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for PageRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(&guard);\n        PageRule {\n            selectors: self.selectors.clone(),\n            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/position_try_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@position-try`][position-try] rule for Anchor Positioning.\n//!\n//! [position-try]: https://drafts.csswg.org/css-anchor-position-1/#fallback-rule\n\nuse std::fmt::Write;\n\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse crate::values::DashedIdent;\nuse cssparser::SourceLocation;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse style_traits::{CssStringWriter, CssWriter, ToCss};\n\n/// A position-try rule.\n#[derive(Clone, Debug, ToShmem)]\npub struct PositionTryRule {\n    /// Name of this position-try rule.\n    pub name: DashedIdent,\n    /// The declaration block this position-try rule contains.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n    /// The source position this rule was found at.\n    pub source_location: SourceLocation,\n}\n\nimpl PositionTryRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)\n    }\n}\n\nimpl DeepCloneWithLock for PositionTryRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        PositionTryRule {\n            name: self.name.clone(),\n            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for PositionTryRule {\n    fn to_css(\n        &self,\n        guard: &SharedRwLockReadGuard,\n        dest: &mut CssStringWriter,\n    ) -> std::fmt::Result {\n        dest.write_str(\"@position-try \")?;\n        self.name.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\" { \")?;\n        let declarations = self.block.read_with(guard);\n        declarations.to_css(dest)?;\n        if !declarations.is_empty() {\n            dest.write_char(' ')?;\n        }\n        dest.write_char('}')\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/property_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\npub use crate::properties_and_values::registry::PropertyRegistration as PropertyRule;\n"
  },
  {
    "path": "style/stylesheets/rule_list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A list of CSS rules.\n\nuse crate::derives::*;\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::loader::StylesheetLoader;\nuse crate::stylesheets::rule_parser::InsertRuleContext;\nuse crate::stylesheets::stylesheet::StylesheetContents;\nuse crate::stylesheets::{AllowImportRules, CssRule, CssRuleTypes, RulesMutateError};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::CssStringWriter;\n\nuse super::CssRuleType;\n\n/// A list of CSS rules.\n#[derive(Debug, ToShmem)]\npub struct CssRules(pub Vec<CssRule>);\n\nimpl CssRules {\n    /// Whether this CSS rules is empty.\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\nimpl DeepCloneWithLock for CssRules {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        CssRules(\n            self.0\n                .iter()\n                .map(|x| x.deep_clone_with_lock(lock, guard))\n                .collect(),\n        )\n    }\n}\n\nimpl CssRules {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = self.0.shallow_size_of(ops);\n        for rule in self.0.iter() {\n            n += rule.size_of(guard, ops);\n        }\n        n\n    }\n\n    /// Trivially construct a new set of CSS rules.\n    pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {\n        Arc::new(shared_lock.wrap(CssRules(rules)))\n    }\n\n    /// Returns whether all the rules in this list are namespace or import\n    /// rules.\n    fn only_ns_or_import(&self) -> bool {\n        self.0.iter().all(|r| match *r {\n            CssRule::Namespace(..) | CssRule::Import(..) => true,\n            _ => false,\n        })\n    }\n\n    /// <https://drafts.csswg.org/cssom/#remove-a-css-rule>\n    pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {\n        // Step 1, 2\n        if index >= self.0.len() {\n            return Err(RulesMutateError::IndexSize);\n        }\n\n        {\n            // Step 3\n            let ref rule = self.0[index];\n\n            // Step 4\n            if let CssRule::Namespace(..) = *rule {\n                if !self.only_ns_or_import() {\n                    return Err(RulesMutateError::InvalidState);\n                }\n            }\n        }\n\n        // Step 5, 6\n        self.0.remove(index);\n        Ok(())\n    }\n\n    /// Serializes this CSSRules to CSS text as a block of rules.\n    ///\n    /// This should be speced into CSSOM spec at some point. See\n    /// <https://github.com/w3c/csswg-drafts/issues/1985>\n    pub fn to_css_block(\n        &self,\n        guard: &SharedRwLockReadGuard,\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        dest.write_str(\" {\")?;\n        self.to_css_block_without_opening(guard, dest)\n    }\n\n    /// As above, but without the opening curly bracket. That's needed for nesting.\n    pub fn to_css_block_without_opening(\n        &self,\n        guard: &SharedRwLockReadGuard,\n        dest: &mut CssStringWriter,\n    ) -> fmt::Result {\n        for rule in self.0.iter() {\n            if rule.is_empty_nested_declarations(guard) {\n                continue;\n            }\n\n            dest.write_str(\"\\n  \")?;\n            let old_len = dest.len();\n            rule.to_css(guard, dest)?;\n            debug_assert_ne!(old_len, dest.len());\n        }\n        dest.write_str(\"\\n}\")\n    }\n\n    /// Parses a rule for <https://drafts.csswg.org/cssom/#insert-a-css-rule>. Caller is\n    /// responsible for calling insert() afterwards.\n    ///\n    /// Written in this funky way because parsing an @import rule may cause us\n    /// to clone a stylesheet from the same document due to caching in the CSS\n    /// loader.\n    ///\n    /// TODO(emilio): We could also pass the write guard down into the loader\n    /// instead, but that seems overkill.\n    pub fn parse_rule_for_insert(\n        &self,\n        lock: &SharedRwLock,\n        rule: &str,\n        parent_stylesheet_contents: &StylesheetContents,\n        index: usize,\n        containing_rule_types: CssRuleTypes,\n        parse_relative_rule_type: Option<CssRuleType>,\n        loader: Option<&dyn StylesheetLoader>,\n        allow_import_rules: AllowImportRules,\n    ) -> Result<CssRule, RulesMutateError> {\n        // Step 1, 2\n        if index > self.0.len() {\n            return Err(RulesMutateError::IndexSize);\n        }\n\n        let insert_rule_context = InsertRuleContext {\n            rule_list: &self.0,\n            index,\n            containing_rule_types,\n            parse_relative_rule_type,\n        };\n\n        // Steps 3, 4, 5, 6\n        CssRule::parse(\n            &rule,\n            insert_rule_context,\n            parent_stylesheet_contents,\n            lock,\n            loader,\n            allow_import_rules,\n        )\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/rule_parser.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Parsing of the stylesheet contents.\n\nuse crate::counter_style::{parse_counter_style_body, parse_counter_style_name_definition};\nuse crate::custom_properties::parse_name as parse_custom_property_name;\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::font_face::parse_font_face_block;\nuse crate::media_queries::MediaList;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::declaration_block::{\n    parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock,\n};\nuse crate::properties_and_values::rule::{parse_property_block, PropertyRuleName};\nuse crate::selector_parser::{SelectorImpl, SelectorParser};\nuse crate::shared_lock::{Locked, SharedRwLock};\nuse crate::str::starts_with_ignore_ascii_case;\nuse crate::stylesheets::container_rule::{ContainerCondition, ContainerConditions, ContainerRule};\nuse crate::stylesheets::document_rule::DocumentCondition;\nuse crate::stylesheets::font_feature_values_rule::parse_family_name_list;\nuse crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};\nuse crate::stylesheets::keyframes_rule::parse_keyframe_list;\nuse crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};\nuse crate::stylesheets::scope_rule::{ScopeBounds, ScopeRule};\nuse crate::stylesheets::supports_rule::SupportsCondition;\nuse crate::stylesheets::{\n    AllowImportRules, AppearanceBaseRule, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules,\n    CustomMediaCondition, CustomMediaRule, DocumentRule, FontFeatureValuesRule,\n    FontPaletteValuesRule, KeyframesRule, MarginRule, MarginRuleType, MediaRule, NamespaceRule,\n    NestedDeclarationsRule, PageRule, PageSelectors, PositionTryRule, RulesMutateError,\n    StartingStyleRule, StyleRule, StylesheetLoader, SupportsRule, ViewTransitionRule,\n};\nuse crate::values::computed::font::FamilyName;\nuse crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName};\nuse crate::{Atom, Namespace, Prefix};\nuse cssparser::{\n    match_ignore_ascii_case, AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr,\n    DeclarationParser, Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser,\n    RuleBodyParser, SourcePosition,\n};\nuse selectors::parser::{ParseRelative, SelectorList};\nuse servo_arc::Arc;\nuse style_traits::{ParseError, StyleParseErrorKind};\nuse style_traits::arc_slice::ArcSlice;\n\n/// The information we need particularly to do CSSOM insertRule stuff.\npub struct InsertRuleContext<'a> {\n    /// The rule list we're about to insert into.\n    pub rule_list: &'a [CssRule],\n    /// The index we're about to get inserted at.\n    pub index: usize,\n    /// The containing rule types of our ancestors.\n    pub containing_rule_types: CssRuleTypes,\n    /// Rule type determining if and how we parse relative selector syntax.\n    pub parse_relative_rule_type: Option<CssRuleType>,\n}\n\nimpl<'a> InsertRuleContext<'a> {\n    /// Returns the max rule state allowable for insertion at a given index in\n    /// the rule list.\n    pub fn max_rule_state_at_index(&self, index: usize) -> State {\n        let rule = match self.rule_list.get(index) {\n            Some(rule) => rule,\n            None => return State::Body,\n        };\n        match rule {\n            CssRule::Import(..) => State::Imports,\n            CssRule::Namespace(..) => State::Namespaces,\n            CssRule::LayerStatement(..) => {\n                // If there are @import / @namespace after this layer, then\n                // we're in the early-layers phase, otherwise we're in the body\n                // and everything is fair game.\n                let next_non_layer_statement_rule = self.rule_list[index + 1..]\n                    .iter()\n                    .find(|r| !matches!(*r, CssRule::LayerStatement(..)));\n                if let Some(non_layer) = next_non_layer_statement_rule {\n                    if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) {\n                        return State::EarlyLayers;\n                    }\n                }\n                State::Body\n            },\n            _ => State::Body,\n        }\n    }\n}\n\n/// The parser for the top-level rules in a stylesheet.\npub struct TopLevelRuleParser<'a, 'i> {\n    /// A reference to the lock we need to use to create rules.\n    pub shared_lock: &'a SharedRwLock,\n    /// A reference to a stylesheet loader if applicable, for `@import` rules.\n    pub loader: Option<&'a dyn StylesheetLoader>,\n    /// The top-level parser context.\n    pub context: ParserContext<'a>,\n    /// The current state of the parser.\n    pub state: State,\n    /// Whether we have tried to parse was invalid due to being in the wrong\n    /// place (e.g. an @import rule was found while in the `Body` state). Reset\n    /// to `false` when `take_had_hierarchy_error` is called.\n    pub dom_error: Option<RulesMutateError>,\n    /// The info we need insert a rule in a list.\n    pub insert_rule_context: Option<InsertRuleContext<'a>>,\n    /// Whether @import rules will be allowed.\n    pub allow_import_rules: AllowImportRules,\n    /// Whether to keep declarations into first_declaration_block, rather than turning it into a\n    /// nested declarations rule.\n    pub wants_first_declaration_block: bool,\n    /// The first declaration block, only relevant when wants_first_declaration_block is true.\n    pub first_declaration_block: PropertyDeclarationBlock,\n    /// Parser state for declaration blocks in either nested rules or style rules.\n    pub declaration_parser_state: DeclarationParserState<'i>,\n    /// State we keep around only for error reporting purposes. Right now that contains just the\n    /// selectors stack for nesting, if any.\n    ///\n    /// TODO(emilio): This isn't populated properly for `insertRule()` but...\n    pub error_reporting_state: Vec<SelectorList<SelectorImpl>>,\n    /// The rules we've parsed so far.\n    pub rules: Vec<CssRule>,\n}\n\nimpl<'a, 'i> TopLevelRuleParser<'a, 'i> {\n    #[inline]\n    fn nested(&mut self) -> &mut NestedRuleParser<'a, 'i> {\n        // SAFETY: NestedRuleParser is just a repr(transparent) wrapper over TopLevelRuleParser\n        const_assert!(\n            std::mem::size_of::<TopLevelRuleParser<'static, 'static>>()\n                == std::mem::size_of::<NestedRuleParser<'static, 'static>>()\n        );\n        const_assert!(\n            std::mem::align_of::<TopLevelRuleParser<'static, 'static>>()\n                == std::mem::align_of::<NestedRuleParser<'static, 'static>>()\n        );\n        unsafe { &mut *(self as *mut _ as *mut NestedRuleParser<'a, 'i>) }\n    }\n\n    /// Returns the current state of the parser.\n    #[inline]\n    pub fn state(&self) -> State {\n        self.state\n    }\n\n    /// If we're in a nested state, this returns whether declarations can be parsed. See\n    /// RuleBodyItemParser::parse_declarations().\n    #[inline]\n    pub fn can_parse_declarations(&self) -> bool {\n        // We also have to check for page rules here because we currently don't\n        // have a bespoke parser for page rules, and parse them as though they\n        // are style rules.\n        // Scope rules can have direct declarations, behaving as if `:where(:scope)`.\n        // See https://drafts.csswg.org/css-cascade-6/#scoped-declarations\n        self.in_specified_rule(\n            CssRuleType::Style.bit() | CssRuleType::Page.bit() | CssRuleType::Scope.bit(),\n        )\n    }\n\n    #[inline]\n    fn in_style_rule(&self) -> bool {\n        self.context\n            .nesting_context\n            .rule_types\n            .contains(CssRuleType::Style)\n    }\n\n    #[inline]\n    fn in_page_rule(&self) -> bool {\n        self.context\n            .nesting_context\n            .rule_types\n            .contains(CssRuleType::Page)\n    }\n\n    #[inline]\n    fn in_specified_rule(&self, bits: u32) -> bool {\n        let types = CssRuleTypes::from_bits(bits);\n        self.context.nesting_context.rule_types.intersects(types)\n    }\n\n    #[inline]\n    fn in_style_or_page_rule(&self) -> bool {\n        self.in_specified_rule(CssRuleType::Style.bit() | CssRuleType::Page.bit())\n    }\n\n    /// Checks whether we can parse a rule that would transition us to\n    /// `new_state`.\n    ///\n    /// This is usually a simple branch, but we may need more bookkeeping if\n    /// doing `insertRule` from CSSOM.\n    fn check_state(&mut self, new_state: State) -> bool {\n        if self.state > new_state {\n            self.dom_error = Some(RulesMutateError::HierarchyRequest);\n            return false;\n        }\n\n        let ctx = match self.insert_rule_context {\n            Some(ref ctx) => ctx,\n            None => return true,\n        };\n\n        let max_rule_state = ctx.max_rule_state_at_index(ctx.index);\n        if new_state > max_rule_state {\n            self.dom_error = Some(RulesMutateError::HierarchyRequest);\n            return false;\n        }\n\n        // If there's anything that isn't a namespace rule (or import rule, but\n        // we checked that already at the beginning), reject with a\n        // StateError.\n        if new_state == State::Namespaces\n            && ctx.rule_list[ctx.index..]\n                .iter()\n                .any(|r| !matches!(*r, CssRule::Namespace(..)))\n        {\n            self.dom_error = Some(RulesMutateError::InvalidState);\n            return false;\n        }\n\n        true\n    }\n}\n\n/// The current state of the parser.\n#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]\npub enum State {\n    /// We haven't started parsing rules.\n    Start = 1,\n    /// We're parsing early `@layer` statement rules.\n    EarlyLayers = 2,\n    /// We're parsing `@import` and early `@layer` statement rules.\n    Imports = 3,\n    /// We're parsing `@namespace` rules.\n    Namespaces = 4,\n    /// We're parsing the main body of the stylesheet.\n    Body = 5,\n}\n\n#[derive(Clone, Debug, MallocSizeOf, ToShmem)]\n/// Vendor prefix.\npub enum VendorPrefix {\n    /// -moz prefix.\n    Moz,\n    /// -webkit prefix.\n    WebKit,\n}\n\n/// A rule prelude for at-rule with block.\npub enum AtRulePrelude {\n    /// A @font-face rule prelude.\n    FontFace,\n    /// A @font-feature-values rule prelude, with its FamilyName list.\n    FontFeatureValues(Vec<FamilyName>),\n    /// A @font-palette-values rule prelude, with its identifier.\n    FontPaletteValues(DashedIdent),\n    /// A @counter-style rule prelude, with its counter style name.\n    CounterStyle(CustomIdent),\n    /// A @media rule prelude, with its media queries.\n    Media(Arc<Locked<MediaList>>),\n    /// A @container rule prelude.\n    Container(ArcSlice<ContainerCondition>),\n    /// An @supports rule, with its conditional\n    Supports(SupportsCondition),\n    /// A @keyframes rule, with its animation name and vendor prefix if exists.\n    Keyframes(KeyframesName, Option<VendorPrefix>),\n    /// A @page rule prelude, with its page name if it exists.\n    Page(PageSelectors),\n    /// A @property rule prelude.\n    Property(PropertyRuleName),\n    /// A @document rule, with its conditional.\n    Document(DocumentCondition),\n    /// A @import rule prelude.\n    Import(\n        CssUrl,\n        Arc<Locked<MediaList>>,\n        Option<ImportSupportsCondition>,\n        ImportLayer,\n    ),\n    /// A @margin rule prelude.\n    Margin(MarginRuleType),\n    /// A @namespace rule prelude.\n    Namespace(Option<Prefix>, Namespace),\n    /// A @layer rule prelude.\n    Layer(Vec<LayerName>),\n    /// A @scope rule prelude.\n    Scope(ScopeBounds),\n    /// A @starting-style prelude.\n    StartingStyle,\n    /// A @appearance-base prelude (UA sheets only).\n    AppearanceBase,\n    /// A @position-try prelude for Anchor Positioning.\n    PositionTry(DashedIdent),\n    /// A @custom-media prelude.\n    CustomMedia(DashedIdent, CustomMediaCondition),\n    /// A @view-transition prelude.\n    ViewTransition,\n}\n\nimpl AtRulePrelude {\n    fn name(&self) -> &'static str {\n        match *self {\n            Self::FontFace => \"font-face\",\n            Self::FontFeatureValues(..) => \"font-feature-values\",\n            Self::FontPaletteValues(..) => \"font-palette-values\",\n            Self::CounterStyle(..) => \"counter-style\",\n            Self::Media(..) => \"media\",\n            Self::CustomMedia(..) => \"custom-media\",\n            Self::Container(..) => \"container\",\n            Self::Supports(..) => \"supports\",\n            Self::Keyframes(..) => \"keyframes\",\n            Self::Page(..) => \"page\",\n            Self::Property(..) => \"property\",\n            Self::Document(..) => \"-moz-document\",\n            Self::Import(..) => \"import\",\n            Self::Margin(..) => \"margin\",\n            Self::Namespace(..) => \"namespace\",\n            Self::Layer(..) => \"layer\",\n            Self::Scope(..) => \"scope\",\n            Self::StartingStyle => \"starting-style\",\n            Self::AppearanceBase => \"appearance-base\",\n            Self::PositionTry(..) => \"position-try\",\n            Self::ViewTransition => \"view-transition\",\n        }\n    }\n}\n\nimpl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> {\n    type Prelude = AtRulePrelude;\n    type AtRule = SourcePosition;\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_prelude<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<AtRulePrelude, ParseError<'i>> {\n        match_ignore_ascii_case! { &*name,\n            \"import\" => {\n                if !self.check_state(State::Imports) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))\n                }\n\n                if let AllowImportRules::No = self.allow_import_rules {\n                    return Err(input.new_custom_error(StyleParseErrorKind::DisallowedImportRule))\n                }\n\n                // FIXME(emilio): We should always be able to have a loader\n                // around! See bug 1533783.\n                if self.loader.is_none() {\n                    error!(\"Saw @import rule, but no way to trigger the load\");\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))\n                }\n\n                let url_string = input.expect_url_or_string()?.as_ref().to_owned();\n                let url = CssUrl::new_from_untainted_string(url_string, &self.context, CorsMode::None);\n\n                let (layer, supports) = ImportRule::parse_layer_and_supports(input, &mut self.context);\n\n                let media = MediaList::parse(&self.context, input);\n                let media = Arc::new(self.shared_lock.wrap(media));\n\n                return Ok(AtRulePrelude::Import(url, media, supports, layer));\n            },\n            \"namespace\" => {\n                if !self.check_state(State::Namespaces) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))\n                }\n\n                let prefix = input.try_parse(|i| i.expect_ident_cloned())\n                                  .map(|s| Prefix::from(s.as_ref())).ok();\n                let maybe_namespace = match input.expect_url_or_string() {\n                    Ok(url_or_string) => url_or_string,\n                    Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => {\n                        return Err(location.new_custom_error(StyleParseErrorKind::UnexpectedTokenWithinNamespace(t)))\n                    }\n                    Err(e) => return Err(e.into()),\n                };\n                let url = Namespace::from(maybe_namespace.as_ref());\n                return Ok(AtRulePrelude::Namespace(prefix, url));\n            },\n            // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet\n            // anything left is invalid.\n            \"charset\" => {\n                self.dom_error = Some(RulesMutateError::HierarchyRequest);\n                return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))\n            },\n            \"layer\" => {\n                let state_to_check = if self.state <= State::EarlyLayers {\n                    // The real state depends on whether there's a block or not.\n                    // We don't know that yet, but the parse_block check deals\n                    // with that.\n                    State::EarlyLayers\n                } else {\n                    State::Body\n                };\n                if !self.check_state(state_to_check) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n            },\n            _ => {\n                // All other rules have blocks, so we do this check early in\n                // parse_block instead.\n            }\n        }\n\n        AtRuleParser::parse_prelude(self.nested(), name, input)\n    }\n\n    #[inline]\n    fn parse_block<'t>(\n        &mut self,\n        prelude: AtRulePrelude,\n        start: &ParserState,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::AtRule, ParseError<'i>> {\n        if !self.check_state(State::Body) {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        AtRuleParser::parse_block(self.nested(), prelude, start, input)?;\n        self.state = State::Body;\n        Ok(start.position())\n    }\n\n    #[inline]\n    fn rule_without_block(\n        &mut self,\n        prelude: AtRulePrelude,\n        start: &ParserState,\n    ) -> Result<Self::AtRule, ()> {\n        match prelude {\n            AtRulePrelude::Import(url, media, supports, layer) => {\n                let loader = self\n                    .loader\n                    .expect(\"Expected a stylesheet loader for @import\");\n\n                let import_rule = loader.request_stylesheet(\n                    url,\n                    start.source_location(),\n                    &self.shared_lock,\n                    media,\n                    supports,\n                    layer,\n                );\n\n                self.state = State::Imports;\n                self.rules.push(CssRule::Import(import_rule))\n            },\n            AtRulePrelude::Namespace(prefix, url) => {\n                let namespaces = self.context.namespaces.to_mut();\n                let prefix = if let Some(prefix) = prefix {\n                    namespaces.prefixes.insert(prefix.clone(), url.clone());\n                    Some(prefix)\n                } else {\n                    namespaces.default = Some(url.clone());\n                    None\n                };\n\n                self.state = State::Namespaces;\n                self.rules.push(CssRule::Namespace(Arc::new(NamespaceRule {\n                    prefix,\n                    url,\n                    source_location: start.source_location(),\n                })));\n            },\n            AtRulePrelude::Layer(..) => {\n                AtRuleParser::rule_without_block(self.nested(), prelude, start)?;\n                if self.state <= State::EarlyLayers {\n                    self.state = State::EarlyLayers;\n                } else {\n                    self.state = State::Body;\n                }\n            },\n            _ => AtRuleParser::rule_without_block(self.nested(), prelude, start)?,\n        };\n\n        Ok(start.position())\n    }\n}\n\nimpl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> {\n    type Prelude = SelectorList<SelectorImpl>;\n    type QualifiedRule = SourcePosition;\n    type Error = StyleParseErrorKind<'i>;\n\n    #[inline]\n    fn parse_prelude<'t>(\n        &mut self,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::Prelude, ParseError<'i>> {\n        if !self.check_state(State::Body) {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        QualifiedRuleParser::parse_prelude(self.nested(), input)\n    }\n\n    #[inline]\n    fn parse_block<'t>(\n        &mut self,\n        prelude: Self::Prelude,\n        start: &ParserState,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::QualifiedRule, ParseError<'i>> {\n        QualifiedRuleParser::parse_block(self.nested(), prelude, start, input)?;\n        self.state = State::Body;\n        Ok(start.position())\n    }\n}\n\n#[repr(transparent)]\n#[derive(Deref, DerefMut)]\nstruct NestedRuleParser<'a, 'i>(TopLevelRuleParser<'a, 'i>);\n\nstruct NestedParseResult {\n    first_declaration_block: PropertyDeclarationBlock,\n    rules: Vec<CssRule>,\n}\n\nimpl<'a, 'i> NestedRuleParser<'a, 'i> {\n    #[inline]\n    fn parse_relative(&self) -> ParseRelative {\n        self.context.nesting_context.parse_relative\n    }\n\n    // https://drafts.csswg.org/css-nesting/#conditionals\n    //     In addition to nested style rules, this specification allows nested group rules inside\n    //     of style rules: any at-rule whose body contains style rules can be nested inside of a\n    //     style rule as well.\n    fn at_rule_allowed(&self, prelude: &AtRulePrelude) -> bool {\n        match prelude {\n            AtRulePrelude::Media(..)\n            | AtRulePrelude::Supports(..)\n            | AtRulePrelude::Container(..)\n            | AtRulePrelude::Document(..)\n            | AtRulePrelude::Layer(..)\n            | AtRulePrelude::CustomMedia(..)\n            | AtRulePrelude::Scope(..)\n            | AtRulePrelude::StartingStyle\n            | AtRulePrelude::AppearanceBase => true,\n\n            AtRulePrelude::Namespace(..)\n            | AtRulePrelude::FontFace\n            | AtRulePrelude::FontFeatureValues(..)\n            | AtRulePrelude::FontPaletteValues(..)\n            | AtRulePrelude::CounterStyle(..)\n            | AtRulePrelude::Keyframes(..)\n            | AtRulePrelude::Page(..)\n            | AtRulePrelude::Property(..)\n            | AtRulePrelude::Import(..)\n            | AtRulePrelude::PositionTry(..)\n            | AtRulePrelude::ViewTransition => !self.in_style_or_page_rule(),\n            AtRulePrelude::Margin(..) => self.in_page_rule(),\n        }\n    }\n\n    fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {\n        let old = self.context.nesting_context.save(rule_type);\n        let r = cb(self);\n        self.context.nesting_context.restore(old);\n        r\n    }\n\n    fn parse_nested_rules(\n        &mut self,\n        input: &mut Parser<'i, '_>,\n        rule_type: CssRuleType,\n    ) -> Arc<Locked<CssRules>> {\n        let rules = self\n            .parse_nested(\n                input, rule_type, /* wants_first_declaration_block = */ false,\n            )\n            .rules;\n        CssRules::new(rules, &self.shared_lock)\n    }\n\n    fn parse_nested(\n        &mut self,\n        input: &mut Parser<'i, '_>,\n        rule_type: CssRuleType,\n        wants_first_declaration_block: bool,\n    ) -> NestedParseResult {\n        debug_assert!(\n            !self.wants_first_declaration_block,\n            \"Should've flushed previous declarations\"\n        );\n        self.nest_for_rule(rule_type, |parser| {\n            parser.wants_first_declaration_block = wants_first_declaration_block;\n            let parse_declarations = parser.parse_declarations();\n            let mut rules = std::mem::take(&mut parser.rules);\n            let mut first_declaration_block = std::mem::take(&mut parser.first_declaration_block);\n            let mut iter = RuleBodyParser::new(input, parser);\n            while let Some(result) = iter.next() {\n                match result {\n                    Ok(()) => {},\n                    Err((error, slice)) => {\n                        if parse_declarations {\n                            let top = &mut **iter.parser;\n                            top.declaration_parser_state\n                                .did_error(&top.context, error, slice);\n                        } else {\n                            let location = error.location;\n                            let error = ContextualParseError::InvalidRule(slice, error);\n                            iter.parser.context.log_css_error(location, error);\n                        }\n                    },\n                }\n            }\n            parser.flush_declarations();\n            debug_assert!(\n                !parser.wants_first_declaration_block,\n                \"Flushing declarations should take care of this.\"\n            );\n            debug_assert!(\n                !parser.declaration_parser_state.has_parsed_declarations(),\n                \"Parsed but didn't consume declarations\"\n            );\n            std::mem::swap(&mut parser.rules, &mut rules);\n            std::mem::swap(\n                &mut parser.first_declaration_block,\n                &mut first_declaration_block,\n            );\n            NestedParseResult {\n                first_declaration_block,\n                rules,\n            }\n        })\n    }\n\n    #[inline(never)]\n    fn handle_error_reporting_selectors_pre(\n        &mut self,\n        start: &ParserState,\n        selectors: &SelectorList<SelectorImpl>,\n    ) {\n        use cssparser::ToCss;\n        debug_assert!(self.context.error_reporting_enabled());\n        self.error_reporting_state.push(selectors.clone());\n        'selector_loop: for selector in selectors.slice().iter() {\n            let mut current = selector.iter();\n            loop {\n                let mut found_host = false;\n                let mut found_non_host = false;\n                for component in &mut current {\n                    if component.is_host() {\n                        found_host = true;\n                    } else {\n                        found_non_host = true;\n                    }\n                    if found_host && found_non_host {\n                        self.context.log_css_error(\n                            start.source_location(),\n                            ContextualParseError::NeverMatchingHostSelector(\n                                selector.to_css_string(),\n                            ),\n                        );\n                        continue 'selector_loop;\n                    }\n                }\n                if current.next_sequence().is_none() {\n                    break;\n                }\n            }\n        }\n    }\n\n    fn handle_error_reporting_selectors_post(&mut self) {\n        self.error_reporting_state.pop();\n    }\n\n    #[inline]\n    fn flush_declarations(&mut self) {\n        let parser = &mut **self;\n        let wants_first_declaration_block = parser.wants_first_declaration_block;\n        parser.wants_first_declaration_block = false;\n        parser\n            .declaration_parser_state\n            .report_errors_if_needed(&parser.context, &parser.error_reporting_state);\n        if !parser.declaration_parser_state.has_parsed_declarations() {\n            return;\n        }\n        let source_location = parser.declaration_parser_state.first_declaration_start();\n        let declarations = parser.declaration_parser_state.take_declarations();\n        if wants_first_declaration_block {\n            debug_assert!(parser.first_declaration_block.is_empty(), \"How?\");\n            parser.first_declaration_block = declarations;\n        } else {\n            let nested_rule = CssRule::NestedDeclarations(Arc::new(parser.shared_lock.wrap(\n                NestedDeclarationsRule {\n                    block: Arc::new(parser.shared_lock.wrap(declarations)),\n                    source_location,\n                },\n            )));\n            parser.rules.push(nested_rule);\n        }\n    }\n}\n\nimpl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {\n    type Prelude = AtRulePrelude;\n    type AtRule = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_prelude<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::Prelude, ParseError<'i>> {\n        Ok(match_ignore_ascii_case! { &*name,\n            \"media\" => {\n                let media_queries = MediaList::parse(&self.context, input);\n                let arc = Arc::new(self.shared_lock.wrap(media_queries));\n                AtRulePrelude::Media(arc)\n            },\n            \"supports\" => {\n                let cond = SupportsCondition::parse(input)?;\n                AtRulePrelude::Supports(cond)\n            },\n            \"font-face\" => {\n                AtRulePrelude::FontFace\n            },\n            \"container\" if cfg!(feature = \"gecko\") => {\n                let conditions = input.parse_comma_separated(|input| {\n                    ContainerCondition::parse(&self.context, input)\n                })?;\n                // Container rules must have at least one condition.\n                debug_assert!(!conditions.is_empty());\n                let conditions = ArcSlice::from_iter(conditions.into_iter());\n                AtRulePrelude::Container(conditions)\n            },\n            \"layer\" => {\n                let names = input.try_parse(|input| {\n                    input.parse_comma_separated(|input| {\n                        LayerName::parse(&self.context, input)\n                    })\n                }).unwrap_or_default();\n                AtRulePrelude::Layer(names)\n            },\n            \"font-feature-values\" if cfg!(feature = \"gecko\") => {\n                let family_names = parse_family_name_list(&self.context, input)?;\n                AtRulePrelude::FontFeatureValues(family_names)\n            },\n            \"font-palette-values\" if static_prefs::pref!(\"layout.css.font-palette.enabled\") => {\n                let name = DashedIdent::parse(&self.context, input)?;\n                AtRulePrelude::FontPaletteValues(name)\n            },\n            \"counter-style\" if cfg!(feature = \"gecko\") => {\n                let name = parse_counter_style_name_definition(input)?;\n                AtRulePrelude::CounterStyle(name)\n            },\n            \"keyframes\" | \"-webkit-keyframes\" | \"-moz-keyframes\" => {\n                let prefix = if starts_with_ignore_ascii_case(&*name, \"-webkit-\") {\n                    Some(VendorPrefix::WebKit)\n                } else if starts_with_ignore_ascii_case(&*name, \"-moz-\") {\n                    Some(VendorPrefix::Moz)\n                } else {\n                    None\n                };\n                if cfg!(feature = \"servo\") &&\n                   prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) {\n                    // Servo should not support @-moz-keyframes.\n                    return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))\n                }\n                let name = KeyframesName::parse(&self.context, input)?;\n                AtRulePrelude::Keyframes(name, prefix)\n            },\n            \"page\" if cfg!(feature = \"gecko\") => {\n                AtRulePrelude::Page(\n                    input.try_parse(|i| PageSelectors::parse(&self.context, i)).unwrap_or_default()\n                )\n            },\n            \"property\" if static_prefs::pref!(\"layout.css.properties-and-values.enabled\") => {\n                let name = input.expect_ident_cloned()?;\n                let name = parse_custom_property_name(&name).map_err(|_| {\n                    input.new_custom_error(StyleParseErrorKind::UnexpectedIdent(name.clone()))\n                })?;\n                AtRulePrelude::Property(PropertyRuleName(Atom::from(name)))\n            },\n            \"-moz-document\" if cfg!(feature = \"gecko\") => {\n                let cond = DocumentCondition::parse(&self.context, input)?;\n                AtRulePrelude::Document(cond)\n            },\n            \"scope\" if static_prefs::pref!(\"layout.css.at-scope.enabled\") => {\n                let bounds = ScopeBounds::parse(&self.context, input, self.parse_relative())?;\n                AtRulePrelude::Scope(bounds)\n            },\n            \"starting-style\" if static_prefs::pref!(\"layout.css.starting-style-at-rules.enabled\") => {\n                AtRulePrelude::StartingStyle\n            },\n            \"appearance-base\" if self.context.chrome_rules_enabled() => {\n                // We allow parsing this in chrome sheets mostly just so that\n                // browser_parsable_css.js checks UA sheets properly.\n                AtRulePrelude::AppearanceBase\n            },\n            \"position-try\" if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") => {\n                let name = DashedIdent::parse(&self.context, input)?;\n                AtRulePrelude::PositionTry(name)\n            },\n            \"custom-media\" if static_prefs::pref!(\"layout.css.custom-media.enabled\") => {\n                let name = DashedIdent::parse(&self.context, input)?;\n                let condition = input.try_parse(CustomMediaCondition::parse_keyword).unwrap_or_else(|_| {\n                    CustomMediaCondition::MediaList(Arc::new(self.shared_lock.wrap(\n                        MediaList::parse(&self.context, input)\n                    )))\n                });\n                AtRulePrelude::CustomMedia(name, condition)\n            },\n            \"view-transition\" if static_prefs::pref!(\"dom.viewTransitions.cross-document.enabled\") => {\n                AtRulePrelude::ViewTransition\n            },\n            _ => {\n                if static_prefs::pref!(\"layout.css.margin-rules.enabled\") {\n                    if let Some(margin_rule_type) = MarginRuleType::match_name(&name) {\n                        return Ok(AtRulePrelude::Margin(margin_rule_type));\n                    }\n                }\n                return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone())))\n            },\n        })\n    }\n\n    fn parse_block<'t>(\n        &mut self,\n        prelude: AtRulePrelude,\n        start: &ParserState,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(), ParseError<'i>> {\n        if !self.at_rule_allowed(&prelude) {\n            self.dom_error = Some(RulesMutateError::HierarchyRequest);\n            return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(prelude.name().into())));\n        }\n        let source_location = start.source_location();\n        self.flush_declarations();\n        let rule = match prelude {\n            AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| {\n                CssRule::FontFace(Arc::new(\n                    p.shared_lock\n                        .wrap(parse_font_face_block(&p.context, input, source_location).into()),\n                ))\n            }),\n            AtRulePrelude::FontFeatureValues(family_names) => {\n                self.nest_for_rule(CssRuleType::FontFeatureValues, |p| {\n                    CssRule::FontFeatureValues(Arc::new(FontFeatureValuesRule::parse(\n                        &p.context,\n                        input,\n                        family_names,\n                        source_location,\n                    )))\n                })\n            },\n            AtRulePrelude::FontPaletteValues(name) => {\n                self.nest_for_rule(CssRuleType::FontPaletteValues, |p| {\n                    CssRule::FontPaletteValues(Arc::new(FontPaletteValuesRule::parse(\n                        &p.context,\n                        input,\n                        name,\n                        source_location,\n                    )))\n                })\n            },\n            AtRulePrelude::CounterStyle(name) => {\n                let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| {\n                    parse_counter_style_body(name, &p.context, input, source_location)\n                })?;\n                CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body)))\n            },\n            AtRulePrelude::Media(media_queries) => CssRule::Media(Arc::new(MediaRule {\n                media_queries,\n                rules: self.parse_nested_rules(input, CssRuleType::Media),\n                source_location,\n            })),\n            AtRulePrelude::Supports(condition) => {\n                let enabled =\n                    self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context));\n                CssRule::Supports(Arc::new(SupportsRule {\n                    condition,\n                    rules: self.parse_nested_rules(input, CssRuleType::Supports),\n                    enabled,\n                    source_location,\n                }))\n            },\n            AtRulePrelude::Keyframes(name, vendor_prefix) => {\n                self.nest_for_rule(CssRuleType::Keyframe, |p| {\n                    let top = &mut **p;\n                    CssRule::Keyframes(Arc::new(top.shared_lock.wrap(KeyframesRule {\n                        name,\n                        keyframes: parse_keyframe_list(&mut top.context, input, top.shared_lock),\n                        vendor_prefix,\n                        source_location,\n                    })))\n                })\n            },\n            AtRulePrelude::Page(selectors) => {\n                let page_rule = if !static_prefs::pref!(\"layout.css.margin-rules.enabled\") {\n                    let declarations = self.nest_for_rule(CssRuleType::Page, |p| {\n                        parse_property_declaration_list(&p.context, input, &[])\n                    });\n                    PageRule {\n                        selectors,\n                        rules: CssRules::new(vec![], self.shared_lock),\n                        block: Arc::new(self.shared_lock.wrap(declarations)),\n                        source_location,\n                    }\n                } else {\n                    let result = self.parse_nested(input, CssRuleType::Page, true);\n                    PageRule {\n                        selectors,\n                        rules: CssRules::new(result.rules, self.shared_lock),\n                        block: Arc::new(self.shared_lock.wrap(result.first_declaration_block)),\n                        source_location,\n                    }\n                };\n                CssRule::Page(Arc::new(self.shared_lock.wrap(page_rule)))\n            },\n            AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| {\n                let rule_data = parse_property_block(&p.context, input, name, source_location)?;\n                Ok::<CssRule, ParseError<'i>>(CssRule::Property(Arc::new(rule_data)))\n            })?,\n            AtRulePrelude::Document(condition) => {\n                if !cfg!(feature = \"gecko\") {\n                    unreachable!()\n                }\n                CssRule::Document(Arc::new(DocumentRule {\n                    condition,\n                    rules: self.parse_nested_rules(input, CssRuleType::Document),\n                    source_location,\n                }))\n            },\n            AtRulePrelude::Container(conditions) => {\n                let source_location = start.source_location();\n                CssRule::Container(Arc::new(ContainerRule {\n                    conditions: ContainerConditions(conditions),\n                    rules: self.parse_nested_rules(input, CssRuleType::Container),\n                    source_location,\n                }))\n            },\n            AtRulePrelude::Layer(names) => {\n                let name = match names.len() {\n                    0 | 1 => names.into_iter().next(),\n                    _ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)),\n                };\n                CssRule::LayerBlock(Arc::new(LayerBlockRule {\n                    name,\n                    rules: self.parse_nested_rules(input, CssRuleType::LayerBlock),\n                    source_location,\n                }))\n            },\n            AtRulePrelude::Margin(rule_type) => {\n                let declarations = self.nest_for_rule(CssRuleType::Margin, |p| {\n                    parse_property_declaration_list(&p.context, input, &[])\n                });\n                CssRule::Margin(Arc::new(MarginRule {\n                    rule_type,\n                    block: Arc::new(self.shared_lock.wrap(declarations)),\n                    source_location,\n                }))\n            },\n            AtRulePrelude::CustomMedia(..)\n            | AtRulePrelude::Import(..)\n            | AtRulePrelude::Namespace(..) => {\n                // These rules don't have blocks.\n                return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));\n            },\n            AtRulePrelude::Scope(bounds) => CssRule::Scope(Arc::new(ScopeRule {\n                bounds,\n                rules: self.parse_nested_rules(input, CssRuleType::Scope),\n                source_location,\n            })),\n            AtRulePrelude::StartingStyle => CssRule::StartingStyle(Arc::new(StartingStyleRule {\n                rules: self.parse_nested_rules(input, CssRuleType::StartingStyle),\n                source_location,\n            })),\n            AtRulePrelude::AppearanceBase => {\n                CssRule::AppearanceBase(Arc::new(AppearanceBaseRule {\n                    rules: self.parse_nested_rules(input, CssRuleType::AppearanceBase),\n                    source_location,\n                }))\n            },\n            AtRulePrelude::PositionTry(name) => {\n                let declarations = self.nest_for_rule(CssRuleType::PositionTry, |p| {\n                    parse_property_declaration_list(&p.context, input, &[])\n                });\n                CssRule::PositionTry(Arc::new(self.shared_lock.wrap(PositionTryRule {\n                    name,\n                    block: Arc::new(self.shared_lock.wrap(declarations)),\n                    source_location,\n                })))\n            },\n            AtRulePrelude::ViewTransition => self.nest_for_rule(CssRuleType::ViewTransition, |p| {\n                CssRule::ViewTransition(Arc::new(ViewTransitionRule::parse(\n                    &p.context,\n                    input,\n                    source_location,\n                )))\n            }),\n        };\n        self.rules.push(rule);\n        Ok(())\n    }\n\n    #[inline]\n    fn rule_without_block(\n        &mut self,\n        prelude: AtRulePrelude,\n        start: &ParserState,\n    ) -> Result<(), ()> {\n        if self.in_style_rule() {\n            return Err(());\n        }\n        let source_location = start.source_location();\n        let rule = match prelude {\n            AtRulePrelude::CustomMedia(name, condition) => {\n                CssRule::CustomMedia(Arc::new(CustomMediaRule {\n                    name,\n                    condition,\n                    source_location,\n                }))\n            },\n            AtRulePrelude::Layer(names) => {\n                if names.is_empty() {\n                    return Err(());\n                }\n                CssRule::LayerStatement(Arc::new(LayerStatementRule {\n                    names,\n                    source_location,\n                }))\n            },\n            _ => return Err(()),\n        };\n        self.flush_declarations();\n        self.rules.push(rule);\n        Ok(())\n    }\n}\n\nimpl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {\n    type Prelude = SelectorList<SelectorImpl>;\n    type QualifiedRule = ();\n    type Error = StyleParseErrorKind<'i>;\n\n    fn parse_prelude<'t>(\n        &mut self,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self::Prelude, ParseError<'i>> {\n        let selector_parser = SelectorParser {\n            stylesheet_origin: self.context.stylesheet_origin,\n            namespaces: &self.context.namespaces,\n            url_data: self.context.url_data,\n            for_supports_rule: false,\n        };\n        SelectorList::parse(&selector_parser, input, self.parse_relative())\n    }\n\n    fn parse_block<'t>(\n        &mut self,\n        selectors: Self::Prelude,\n        start: &ParserState,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(), ParseError<'i>> {\n        let source_location = start.source_location();\n        let reporting_errors = self.context.error_reporting_enabled();\n        if reporting_errors {\n            self.handle_error_reporting_selectors_pre(start, &selectors);\n        }\n        self.flush_declarations();\n        let result = self.parse_nested(input, CssRuleType::Style, true);\n        if reporting_errors {\n            self.handle_error_reporting_selectors_post();\n        }\n        let block = Arc::new(self.shared_lock.wrap(result.first_declaration_block));\n        let top = &mut **self;\n        top.rules\n            .push(CssRule::Style(Arc::new(top.shared_lock.wrap(StyleRule {\n                selectors,\n                block,\n                rules: if result.rules.is_empty() {\n                    None\n                } else {\n                    Some(CssRules::new(result.rules, top.shared_lock))\n                },\n                source_location,\n            }))));\n        Ok(())\n    }\n}\n\nimpl<'a, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'i> {\n    type Declaration = ();\n    type Error = StyleParseErrorKind<'i>;\n    fn parse_value<'t>(\n        &mut self,\n        name: CowRcStr<'i>,\n        input: &mut Parser<'i, 't>,\n        declaration_start: &ParserState,\n    ) -> Result<(), ParseError<'i>> {\n        let top = &mut **self;\n        top.declaration_parser_state\n            .parse_value(&top.context, name, input, declaration_start)\n    }\n}\n\nimpl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for NestedRuleParser<'a, 'i> {\n    fn parse_qualified(&self) -> bool {\n        true\n    }\n\n    /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse\n    /// raw declarations there.\n    fn parse_declarations(&self) -> bool {\n        self.can_parse_declarations()\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/rules_iterator.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! An iterator over a list of rules.\n\nuse crate::context::QuirksMode;\nuse crate::device::Device;\nuse crate::shared_lock::SharedRwLockReadGuard;\nuse crate::stylesheets::{\n    CssRule, CssRuleRef, CustomMediaEvaluator, CustomMediaMap, DocumentRule, ImportRule, MediaRule,\n    SupportsRule,\n};\nuse smallvec::SmallVec;\nuse std::ops::Deref;\nuse std::slice;\n\n/// An iterator over a list of rules.\npub struct RulesIterator<'a, 'b, C, CMM>\nwhere\n    'b: 'a,\n    C: NestedRuleIterationCondition + 'static,\n    CMM: Deref<Target = CustomMediaMap>,\n{\n    device: &'a Device,\n    quirks_mode: QuirksMode,\n    custom_media: CMM,\n    guard: &'a SharedRwLockReadGuard<'b>,\n    stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>,\n    last_rule_had_children: bool,\n    _phantom: ::std::marker::PhantomData<C>,\n}\n\nimpl<'a, 'b, C, CMM> RulesIterator<'a, 'b, C, CMM>\nwhere\n    'b: 'a,\n    C: NestedRuleIterationCondition + 'static,\n    CMM: Deref<Target = CustomMediaMap>,\n{\n    /// Returns the custom media map passed at construction.\n    pub fn custom_media(&mut self) -> &mut CMM {\n        &mut self.custom_media\n    }\n\n    /// Creates a new `RulesIterator` to iterate over `rules`.\n    pub fn new(\n        device: &'a Device,\n        quirks_mode: QuirksMode,\n        custom_media: CMM,\n        guard: &'a SharedRwLockReadGuard<'b>,\n        rules: slice::Iter<'a, CssRule>,\n    ) -> Self {\n        let mut stack = SmallVec::new();\n        stack.push(rules);\n        Self {\n            device,\n            quirks_mode,\n            custom_media,\n            guard,\n            stack,\n            last_rule_had_children: false,\n            _phantom: ::std::marker::PhantomData,\n        }\n    }\n\n    /// Skips all the remaining children of the last nested rule processed.\n    pub fn skip_children(&mut self) {\n        if self.last_rule_had_children {\n            self.stack.pop();\n            self.last_rule_had_children = false;\n        }\n    }\n\n    /// Returns the children of `rule`, and whether `rule` is effective.\n    pub fn children(\n        rule: &'a CssRule,\n        device: &'a Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        guard: &'a SharedRwLockReadGuard<'_>,\n        effective: &mut bool,\n    ) -> &'a [CssRule] {\n        *effective = true;\n        match *rule {\n            CssRule::Namespace(_)\n            | CssRule::FontFace(_)\n            | CssRule::CounterStyle(_)\n            | CssRule::CustomMedia(_)\n            | CssRule::Keyframes(_)\n            | CssRule::Margin(_)\n            | CssRule::Property(_)\n            | CssRule::LayerStatement(_)\n            | CssRule::FontFeatureValues(_)\n            | CssRule::FontPaletteValues(_)\n            | CssRule::NestedDeclarations(_)\n            | CssRule::PositionTry(_)\n            | CssRule::ViewTransition(_) => &[],\n            CssRule::Page(ref page_rule) => {\n                let page_rule = page_rule.read_with(guard);\n                let rules = page_rule.rules.read_with(guard);\n                rules.0.as_slice()\n            },\n            CssRule::Style(ref style_rule) => {\n                let style_rule = style_rule.read_with(guard);\n                match style_rule.rules.as_ref() {\n                    Some(r) => r.read_with(guard).0.as_slice(),\n                    None => &[],\n                }\n            },\n            CssRule::Import(ref import_rule) => {\n                let import_rule = import_rule.read_with(guard);\n                if !C::process_import(guard, device, quirks_mode, custom_media_map, import_rule) {\n                    *effective = false;\n                    return &[];\n                }\n                import_rule.stylesheet.rules(guard)\n            },\n            CssRule::Document(ref doc_rule) => {\n                if !C::process_document(guard, device, quirks_mode, doc_rule) {\n                    *effective = false;\n                    return &[];\n                }\n                doc_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::Container(ref container_rule) => {\n                container_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::Media(ref media_rule) => {\n                if !C::process_media(guard, device, quirks_mode, custom_media_map, media_rule) {\n                    *effective = false;\n                    return &[];\n                }\n                media_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::Supports(ref supports_rule) => {\n                if !C::process_supports(guard, device, quirks_mode, supports_rule) {\n                    *effective = false;\n                    return &[];\n                }\n                supports_rule.rules.read_with(guard).0.as_slice()\n            },\n            CssRule::LayerBlock(ref layer_rule) => layer_rule.rules.read_with(guard).0.as_slice(),\n            CssRule::Scope(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n            CssRule::StartingStyle(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n            CssRule::AppearanceBase(ref rule) => rule.rules.read_with(guard).0.as_slice(),\n        }\n    }\n}\n\nimpl<'a, 'b, C, CMM> Iterator for RulesIterator<'a, 'b, C, CMM>\nwhere\n    'b: 'a,\n    C: NestedRuleIterationCondition + 'static,\n    CMM: Deref<Target = CustomMediaMap>,\n{\n    type Item = &'a CssRule;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.last_rule_had_children = false;\n        while !self.stack.is_empty() {\n            let rule = {\n                let nested_iter = self.stack.last_mut().unwrap();\n                match nested_iter.next() {\n                    Some(r) => r,\n                    None => {\n                        self.stack.pop();\n                        continue;\n                    },\n                }\n            };\n\n            let mut effective = true;\n            let children = Self::children(\n                rule,\n                self.device,\n                self.quirks_mode,\n                &self.custom_media,\n                self.guard,\n                &mut effective,\n            );\n            if !effective {\n                continue;\n            }\n            if !children.is_empty() {\n                debug_assert_eq!(\n                    rule.children(self.guard).len(),\n                    children.len(),\n                    \"Should agree with CssRule::children if effective\"\n                );\n                self.last_rule_had_children = true;\n                self.stack.push(children.iter());\n            }\n            return Some(rule);\n        }\n\n        None\n    }\n}\n\n/// RulesIterator.\npub trait NestedRuleIterationCondition {\n    /// Whether we should process the nested rules in a given `@import` rule.\n    fn process_import(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        rule: &ImportRule,\n    ) -> bool;\n\n    /// Whether we should process the nested rules in a given `@media` rule.\n    fn process_media(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        rule: &MediaRule,\n    ) -> bool;\n\n    /// Whether we should process the nested rules in a given `@-moz-document`\n    /// rule.\n    fn process_document(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        rule: &DocumentRule,\n    ) -> bool;\n\n    /// Whether we should process the nested rules in a given `@supports` rule.\n    fn process_supports(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        rule: &SupportsRule,\n    ) -> bool;\n}\n\n/// A struct that represents the condition that a rule applies to the document.\npub struct EffectiveRules;\n\nimpl EffectiveRules {\n    /// Returns whether a given rule is effective.\n    pub fn is_effective(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        rule: &CssRuleRef,\n    ) -> bool {\n        match *rule {\n            CssRuleRef::Import(import_rule) => {\n                let import_rule = import_rule.read_with(guard);\n                Self::process_import(guard, device, quirks_mode, custom_media_map, import_rule)\n            },\n            CssRuleRef::Document(doc_rule) => {\n                Self::process_document(guard, device, quirks_mode, doc_rule)\n            },\n            CssRuleRef::Media(media_rule) => {\n                Self::process_media(guard, device, quirks_mode, custom_media_map, media_rule)\n            },\n            CssRuleRef::Supports(supports_rule) => {\n                Self::process_supports(guard, device, quirks_mode, supports_rule)\n            },\n            _ => true,\n        }\n    }\n}\n\nimpl NestedRuleIterationCondition for EffectiveRules {\n    fn process_import(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        rule: &ImportRule,\n    ) -> bool {\n        match rule.stylesheet.media(guard) {\n            Some(m) => m.evaluate(\n                device,\n                quirks_mode,\n                &mut CustomMediaEvaluator::new(custom_media_map, guard),\n            ),\n            None => true,\n        }\n    }\n\n    fn process_media(\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: &CustomMediaMap,\n        rule: &MediaRule,\n    ) -> bool {\n        rule.media_queries.read_with(guard).evaluate(\n            device,\n            quirks_mode,\n            &mut CustomMediaEvaluator::new(custom_media_map, guard),\n        )\n    }\n\n    fn process_document(\n        _: &SharedRwLockReadGuard,\n        device: &Device,\n        _: QuirksMode,\n        rule: &DocumentRule,\n    ) -> bool {\n        rule.condition.evaluate(device)\n    }\n\n    fn process_supports(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        rule: &SupportsRule,\n    ) -> bool {\n        rule.enabled\n    }\n}\n\n/// A filter that processes all the rules in a rule list.\npub struct AllRules;\n\nimpl NestedRuleIterationCondition for AllRules {\n    fn process_import(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &CustomMediaMap,\n        _: &ImportRule,\n    ) -> bool {\n        true\n    }\n\n    fn process_media(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &CustomMediaMap,\n        _: &MediaRule,\n    ) -> bool {\n        true\n    }\n\n    fn process_document(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &DocumentRule,\n    ) -> bool {\n        true\n    }\n\n    fn process_supports(\n        _: &SharedRwLockReadGuard,\n        _: &Device,\n        _: QuirksMode,\n        _: &SupportsRule,\n    ) -> bool {\n        true\n    }\n}\n\n/// An iterator over all the effective rules of a stylesheet.\n///\n/// NOTE: This iterator recurses into `@import` rules.\npub type EffectiveRulesIterator<'a, 'b, CMM> = RulesIterator<'a, 'b, EffectiveRules, CMM>;\n\nimpl<'a, 'b, CMM> EffectiveRulesIterator<'a, 'b, CMM>\nwhere\n    CMM: Deref<Target = CustomMediaMap>,\n{\n    /// Returns an iterator over the effective children of a rule, even if\n    /// `rule` itself is not effective.\n    pub fn effective_children(\n        device: &'a Device,\n        quirks_mode: QuirksMode,\n        custom_media_map: CMM,\n        guard: &'a SharedRwLockReadGuard<'b>,\n        rule: &'a CssRule,\n    ) -> Self {\n        let children = RulesIterator::<AllRules, CMM>::children(\n            rule,\n            device,\n            quirks_mode,\n            &custom_media_map,\n            guard,\n            &mut false,\n        );\n        EffectiveRulesIterator::new(\n            device,\n            quirks_mode,\n            custom_media_map,\n            guard,\n            children.iter(),\n        )\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/scope_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@scope`][scope] rule.\n//!\n//! [scope]: https://drafts.csswg.org/css-cascade-6/#scoped-styles\n\nuse crate::applicable_declarations::ScopeProximity;\nuse crate::derives::*;\nuse crate::dom::TElement;\nuse crate::parser::ParserContext;\nuse crate::selector_parser::{SelectorImpl, SelectorParser};\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse crate::simple_buckets_map::SimpleBucketsMap;\nuse crate::stylesheets::CssRules;\nuse cssparser::{Parser, SourceLocation, ToCss};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{\n    MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf,\n};\nuse selectors::context::{MatchingContext, QuirksMode};\nuse selectors::matching::matches_selector;\nuse selectors::parser::{Component, ParseRelative, Selector, SelectorList};\nuse selectors::OpaqueElement;\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ParseError};\n\n/// A scoped rule.\n#[derive(Debug, ToShmem)]\npub struct ScopeRule {\n    /// Bounds at which this rule applies.\n    pub bounds: ScopeBounds,\n    /// The nested rules inside the block.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl DeepCloneWithLock for ScopeRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        Self {\n            bounds: self.bounds.clone(),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl ToCssWithGuard for ScopeRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@scope\")?;\n        {\n            let mut writer = CssWriter::new(dest);\n            if let Some(start) = self.bounds.start.as_ref() {\n                writer.write_str(\" (\")?;\n                start.to_css(&mut writer)?;\n                writer.write_char(')')?;\n            }\n            if let Some(end) = self.bounds.end.as_ref() {\n                writer.write_str(\" to (\")?;\n                end.to_css(&mut writer)?;\n                writer.write_char(')')?;\n            }\n        }\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl ScopeRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n            + self.bounds.size_of(ops)\n    }\n}\n\n/// Bounds of the scope.\n#[derive(Debug, Clone, ToShmem)]\npub struct ScopeBounds {\n    /// Start of the scope.\n    pub start: Option<SelectorList<SelectorImpl>>,\n    /// End of the scope.\n    pub end: Option<SelectorList<SelectorImpl>>,\n}\n\nimpl ScopeBounds {\n    #[cfg(feature = \"gecko\")]\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        fn bound_size_of(\n            bound: &Option<SelectorList<SelectorImpl>>,\n            ops: &mut MallocSizeOfOps,\n        ) -> usize {\n            bound\n                .as_ref()\n                .map(|list| list.unconditional_size_of(ops))\n                .unwrap_or(0)\n        }\n        bound_size_of(&self.start, ops) + bound_size_of(&self.end, ops)\n    }\n}\n\nfn parse_scope<'a>(\n    context: &ParserContext,\n    input: &mut Parser<'a, '_>,\n    parse_relative: ParseRelative,\n    for_end: bool,\n) -> Result<Option<SelectorList<SelectorImpl>>, ParseError<'a>> {\n    input.try_parse(|input| {\n        if for_end {\n            // scope-end not existing is valid.\n            if input.try_parse(|i| i.expect_ident_matching(\"to\")).is_err() {\n                return Ok(None);\n            }\n        }\n        let parens = input.try_parse(|i| i.expect_parenthesis_block());\n        if for_end {\n            // `@scope to {}` is NOT valid.\n            parens?;\n        } else if parens.is_err() {\n            // `@scope {}` is valid.\n            return Ok(None);\n        }\n        input.parse_nested_block(|input| {\n            let selector_parser = SelectorParser {\n                stylesheet_origin: context.stylesheet_origin,\n                namespaces: &context.namespaces,\n                url_data: context.url_data,\n                for_supports_rule: false,\n            };\n            let parse_relative = if for_end {\n                ParseRelative::ForScope\n            } else {\n                parse_relative\n            };\n            Ok(Some(SelectorList::parse_disallow_pseudo(\n                &selector_parser,\n                input,\n                parse_relative,\n            )?))\n        })\n    })\n}\n\nimpl ScopeBounds {\n    /// Parse a container condition.\n    pub fn parse<'a>(\n        context: &ParserContext,\n        input: &mut Parser<'a, '_>,\n        parse_relative: ParseRelative,\n    ) -> Result<Self, ParseError<'a>> {\n        let start = parse_scope(context, input, parse_relative, false)?;\n        let end = parse_scope(context, input, parse_relative, true)?;\n        Ok(Self { start, end })\n    }\n}\n\n/// Types of implicit scope root.\n#[derive(Debug, Copy, Clone, MallocSizeOf)]\npub enum ImplicitScopeRoot {\n    /// This implicit scope root is in the light tree.\n    InLightTree(OpaqueElement),\n    /// This implicit scope root is the document element, regardless of which (light|shadow) tree\n    /// the element being matched is. This is the case for e.g. if you specified an implicit scope\n    /// within a user stylesheet.\n    DocumentElement,\n    /// The implicit scope root is in a constructed stylesheet - the scope root the element\n    /// under consideration's shadow root (If one exists).\n    Constructed,\n    /// This implicit scope root is in the shadow tree.\n    InShadowTree(OpaqueElement),\n    /// This implicit scope root is the shadow host of the stylesheet-containing shadow tree.\n    ShadowHost(OpaqueElement),\n}\n\nimpl ImplicitScopeRoot {\n    /// Return true if this matches the shadow host.\n    pub fn matches_shadow_host(&self) -> bool {\n        match self {\n            Self::InLightTree(..) | Self::InShadowTree(..) | Self::DocumentElement => false,\n            Self::ShadowHost(..) | Self::Constructed => true,\n        }\n    }\n\n    /// Return the implicit scope root element.\n    pub fn element(&self, current_host: Option<OpaqueElement>) -> ImplicitScopeTarget {\n        match self {\n            Self::InLightTree(e) | Self::InShadowTree(e) | Self::ShadowHost(e) => {\n                ImplicitScopeTarget::Element(*e)\n            },\n            Self::Constructed | Self::DocumentElement => {\n                if matches!(self, Self::Constructed) {\n                    if let Some(host) = current_host {\n                        return ImplicitScopeTarget::Element(host);\n                    }\n                }\n                ImplicitScopeTarget::DocumentElement\n            },\n        }\n    }\n}\n\n/// Target of this implicit scope.\npub enum ImplicitScopeTarget {\n    /// Target matches only the specified element.\n    Element(OpaqueElement),\n    /// Implicit scope whose target is the document element.\n    DocumentElement,\n}\n\nimpl ImplicitScopeTarget {\n    /// Check if this element is the implicit scope.\n    fn check<E: TElement>(&self, element: E) -> bool {\n        match self {\n            Self::Element(e) => element.opaque() == *e,\n            Self::DocumentElement => element.is_root(),\n        }\n    }\n}\n\n/// Target of this scope.\npub enum ScopeTarget<'a> {\n    /// Target matches an element matching the specified selector list.\n    Selector(&'a SelectorList<SelectorImpl>),\n    /// Target matches an implicit scope target.\n    Implicit(ImplicitScopeTarget),\n}\n\nimpl<'a> ScopeTarget<'a> {\n    /// Check if the given element is the scope.\n    fn check<E: TElement>(\n        &self,\n        element: E,\n        scope: Option<OpaqueElement>,\n        scope_subject_map: &ScopeSubjectMap,\n        context: &mut MatchingContext<E::Impl>,\n    ) -> bool {\n        match self {\n            Self::Selector(list) => context.nest_for_scope_condition(scope, |context| {\n                if scope_subject_map.early_reject(element, context.quirks_mode()) {\n                    return false;\n                }\n                for selector in list.slice().iter() {\n                    if matches_selector(selector, 0, None, &element, context) {\n                        return true;\n                    }\n                }\n                false\n            }),\n            Self::Implicit(t) => t.check(element),\n        }\n    }\n}\n\n/// A scope root candidate.\n#[derive(Clone, Copy, Debug)]\npub struct ScopeRootCandidate {\n    /// This candidate's scope root.\n    pub root: OpaqueElement,\n    /// Ancestor hop from the element under consideration to this scope root.\n    pub proximity: ScopeProximity,\n}\n\nimpl ScopeRootCandidate {\n    /// Get the element corresponding to this scope root candidate.\n    pub fn get_scope_root_element<E>(&self, originating_element: E) -> Option<E>\n    where\n        E: TElement,\n    {\n        // Could just unsafe-convert from opaque element - technically\n        // faster as well, but it doesn't seem worth having to manually\n        // assure safety every time.\n        let mut e = originating_element;\n        let hops = self.proximity.get()?;\n        for _ in 0..hops {\n            e = e.parent_element()?;\n        }\n        debug_assert_eq!(e.opaque(), self.root);\n        Some(e)\n    }\n}\n\n/// Collect potential scope roots for a given element and its scope target.\n/// The check may not pass the ceiling, if specified.\npub fn collect_scope_roots<E>(\n    element: E,\n    ceiling: Option<OpaqueElement>,\n    context: &mut MatchingContext<E::Impl>,\n    target: &ScopeTarget,\n    matches_shadow_host: bool,\n    scope_subject_map: &ScopeSubjectMap,\n) -> Vec<ScopeRootCandidate>\nwhere\n    E: TElement,\n{\n    let mut result = vec![];\n    let mut parent = Some(element);\n    let mut proximity = 0usize;\n    while let Some(p) = parent {\n        if target.check(p, ceiling, scope_subject_map, context) {\n            result.push(ScopeRootCandidate {\n                root: p.opaque(),\n                proximity: ScopeProximity::new(proximity),\n            });\n            // Note that we can't really break here - we need to consider\n            // ALL scope roots to figure out whch one didn't end.\n        }\n        if ceiling == Some(p.opaque()) {\n            break;\n        }\n        parent = p.parent_element();\n        proximity += 1;\n        // We we got to the top of the shadow tree - keep going\n        // if we may match the shadow host.\n        if parent.is_none() && matches_shadow_host {\n            parent = p.containing_shadow_host();\n        }\n    }\n    result\n}\n\n/// Given the scope-end selector, check if the element is outside of the scope.\n/// That is, check if any ancestor to the root matches the scope-end selector.\npub fn element_is_outside_of_scope<E>(\n    selector: &Selector<E::Impl>,\n    element: E,\n    root: OpaqueElement,\n    context: &mut MatchingContext<E::Impl>,\n    root_may_be_shadow_host: bool,\n) -> bool\nwhere\n    E: TElement,\n{\n    let mut parent = Some(element);\n    context.nest_for_scope_condition(Some(root), |context| {\n        while let Some(p) = parent {\n            if matches_selector(selector, 0, None, &p, context) {\n                return true;\n            }\n            if p.opaque() == root {\n                // Reached the top, not lying outside of scope.\n                break;\n            }\n            parent = p.parent_element();\n            if parent.is_none() && root_may_be_shadow_host {\n                if let Some(host) = p.containing_shadow_host() {\n                    // Pretty much an edge case where user specified scope-start and -end of :host\n                    return host.opaque() == root;\n                }\n            }\n        }\n        return false;\n    })\n}\n\n/// A map containing simple selectors in subjects of scope selectors.\n/// This allows fast-rejecting scopes before running the full match.\n#[derive(Clone, Debug, Default, MallocSizeOf)]\npub struct ScopeSubjectMap {\n    buckets: SimpleBucketsMap<()>,\n    any: bool,\n}\n\nimpl ScopeSubjectMap {\n    /// Add the `<scope-start>` of a scope.\n    pub fn add_bound_start(\n        &mut self,\n        selectors: &SelectorList<SelectorImpl>,\n        quirks_mode: QuirksMode,\n    ) {\n        if self.add_selector_list(selectors, quirks_mode) {\n            self.any = true;\n        }\n    }\n\n    fn add_selector_list(\n        &mut self,\n        selectors: &SelectorList<SelectorImpl>,\n        quirks_mode: QuirksMode,\n    ) -> bool {\n        let mut is_any = false;\n        for selector in selectors.slice().iter() {\n            is_any = is_any || self.add_selector(selector, quirks_mode);\n        }\n        is_any\n    }\n\n    fn add_selector(&mut self, selector: &Selector<SelectorImpl>, quirks_mode: QuirksMode) -> bool {\n        let mut is_any = true;\n        let mut iter = selector.iter();\n        while let Some(c) = iter.next() {\n            let component_any = match c {\n                Component::Class(cls) => {\n                    match self.buckets.classes.try_entry(cls.0.clone(), quirks_mode) {\n                        Ok(e) => {\n                            e.or_insert(());\n                            false\n                        },\n                        Err(_) => true,\n                    }\n                },\n                Component::ID(id) => match self.buckets.ids.try_entry(id.0.clone(), quirks_mode) {\n                    Ok(e) => {\n                        e.or_insert(());\n                        false\n                    },\n                    Err(_) => true,\n                },\n                Component::LocalName(local_name) => {\n                    self.buckets\n                        .local_names\n                        .insert(local_name.lower_name.clone(), ());\n                    false\n                },\n                Component::Is(ref list) | Component::Where(ref list) => {\n                    self.add_selector_list(list, quirks_mode)\n                },\n                _ => true,\n            };\n\n            is_any = is_any && component_any;\n        }\n        is_any\n    }\n\n    /// Shrink the map as much as possible.\n    pub fn shrink_if_needed(&mut self) {\n        self.buckets.shrink_if_needed();\n    }\n\n    /// Clear the map.\n    pub fn clear(&mut self) {\n        self.buckets.clear();\n        self.any = false;\n    }\n\n    /// Could a given element possibly be a scope root?\n    fn early_reject<E: TElement>(&self, element: E, quirks_mode: QuirksMode) -> bool {\n        if self.any {\n            return false;\n        }\n\n        if let Some(id) = element.id() {\n            if self.buckets.ids.get(id, quirks_mode).is_some() {\n                return false;\n            }\n        }\n\n        let mut found = false;\n        element.each_class(|cls| {\n            if self.buckets.classes.get(cls, quirks_mode).is_some() {\n                found = true;\n            }\n        });\n        if found {\n            return false;\n        }\n\n        if self.buckets.local_names.get(element.local_name()).is_some() {\n            return false;\n        }\n\n        true\n    }\n}\n\n/// Determine if this selector list, when used as a scope bound selector, is considered trivial.\npub fn scope_selector_list_is_trivial(list: &SelectorList<SelectorImpl>) -> bool {\n    fn scope_selector_is_trivial(selector: &Selector<SelectorImpl>) -> bool {\n        // A selector is trivial if:\n        // * There is no selector conditional on its siblings and/or descendant to match, and\n        // * There is no dependency on sibling relations, and\n        // * There's no ID selector in the selector. A more correct approach may be to ensure that\n        //   scoping roots of the style sharing candidates and targets have matching IDs, but that\n        //   requires re-plumbing what we pass around for scope roots.\n        let mut iter = selector.iter();\n        loop {\n            while let Some(c) = iter.next() {\n                match c {\n                    Component::ID(_)\n                    | Component::Nth(_)\n                    | Component::NthOf(_)\n                    | Component::Has(_) => return false,\n                    Component::Is(ref list)\n                    | Component::Where(ref list)\n                    | Component::Negation(ref list) => {\n                        if !scope_selector_list_is_trivial(list) {\n                            return false;\n                        }\n                    },\n                    _ => (),\n                }\n            }\n\n            match iter.next_sequence() {\n                Some(c) => {\n                    if c.is_sibling() {\n                        return false;\n                    }\n                },\n                None => return true,\n            }\n        }\n    }\n\n    list.slice().iter().all(|s| scope_selector_is_trivial(s))\n}\n"
  },
  {
    "path": "style/stylesheets/starting_style_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! before-change style: the `@starting-style` rules.\n//! https://drafts.csswg.org/css-transitions-2/#defining-before-change-style\n\nuse crate::derives::*;\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::CssRules;\nuse cssparser::SourceLocation;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse servo_arc::Arc;\nuse std::fmt::{self, Debug, Write};\nuse style_traits::CssStringWriter;\n\n/// A [`@starting-style`][starting-style] rule.\n///\n/// [starting-style]: https://drafts.csswg.org/css-transitions-2/#at-ruledef-starting-style\n#[derive(Debug, ToShmem)]\npub struct StartingStyleRule {\n    /// The nested rules to this starting-style rule.\n    pub rules: Arc<Locked<CssRules>>,\n    /// The source position where this starting-style rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl StartingStyleRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl ToCssWithGuard for StartingStyleRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@starting-style\")?;\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for StartingStyleRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        StartingStyleRule {\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/style_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A style rule.\n\nuse crate::derives::*;\nuse crate::properties::PropertyDeclarationBlock;\nuse crate::selector_parser::SelectorImpl;\nuse crate::shared_lock::{\n    DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,\n};\nuse crate::stylesheets::{style_or_page_rule_to_css, CssRules};\nuse cssparser::SourceLocation;\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{\n    MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf,\n};\nuse selectors::SelectorList;\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::CssStringWriter;\n\n/// A style rule, with selectors and declarations.\n#[derive(Debug, ToShmem)]\npub struct StyleRule {\n    /// The list of selectors in this rule.\n    pub selectors: SelectorList<SelectorImpl>,\n    /// The declaration block with the properties it contains.\n    pub block: Arc<Locked<PropertyDeclarationBlock>>,\n    /// The nested rules to this style rule. Only non-`None` when nesting is enabled.\n    pub rules: Option<Arc<Locked<CssRules>>>,\n    /// The location in the sheet where it was found.\n    pub source_location: SourceLocation,\n}\n\nimpl DeepCloneWithLock for StyleRule {\n    /// Deep clones this StyleRule.\n    fn deep_clone_with_lock(\n        &self,\n        lock: &SharedRwLock,\n        guard: &SharedRwLockReadGuard,\n    ) -> StyleRule {\n        StyleRule {\n            selectors: self.selectors.clone(),\n            block: Arc::new(lock.wrap(self.block.read_with(guard).clone())),\n            rules: self.rules.as_ref().map(|rules| {\n                let rules = rules.read_with(guard);\n                Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard)))\n            }),\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\nimpl StyleRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n        n += self.selectors.unconditional_size_of(ops);\n        n += self.block.unconditional_shallow_size_of(ops)\n            + self.block.read_with(guard).size_of(ops);\n        if let Some(ref rules) = self.rules {\n            n += rules.unconditional_shallow_size_of(ops)\n                + rules.read_with(guard).size_of(guard, ops)\n        }\n        n\n    }\n}\n\nimpl ToCssWithGuard for StyleRule {\n    /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        use cssparser::ToCss;\n        self.selectors.to_css(dest)?;\n        dest.write_char(' ')?;\n        style_or_page_rule_to_css(self.rules.as_ref(), &self.block, guard, dest)\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/stylesheet.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::error_reporting::{ContextualParseError, ParseErrorReporter};\nuse crate::media_queries::MediaList;\nuse crate::parser::ParserContext;\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard};\nuse crate::stylesheets::loader::StylesheetLoader;\nuse crate::stylesheets::rule_parser::{State, TopLevelRuleParser};\nuse crate::stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator};\nuse crate::stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator};\nuse crate::stylesheets::{\n    CssRule, CssRules, CustomMediaEvaluator, CustomMediaMap, Origin, UrlExtraData,\n};\nuse crate::use_counters::UseCounters;\nuse crate::{Namespace, Prefix};\nuse cssparser::{Parser, ParserInput, StyleSheetParser};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse rustc_hash::FxHashMap;\nuse servo_arc::Arc;\nuse std::ops::Deref;\nuse std::sync::atomic::{AtomicBool, Ordering};\nuse style_traits::ParsingMode;\n\nuse super::scope_rule::ImplicitScopeRoot;\n\n/// A set of namespaces applying to a given stylesheet.\n///\n/// The namespace id is used in gecko\n#[derive(Clone, Debug, Default, MallocSizeOf)]\n#[allow(missing_docs)]\npub struct Namespaces {\n    pub default: Option<Namespace>,\n    pub prefixes: FxHashMap<Prefix, Namespace>,\n}\n\n/// The contents of a given stylesheet. This effectively maps to a\n/// StyleSheetInner in Gecko.\n#[derive(Debug)]\npub struct StylesheetContents {\n    /// List of rules in the order they were found (important for\n    /// cascading order)\n    pub rules: Arc<Locked<CssRules>>,\n    /// The origin of this stylesheet.\n    pub origin: Origin,\n    /// The url data this stylesheet should use.\n    pub url_data: UrlExtraData,\n    /// The namespaces that apply to this stylesheet.\n    pub namespaces: Namespaces,\n    /// The quirks mode of this stylesheet.\n    pub quirks_mode: QuirksMode,\n    /// This stylesheet's source map URL.\n    pub source_map_url: Option<String>,\n    /// This stylesheet's source URL.\n    pub source_url: Option<String>,\n    /// The use counters of the original stylesheet.\n    pub use_counters: UseCounters,\n\n    /// We don't want to allow construction outside of this file, to guarantee\n    /// that all contents are created with Arc<>.\n    _forbid_construction: (),\n}\n\nimpl StylesheetContents {\n    /// Parse a given CSS string, with a given url-data, origin, and\n    /// quirks mode.\n    pub fn from_str(\n        css: &str,\n        url_data: UrlExtraData,\n        origin: Origin,\n        shared_lock: &SharedRwLock,\n        stylesheet_loader: Option<&dyn StylesheetLoader>,\n        error_reporter: Option<&dyn ParseErrorReporter>,\n        quirks_mode: QuirksMode,\n        allow_import_rules: AllowImportRules,\n        sanitization_data: Option<&mut SanitizationData>,\n    ) -> Arc<Self> {\n        let use_counters = UseCounters::default();\n        let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules(\n            css,\n            &url_data,\n            origin,\n            &shared_lock,\n            stylesheet_loader,\n            error_reporter,\n            quirks_mode,\n            Some(&use_counters),\n            allow_import_rules,\n            sanitization_data,\n        );\n\n        Arc::new(Self {\n            rules: CssRules::new(rules, &shared_lock),\n            origin,\n            url_data,\n            namespaces,\n            quirks_mode,\n            source_map_url,\n            source_url,\n            use_counters,\n            _forbid_construction: (),\n        })\n    }\n\n    /// Creates a new StylesheetContents with the specified pre-parsed rules,\n    /// origin, URL data, and quirks mode.\n    ///\n    /// Since the rules have already been parsed, and the intention is that\n    /// this function is used for read only User Agent style sheets, an empty\n    /// namespace map is used, and the source map and source URLs are set to\n    /// None.\n    ///\n    /// An empty namespace map should be fine, as it is only used for parsing,\n    /// not serialization of existing selectors.  Since UA sheets are read only,\n    /// we should never need the namespace map.\n    pub fn from_shared_data(\n        rules: Arc<Locked<CssRules>>,\n        origin: Origin,\n        url_data: UrlExtraData,\n        quirks_mode: QuirksMode,\n    ) -> Arc<Self> {\n        debug_assert!(rules.is_static());\n        Arc::new(Self {\n            rules,\n            origin,\n            url_data,\n            namespaces: Namespaces::default(),\n            quirks_mode,\n            source_map_url: None,\n            source_url: None,\n            use_counters: UseCounters::default(),\n            _forbid_construction: (),\n        })\n    }\n\n    /// Returns a reference to the list of rules.\n    #[inline]\n    pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {\n        &self.rules.read_with(guard).0\n    }\n\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        if self.rules.is_static() {\n            return 0;\n        }\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n\n    /// Return an iterator using the condition `C`.\n    #[inline]\n    pub fn iter_rules<'a, 'b, C, CMM>(\n        &'a self,\n        device: &'a Device,\n        custom_media: CMM,\n        guard: &'a SharedRwLockReadGuard<'b>,\n    ) -> RulesIterator<'a, 'b, C, CMM>\n    where\n        C: NestedRuleIterationCondition,\n        CMM: Deref<Target = CustomMediaMap>,\n    {\n        RulesIterator::new(\n            device,\n            self.quirks_mode,\n            custom_media,\n            guard,\n            self.rules(guard).iter(),\n        )\n    }\n\n    /// Return an iterator over the effective rules within the style-sheet, as\n    /// according to the supplied `Device`.\n    #[inline]\n    pub fn effective_rules<'a, 'b, CMM: Deref<Target = CustomMediaMap>>(\n        &'a self,\n        device: &'a Device,\n        custom_media: CMM,\n        guard: &'a SharedRwLockReadGuard<'b>,\n    ) -> EffectiveRulesIterator<'a, 'b, CMM> {\n        self.iter_rules::<EffectiveRules, CMM>(device, custom_media, guard)\n    }\n\n    /// Perform a deep clone, of this stylesheet, with an explicit URL data if needed.\n    pub fn deep_clone(\n        &self,\n        lock: &SharedRwLock,\n        url_data: Option<&UrlExtraData>,\n        guard: &SharedRwLockReadGuard,\n    ) -> Arc<Self> {\n        // Make a deep clone of the rules, using the new lock.\n        let rules = self\n            .rules\n            .read_with(guard)\n            .deep_clone_with_lock(lock, guard);\n\n        let url_data = url_data.cloned().unwrap_or_else(|| self.url_data.clone());\n\n        Arc::new(Self {\n            rules: Arc::new(lock.wrap(rules)),\n            quirks_mode: self.quirks_mode,\n            origin: self.origin,\n            url_data,\n            namespaces: self.namespaces.clone(),\n            source_map_url: self.source_map_url.clone(),\n            source_url: self.source_url.clone(),\n            use_counters: self.use_counters.clone(),\n            _forbid_construction: (),\n        })\n    }\n}\n\n/// The structure servo uses to represent a stylesheet.\n#[derive(Debug)]\npub struct Stylesheet {\n    /// The contents of this stylesheet.\n    pub contents: Locked<Arc<StylesheetContents>>,\n    /// The lock used for objects inside this stylesheet\n    pub shared_lock: SharedRwLock,\n    /// List of media associated with the Stylesheet.\n    pub media: Arc<Locked<MediaList>>,\n    /// Whether this stylesheet should be disabled.\n    pub disabled: AtomicBool,\n}\n\n/// A trait to represent a given stylesheet in a document.\npub trait StylesheetInDocument: ::std::fmt::Debug {\n    /// Get whether this stylesheet is enabled.\n    fn enabled(&self) -> bool;\n\n    /// Get the media associated with this stylesheet.\n    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>;\n\n    /// Returns a reference to the contents of the stylesheet.\n    fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents;\n\n    /// Returns whether the style-sheet applies for the current device.\n    fn is_effective_for_device(\n        &self,\n        device: &Device,\n        custom_media: &CustomMediaMap,\n        guard: &SharedRwLockReadGuard,\n    ) -> bool {\n        let media = match self.media(guard) {\n            Some(m) => m,\n            None => return true,\n        };\n        media.evaluate(\n            device,\n            self.contents(guard).quirks_mode,\n            &mut CustomMediaEvaluator::new(custom_media, guard),\n        )\n    }\n\n    /// Return the implicit scope root for this stylesheet, if one exists.\n    fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot>;\n}\n\nimpl StylesheetInDocument for Stylesheet {\n    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {\n        Some(self.media.read_with(guard))\n    }\n\n    fn enabled(&self) -> bool {\n        !self.disabled()\n    }\n\n    #[inline]\n    fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {\n        self.contents.read_with(guard)\n    }\n\n    fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {\n        None\n    }\n}\n\n/// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and\n/// suitable for its use in a `StylesheetSet`.\n#[derive(Clone, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct DocumentStyleSheet(\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")] pub Arc<Stylesheet>,\n);\n\nimpl PartialEq for DocumentStyleSheet {\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl StylesheetInDocument for DocumentStyleSheet {\n    fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {\n        self.0.media(guard)\n    }\n\n    fn enabled(&self) -> bool {\n        self.0.enabled()\n    }\n\n    #[inline]\n    fn contents<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a StylesheetContents {\n        self.0.contents(guard)\n    }\n\n    fn implicit_scope_root(&self) -> Option<ImplicitScopeRoot> {\n        None\n    }\n}\n\n/// The kind of sanitization to use when parsing a stylesheet.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum SanitizationKind {\n    /// Perform no sanitization.\n    None,\n    /// Allow only @font-face, style rules, and @namespace.\n    Standard,\n    /// Allow everything but conditional rules.\n    NoConditionalRules,\n}\n\n/// Whether @import rules are allowed.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum AllowImportRules {\n    /// @import rules will be parsed.\n    Yes,\n    /// @import rules will not be parsed.\n    No,\n}\n\nimpl SanitizationKind {\n    fn allows(self, rule: &CssRule, guard: &SharedRwLockReadGuard) -> bool {\n        if !self.allows_self(rule) {\n            return false;\n        }\n        for child in rule.children(guard) {\n            if !self.allows(child, guard) {\n                return false;\n            }\n        }\n        true\n    }\n\n    fn allows_self(self, rule: &CssRule) -> bool {\n        debug_assert_ne!(self, SanitizationKind::None);\n        // NOTE(emilio): If this becomes more complex (not filtering just by\n        // top-level rules), we should thread all the data through nested rules\n        // and such. But this doesn't seem necessary at the moment.\n        let is_standard = matches!(self, SanitizationKind::Standard);\n        match *rule {\n            CssRule::Document(..) |\n            CssRule::Media(..) |\n            CssRule::CustomMedia(..) |\n            CssRule::Supports(..) |\n            CssRule::Import(..) |\n            CssRule::Container(..) |\n            // TODO(emilio): Perhaps Layer should not be always sanitized? But\n            // we sanitize @media and co, so this seems safer for now.\n            CssRule::LayerStatement(..) |\n            CssRule::LayerBlock(..) |\n            // TODO(dshin): Same comment as Layer applies - shouldn't give away\n            // something like display size - erring on the side of \"safe\" for now.\n            CssRule::Scope(..) |\n            CssRule::StartingStyle(..) |\n            CssRule::AppearanceBase(..) => false,\n\n            CssRule::FontFace(..) |\n            CssRule::Namespace(..) |\n            CssRule::Style(..) |\n            CssRule::NestedDeclarations(..) |\n            CssRule::PositionTry(..) => true,\n\n            CssRule::Keyframes(..) |\n            CssRule::Page(..) |\n            CssRule::Margin(..) |\n            CssRule::Property(..) |\n            CssRule::FontFeatureValues(..) |\n            CssRule::FontPaletteValues(..) |\n            CssRule::CounterStyle(..) |\n            CssRule::ViewTransition(..) => !is_standard,\n        }\n    }\n}\n\n/// A struct to hold the data relevant to style sheet sanitization.\n#[derive(Debug)]\npub struct SanitizationData {\n    kind: SanitizationKind,\n    output: String,\n}\n\nimpl SanitizationData {\n    /// Create a new input for sanitization.\n    #[inline]\n    pub fn new(kind: SanitizationKind) -> Option<Self> {\n        if matches!(kind, SanitizationKind::None) {\n            return None;\n        }\n        Some(Self {\n            kind,\n            output: String::new(),\n        })\n    }\n\n    /// Take the sanitized output.\n    #[inline]\n    pub fn take(self) -> String {\n        self.output\n    }\n}\n\nimpl Stylesheet {\n    fn parse_rules(\n        css: &str,\n        url_data: &UrlExtraData,\n        origin: Origin,\n        shared_lock: &SharedRwLock,\n        stylesheet_loader: Option<&dyn StylesheetLoader>,\n        error_reporter: Option<&dyn ParseErrorReporter>,\n        quirks_mode: QuirksMode,\n        use_counters: Option<&UseCounters>,\n        allow_import_rules: AllowImportRules,\n        mut sanitization_data: Option<&mut SanitizationData>,\n    ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) {\n        let mut input = ParserInput::new(css);\n        let mut input = Parser::new(&mut input);\n\n        let context = ParserContext::new(\n            origin,\n            url_data,\n            None,\n            ParsingMode::DEFAULT,\n            quirks_mode,\n            /* namespaces = */ Default::default(),\n            error_reporter,\n            use_counters,\n            /* attr_taint */ Default::default(),\n        );\n\n        let mut rule_parser = TopLevelRuleParser {\n            shared_lock,\n            loader: stylesheet_loader,\n            context,\n            state: State::Start,\n            dom_error: None,\n            insert_rule_context: None,\n            allow_import_rules,\n            declaration_parser_state: Default::default(),\n            first_declaration_block: Default::default(),\n            wants_first_declaration_block: false,\n            error_reporting_state: Default::default(),\n            rules: Vec::new(),\n        };\n\n        {\n            let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser);\n            while let Some(result) = iter.next() {\n                match result {\n                    Ok(rule_start) => {\n                        // TODO(emilio, nesting): sanitize nested CSS rules, probably?\n                        if let Some(ref mut data) = sanitization_data {\n                            if let Some(ref rule) = iter.parser.rules.last() {\n                                if !data.kind.allows(rule, &shared_lock.read()) {\n                                    iter.parser.rules.pop();\n                                    continue;\n                                }\n                            }\n                            let end = iter.input.position().byte_index();\n                            data.output.push_str(&css[rule_start.byte_index()..end]);\n                        }\n                    },\n                    Err((error, slice)) => {\n                        let location = error.location;\n                        let error = ContextualParseError::InvalidRule(slice, error);\n                        iter.parser.context.log_css_error(location, error);\n                    },\n                }\n            }\n        }\n\n        let source_map_url = input.current_source_map_url().map(String::from);\n        let source_url = input.current_source_url().map(String::from);\n        (\n            rule_parser.context.namespaces.into_owned(),\n            rule_parser.rules,\n            source_map_url,\n            source_url,\n        )\n    }\n\n    /// Creates an empty stylesheet and parses it with a given base url, origin and media.\n    pub fn from_str(\n        css: &str,\n        url_data: UrlExtraData,\n        origin: Origin,\n        media: Arc<Locked<MediaList>>,\n        shared_lock: SharedRwLock,\n        stylesheet_loader: Option<&dyn StylesheetLoader>,\n        error_reporter: Option<&dyn ParseErrorReporter>,\n        quirks_mode: QuirksMode,\n        allow_import_rules: AllowImportRules,\n    ) -> Self {\n        // FIXME: Consider adding use counters to Servo?\n        let contents = StylesheetContents::from_str(\n            css,\n            url_data,\n            origin,\n            &shared_lock,\n            stylesheet_loader,\n            error_reporter,\n            quirks_mode,\n            allow_import_rules,\n            /* sanitized_output = */ None,\n        );\n\n        Stylesheet {\n            contents: shared_lock.wrap(contents),\n            shared_lock,\n            media,\n            disabled: AtomicBool::new(false),\n        }\n    }\n\n    /// Returns whether the stylesheet has been explicitly disabled through the\n    /// CSSOM.\n    pub fn disabled(&self) -> bool {\n        self.disabled.load(Ordering::SeqCst)\n    }\n\n    /// Records that the stylesheet has been explicitly disabled through the\n    /// CSSOM.\n    ///\n    /// Returns whether the the call resulted in a change in disabled state.\n    ///\n    /// Disabled stylesheets remain in the document, but their rules are not\n    /// added to the Stylist.\n    pub fn set_disabled(&self, disabled: bool) -> bool {\n        self.disabled.swap(disabled, Ordering::SeqCst) != disabled\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl Clone for Stylesheet {\n    fn clone(&self) -> Self {\n        // Create a new lock for our clone.\n        let lock = self.shared_lock.clone();\n        let guard = self.shared_lock.read();\n\n        // Make a deep clone of the media, using the new lock.\n        let media = self.media.read_with(&guard).clone();\n        let media = Arc::new(lock.wrap(media));\n        let contents = lock.wrap(\n            self.contents\n                .read_with(&guard)\n                .deep_clone(&lock, None, &guard),\n        );\n\n        Stylesheet {\n            contents,\n            media,\n            shared_lock: lock,\n            disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)),\n        }\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/supports_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports)\n\nuse crate::derives::*;\nuse crate::font_face::{FontFaceSourceFormatKeyword, FontFaceSourceTechFlags};\nuse crate::parser::ParserContext;\nuse crate::properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration};\nuse crate::selector_parser::{SelectorImpl, SelectorParser};\nuse crate::shared_lock::{DeepCloneWithLock, Locked};\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse crate::stylesheets::{CssRuleType, CssRules};\nuse cssparser::parse_important;\nuse cssparser::{match_ignore_ascii_case, ParseError as CssParseError, ParserInput};\nuse cssparser::{Delimiter, Parser, SourceLocation, Token};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};\nuse selectors::parser::{Selector, SelectorParseErrorKind};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse std::str;\nuse style_traits::{CssStringWriter, CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// An [`@supports`][supports] rule.\n///\n/// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports\n#[derive(Debug, ToShmem)]\npub struct SupportsRule {\n    /// The parsed condition\n    pub condition: SupportsCondition,\n    /// Child rules\n    pub rules: Arc<Locked<CssRules>>,\n    /// The result of evaluating the condition\n    pub enabled: bool,\n    /// The line and column of the rule's source code.\n    pub source_location: SourceLocation,\n}\n\nimpl SupportsRule {\n    /// Measure heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {\n        // Measurement of other fields may be added later.\n        self.rules.unconditional_shallow_size_of(ops)\n            + self.rules.read_with(guard).size_of(guard, ops)\n    }\n}\n\nimpl ToCssWithGuard for SupportsRule {\n    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@supports \")?;\n        self.condition.to_css(&mut CssWriter::new(dest))?;\n        self.rules.read_with(guard).to_css_block(guard, dest)\n    }\n}\n\nimpl DeepCloneWithLock for SupportsRule {\n    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {\n        let rules = self.rules.read_with(guard);\n        SupportsRule {\n            condition: self.condition.clone(),\n            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),\n            enabled: self.enabled,\n            source_location: self.source_location.clone(),\n        }\n    }\n}\n\n/// An @supports condition\n///\n/// <https://drafts.csswg.org/css-conditional-3/#at-supports>\n#[derive(Clone, Debug, ToShmem)]\npub enum SupportsCondition {\n    /// `not (condition)`\n    Not(Box<SupportsCondition>),\n    /// `(condition)`\n    Parenthesized(Box<SupportsCondition>),\n    /// `(condition) and (condition) and (condition) ..`\n    And(Vec<SupportsCondition>),\n    /// `(condition) or (condition) or (condition) ..`\n    Or(Vec<SupportsCondition>),\n    /// `property-ident: value` (value can be any tokens)\n    Declaration(Declaration),\n    /// A `selector()` function.\n    Selector(RawSelector),\n    /// `font-format(<font-format>)`\n    FontFormat(FontFaceSourceFormatKeyword),\n    /// `font-tech(<font-tech>)`\n    FontTech(FontFaceSourceTechFlags),\n    /// `(any tokens)` or `func(any tokens)`\n    FutureSyntax(String),\n}\n\nimpl SupportsCondition {\n    /// Parse a condition\n    ///\n    /// <https://drafts.csswg.org/css-conditional/#supports_condition>\n    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"not\")).is_ok() {\n            let inner = SupportsCondition::parse_in_parens(input)?;\n            return Ok(SupportsCondition::Not(Box::new(inner)));\n        }\n\n        let in_parens = SupportsCondition::parse_in_parens(input)?;\n\n        let location = input.current_source_location();\n        let (keyword, wrapper) = match input.next() {\n            // End of input\n            Err(..) => return Ok(in_parens),\n            Ok(&Token::Ident(ref ident)) => {\n                match_ignore_ascii_case! { &ident,\n                    \"and\" => (\"and\", SupportsCondition::And as fn(_) -> _),\n                    \"or\" => (\"or\", SupportsCondition::Or as fn(_) -> _),\n                    _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))\n                }\n            },\n            Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),\n        };\n\n        let mut conditions = Vec::with_capacity(2);\n        conditions.push(in_parens);\n        loop {\n            conditions.push(SupportsCondition::parse_in_parens(input)?);\n            if input\n                .try_parse(|input| input.expect_ident_matching(keyword))\n                .is_err()\n            {\n                // Did not find the expected keyword.\n                // If we found some other token, it will be rejected by\n                // `Parser::parse_entirely` somewhere up the stack.\n                return Ok(wrapper(conditions));\n            }\n        }\n    }\n\n    /// Parses a functional supports condition.\n    fn parse_functional<'i, 't>(\n        function: &str,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        match_ignore_ascii_case! { function,\n            \"selector\" => {\n                let pos = input.position();\n                consume_any_value(input)?;\n                Ok(SupportsCondition::Selector(RawSelector(\n                    input.slice_from(pos).to_owned()\n                )))\n            },\n            \"font-format\" if static_prefs::pref!(\"layout.css.font-tech.enabled\") => {\n                let kw = FontFaceSourceFormatKeyword::parse(input)?;\n                Ok(SupportsCondition::FontFormat(kw))\n            },\n            \"font-tech\" if static_prefs::pref!(\"layout.css.font-tech.enabled\") => {\n                let flag = FontFaceSourceTechFlags::parse_one(input)?;\n                Ok(SupportsCondition::FontTech(flag))\n            },\n            _ => {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n        }\n    }\n\n    /// Parses an `@import` condition as per\n    /// https://drafts.csswg.org/css-cascade-5/#typedef-import-conditions\n    pub fn parse_for_import<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"supports\")?;\n        input.parse_nested_block(parse_condition_or_declaration)\n    }\n\n    /// <https://drafts.csswg.org/css-conditional-3/#supports_condition_in_parens>\n    fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        // Whitespace is normally taken care of in `Parser::next`, but we want to not include it in\n        // `pos` for the SupportsCondition::FutureSyntax cases.\n        input.skip_whitespace();\n        let pos = input.position();\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::ParenthesisBlock => {\n                let nested = input\n                    .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration));\n                if let Ok(nested) = nested {\n                    return Ok(Self::Parenthesized(Box::new(nested)));\n                }\n            },\n            Token::Function(ref ident) => {\n                let ident = ident.clone();\n                let nested = input.try_parse(|input| {\n                    input.parse_nested_block(|input| {\n                        SupportsCondition::parse_functional(&ident, input)\n                    })\n                });\n                if nested.is_ok() {\n                    return nested;\n                }\n            },\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        }\n        input.parse_nested_block(consume_any_value)?;\n        Ok(SupportsCondition::FutureSyntax(\n            input.slice_from(pos).to_owned(),\n        ))\n    }\n\n    /// Evaluate a supports condition\n    pub fn eval(&self, cx: &ParserContext) -> bool {\n        match *self {\n            SupportsCondition::Not(ref cond) => !cond.eval(cx),\n            SupportsCondition::Parenthesized(ref cond) => cond.eval(cx),\n            SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)),\n            SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)),\n            SupportsCondition::Declaration(ref decl) => decl.eval(cx),\n            SupportsCondition::Selector(ref selector) => selector.eval(cx),\n            SupportsCondition::FontFormat(ref format) => eval_font_format(format),\n            SupportsCondition::FontTech(ref tech) => eval_font_tech(tech),\n            SupportsCondition::FutureSyntax(_) => false,\n        }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nfn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool {\n    use crate::gecko_bindings::bindings;\n    unsafe { bindings::Gecko_IsFontFormatSupported(*kw) }\n}\n\n#[cfg(feature = \"gecko\")]\nfn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool {\n    use crate::gecko_bindings::bindings;\n    unsafe { bindings::Gecko_IsFontTechSupported(*flag) }\n}\n\n#[cfg(feature = \"servo\")]\nfn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool {\n    false\n}\n\n#[cfg(feature = \"servo\")]\nfn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool {\n    false\n}\n\n/// supports_condition | declaration\n/// <https://drafts.csswg.org/css-conditional/#dom-css-supports-conditiontext-conditiontext>\npub fn parse_condition_or_declaration<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<SupportsCondition, ParseError<'i>> {\n    if let Ok(condition) = input.try_parse(SupportsCondition::parse) {\n        Ok(condition)\n    } else {\n        Declaration::parse(input).map(SupportsCondition::Declaration)\n    }\n}\n\nimpl ToCss for SupportsCondition {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            SupportsCondition::Not(ref cond) => {\n                dest.write_str(\"not \")?;\n                cond.to_css(dest)\n            },\n            SupportsCondition::Parenthesized(ref cond) => {\n                dest.write_char('(')?;\n                cond.to_css(dest)?;\n                dest.write_char(')')\n            },\n            SupportsCondition::And(ref vec) => {\n                let mut first = true;\n                for cond in vec {\n                    if !first {\n                        dest.write_str(\" and \")?;\n                    }\n                    first = false;\n                    cond.to_css(dest)?;\n                }\n                Ok(())\n            },\n            SupportsCondition::Or(ref vec) => {\n                let mut first = true;\n                for cond in vec {\n                    if !first {\n                        dest.write_str(\" or \")?;\n                    }\n                    first = false;\n                    cond.to_css(dest)?;\n                }\n                Ok(())\n            },\n            SupportsCondition::Declaration(ref decl) => decl.to_css(dest),\n            SupportsCondition::Selector(ref selector) => {\n                dest.write_str(\"selector(\")?;\n                selector.to_css(dest)?;\n                dest.write_char(')')\n            },\n            SupportsCondition::FontFormat(ref kw) => {\n                dest.write_str(\"font-format(\")?;\n                kw.to_css(dest)?;\n                dest.write_char(')')\n            },\n            SupportsCondition::FontTech(ref flag) => {\n                dest.write_str(\"font-tech(\")?;\n                flag.to_css(dest)?;\n                dest.write_char(')')\n            },\n            SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s),\n        }\n    }\n}\n\n#[derive(Clone, Debug, ToShmem)]\n/// A possibly-invalid CSS selector.\npub struct RawSelector(pub String);\n\nimpl ToCss for RawSelector {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(&self.0)\n    }\n}\n\nimpl RawSelector {\n    /// Tries to evaluate a `selector()` function.\n    pub fn eval(&self, context: &ParserContext) -> bool {\n        let mut input = ParserInput::new(&self.0);\n        let mut input = Parser::new(&mut input);\n        input\n            .parse_entirely(|input| -> Result<(), CssParseError<()>> {\n                let parser = SelectorParser {\n                    namespaces: &context.namespaces,\n                    stylesheet_origin: context.stylesheet_origin,\n                    url_data: context.url_data,\n                    for_supports_rule: true,\n                };\n\n                Selector::<SelectorImpl>::parse(&parser, input)\n                    .map_err(|_| input.new_custom_error(()))?;\n\n                Ok(())\n            })\n            .is_ok()\n    }\n}\n\n#[derive(Clone, Debug, ToShmem)]\n/// A possibly-invalid property declaration\npub struct Declaration(pub String);\n\nimpl ToCss for Declaration {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(&self.0)\n    }\n}\n\n/// <https://drafts.csswg.org/css-syntax-3/#typedef-any-value>\nfn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> {\n    input.expect_no_error_token().map_err(|err| err.into())\n}\n\nimpl Declaration {\n    /// Parse a declaration\n    pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Declaration, ParseError<'i>> {\n        let pos = input.position();\n        input.expect_ident()?;\n        input.expect_colon()?;\n        consume_any_value(input)?;\n        Ok(Declaration(input.slice_from(pos).to_owned()))\n    }\n\n    /// Determine if a declaration parses\n    ///\n    /// <https://drafts.csswg.org/css-conditional-3/#support-definition>\n    pub fn eval(&self, context: &ParserContext) -> bool {\n        debug_assert!(context.rule_types().contains(CssRuleType::Style));\n\n        let mut input = ParserInput::new(&self.0);\n        let mut input = Parser::new(&mut input);\n        input\n            .parse_entirely(|input| -> Result<(), CssParseError<()>> {\n                let prop = input.expect_ident_cloned().unwrap();\n                input.expect_colon().unwrap();\n\n                let id =\n                    PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?;\n\n                let mut declarations = SourcePropertyDeclaration::default();\n                input.parse_until_before(Delimiter::Bang, |input| {\n                    PropertyDeclaration::parse_into(&mut declarations, id, &context, input)\n                        .map_err(|_| input.new_custom_error(()))\n                })?;\n                let _ = input.try_parse(parse_important);\n                Ok(())\n            })\n            .is_ok()\n    }\n}\n"
  },
  {
    "path": "style/stylesheets/view_transition_rule.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A [`@view-transition`][view-transition] rule\n//!\n//! [view-transition]: https://drafts.csswg.org/css-view-transitions-2/#view-transition-rule\n\nuse crate::derives::*;\nuse crate::error_reporting::ContextualParseError;\nuse crate::parser::ParserContext;\nuse crate::properties::view_transition::{DescriptorParser, Descriptors};\nuse crate::shared_lock::DeepCloneWithLock;\nuse crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};\nuse cssparser::{Parser, RuleBodyParser, SourceLocation};\nuse std::fmt::{self, Write};\nuse style_traits::{CssStringWriter, CssWriter, ToCss};\n\n/// A view-transition rule\n#[derive(Clone, Debug, MallocSizeOf, ToShmem)]\npub struct ViewTransitionRule {\n    /// The descriptors of this view-transition rule.\n    pub descriptors: Descriptors,\n    /// The source position where this view-transition rule was found.\n    pub source_location: SourceLocation,\n}\n\nimpl ViewTransitionRule {\n    /// Parses a ViewTransitionRule\n    pub fn parse(context: &ParserContext, input: &mut Parser, location: SourceLocation) -> Self {\n        let mut rule = ViewTransitionRule {\n            descriptors: Descriptors::default(),\n            source_location: location,\n        };\n\n        let mut parser = DescriptorParser {\n            context,\n            descriptors: &mut rule.descriptors,\n        };\n        let mut iter = RuleBodyParser::new(input, &mut parser);\n\n        while let Some(declaration) = iter.next() {\n            if let Err((error, slice)) = declaration {\n                let location = error.location;\n                let error = ContextualParseError::UnsupportedViewTransitionDescriptor(slice, error);\n                context.log_css_error(location, error);\n            }\n        }\n\n        rule\n    }\n}\n\nimpl ToCssWithGuard for ViewTransitionRule {\n    fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {\n        dest.write_str(\"@view-transition { \")?;\n        self.descriptors.to_css(&mut CssWriter::new(dest))?;\n        dest.write_str(\"}\")\n    }\n}\n\nimpl DeepCloneWithLock for ViewTransitionRule {\n    fn deep_clone_with_lock(&self, _lock: &SharedRwLock, _guard: &SharedRwLockReadGuard) -> Self {\n        self.clone()\n    }\n}\n\n/// <https://drafts.csswg.org/css-view-transitions-2/#view-transition-navigation>\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum NavigationType {\n    /// No transition\n    #[default]\n    None,\n    /// Perform transition if conditions are met\n    Auto,\n}\n\n/// The `types` descriptor value: `none | <custom-ident>+`\n///\n/// <https://drafts.csswg.org/css-view-transitions-2/#typedef-pt-name-selector-list>\npub use crate::values::specified::animation::ViewTransitionClass as TransitionTypes;\n"
  },
  {
    "path": "style/stylist.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Selector matching.\n\nuse crate::applicable_declarations::{\n    ApplicableDeclarationBlock, ApplicableDeclarationList, CascadePriority, ScopeProximity,\n};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::{CascadeInputs, QuirksMode};\nuse crate::custom_properties::ComputedCustomProperties;\nuse crate::custom_properties::{parse_name, SpecifiedValue};\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::dom::TElement;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion};\nuse crate::invalidation::element::invalidation_map::{\n    note_selector_for_invalidation, AdditionalRelativeSelectorInvalidationMap, Dependency,\n    DependencyInvalidationKind, InvalidationMap, ScopeDependencyInvalidationKind,\n};\nuse crate::invalidation::media_queries::{\n    EffectiveMediaQueryResults, MediaListKey, ToMediaListKey,\n};\nuse crate::invalidation::stylesheets::{RuleChangeKind, StylesheetInvalidationSet};\n#[cfg(feature = \"gecko\")]\nuse crate::properties::StyleBuilder;\nuse crate::properties::{\n    self, AnimationDeclarations, CascadeMode, ComputedValues, FirstLineReparenting,\n    PropertyDeclarationBlock,\n};\nuse crate::properties_and_values::registry::{\n    PropertyRegistration, ScriptRegistry as CustomPropertyScriptRegistry,\n};\nuse crate::properties_and_values::rule::{\n    Descriptors as PropertyDescriptors, Inherits, PropertyRegistrationError, PropertyRuleName,\n};\nuse crate::properties_and_values::syntax::Descriptor;\nuse crate::rule_cache::{RuleCache, RuleCacheConditions};\nuse crate::rule_collector::RuleCollector;\nuse crate::rule_tree::{\n    CascadeLevel, CascadeOrigin, RuleCascadeFlags, RuleTree, StrongRuleNode, StyleSource,\n};\nuse crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};\nuse crate::selector_parser::{NonTSPseudoClass, PerPseudoElementMap, PseudoElement, SelectorImpl};\nuse crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};\nuse crate::sharing::{RevalidationResult, ScopeRevalidationResult};\nuse crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};\nuse crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};\nuse crate::stylesheets::container_rule::ContainerCondition;\nuse crate::stylesheets::import_rule::ImportLayer;\nuse crate::stylesheets::keyframes_rule::KeyframesAnimation;\nuse crate::stylesheets::layer_rule::{LayerName, LayerOrder};\nuse crate::stylesheets::scope_rule::{\n    collect_scope_roots, element_is_outside_of_scope, scope_selector_list_is_trivial,\n    ImplicitScopeRoot, ScopeRootCandidate, ScopeSubjectMap, ScopeTarget,\n};\nuse crate::stylesheets::UrlExtraData;\nuse crate::stylesheets::{\n    CounterStyleRule, CssRule, CssRuleRef, EffectiveRulesIterator, FontFaceRule,\n    FontFeatureValuesRule, FontPaletteValuesRule, Origin, OriginSet, PagePseudoClassFlags,\n    PageRule, PerOrigin, PerOriginIter, PositionTryRule, StylesheetContents, StylesheetInDocument,\n    ViewTransitionRule,\n};\nuse crate::stylesheets::{CustomMediaEvaluator, CustomMediaMap};\n#[cfg(feature = \"gecko\")]\nuse crate::values::specified::position::PositionTryFallbacksItem;\nuse crate::values::specified::position::PositionTryFallbacksTryTactic;\nuse crate::values::{computed, AtomIdent, Parser, SourceLocation};\nuse crate::AllocErr;\nuse crate::ArcSlice;\nuse crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};\nuse cssparser::ParserInput;\nuse dom::{DocumentState, ElementState};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::MallocUnconditionalShallowSizeOf;\nuse malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};\nuse rustc_hash::FxHashMap;\nuse selectors::attr::{CaseSensitivity, NamespaceConstraint};\nuse selectors::bloom::BloomFilter;\nuse selectors::matching::{\n    matches_selector, selector_may_match, MatchingContext, MatchingMode, NeedsSelectorFlags,\n    SelectorCaches,\n};\nuse selectors::matching::{MatchingForInvalidation, VisitedHandlingMode};\nuse selectors::parser::{\n    AncestorHashes, Combinator, Component, MatchesFeaturelessHost, Selector, SelectorIter,\n    SelectorList,\n};\nuse selectors::visitor::{SelectorListKind, SelectorVisitor};\nuse servo_arc::{Arc, ArcBorrow, ThinArc};\nuse smallvec::SmallVec;\nuse std::cmp::Ordering;\nuse std::hash::{Hash, Hasher};\nuse std::mem;\nuse std::sync::{LazyLock, Mutex};\n\n/// The type of the stylesheets that the stylist contains.\n#[cfg(feature = \"servo\")]\npub type StylistSheet = crate::stylesheets::DocumentStyleSheet;\n\n/// The type of the stylesheets that the stylist contains.\n#[cfg(feature = \"gecko\")]\npub type StylistSheet = crate::gecko::data::GeckoStyleSheet;\n\n#[derive(Debug, Clone)]\nstruct StylesheetContentsPtr(Arc<StylesheetContents>);\n\nimpl PartialEq for StylesheetContentsPtr {\n    #[inline]\n    fn eq(&self, other: &Self) -> bool {\n        Arc::ptr_eq(&self.0, &other.0)\n    }\n}\n\nimpl Eq for StylesheetContentsPtr {}\n\nimpl Hash for StylesheetContentsPtr {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        let contents: &StylesheetContents = &*self.0;\n        (contents as *const StylesheetContents).hash(state)\n    }\n}\n\ntype StyleSheetContentList = Vec<StylesheetContentsPtr>;\n\n/// The @position-try rules that have changed.\n#[derive(Default, Debug, MallocSizeOf)]\npub struct CascadeDataDifference {\n    /// The set of changed @position-try rule names.\n    pub changed_position_try_names: PrecomputedHashSet<Atom>,\n}\n\nimpl CascadeDataDifference {\n    /// Merges another difference into `self`.\n    pub fn merge_with(&mut self, other: Self) {\n        self.changed_position_try_names\n            .extend(other.changed_position_try_names.into_iter())\n    }\n\n    /// Returns whether we're empty.\n    pub fn is_empty(&self) -> bool {\n        self.changed_position_try_names.is_empty()\n    }\n\n    fn update(&mut self, old_data: &PositionTryMap, new_data: &PositionTryMap) {\n        let mut any_different_key = false;\n        let different_len = old_data.len() != new_data.len();\n        for (name, rules) in old_data.iter() {\n            let changed = match new_data.get(name) {\n                Some(new_rule) => !Arc::ptr_eq(&rules.last().unwrap().0, new_rule),\n                None => {\n                    any_different_key = true;\n                    true\n                },\n            };\n            if changed {\n                self.changed_position_try_names.insert(name.clone());\n            }\n        }\n\n        if any_different_key || different_len {\n            for name in new_data.keys() {\n                // If the key exists in both, we've already checked it above.\n                if !old_data.contains_key(name) {\n                    self.changed_position_try_names.insert(name.clone());\n                }\n            }\n        }\n    }\n}\n\n/// A key in the cascade data cache.\n#[derive(Debug, Hash, Default, PartialEq, Eq)]\nstruct CascadeDataCacheKey {\n    media_query_results: Vec<MediaListKey>,\n    contents: StyleSheetContentList,\n}\n\nunsafe impl Send for CascadeDataCacheKey {}\nunsafe impl Sync for CascadeDataCacheKey {}\n\ntrait CascadeDataCacheEntry: Sized {\n    /// Rebuilds the cascade data for the new stylesheet collection. The\n    /// collection is guaranteed to be dirty.\n    fn rebuild<S>(\n        device: &Device,\n        quirks_mode: QuirksMode,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        old_entry: &Self,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<Arc<Self>, AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static;\n    /// Measures heap memory usage.\n    #[cfg(feature = \"gecko\")]\n    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes);\n}\n\nstruct CascadeDataCache<Entry> {\n    entries: FxHashMap<CascadeDataCacheKey, Arc<Entry>>,\n}\n\nimpl<Entry> CascadeDataCache<Entry>\nwhere\n    Entry: CascadeDataCacheEntry,\n{\n    fn new() -> Self {\n        Self {\n            entries: Default::default(),\n        }\n    }\n\n    fn len(&self) -> usize {\n        self.entries.len()\n    }\n\n    // FIXME(emilio): This may need to be keyed on quirks-mode too, though for\n    // UA sheets there aren't class / id selectors on those sheets, usually, so\n    // it's probably ok... For the other cache the quirks mode shouldn't differ\n    // so also should be fine.\n    fn lookup<'a, S>(\n        &'a mut self,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        old_entry: &Entry,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<Option<Arc<Entry>>, AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        use std::collections::hash_map::Entry as HashMapEntry;\n        debug!(\"StyleSheetCache::lookup({})\", self.len());\n\n        if !collection.dirty() {\n            return Ok(None);\n        }\n\n        let mut key = CascadeDataCacheKey::default();\n        let mut custom_media_map = CustomMediaMap::default();\n        for sheet in collection.sheets() {\n            CascadeData::collect_applicable_media_query_results_into(\n                device,\n                sheet,\n                guard,\n                &mut key.media_query_results,\n                &mut key.contents,\n                &mut custom_media_map,\n            )\n        }\n\n        let new_entry;\n        match self.entries.entry(key) {\n            HashMapEntry::Vacant(e) => {\n                debug!(\"> Picking the slow path (not in the cache)\");\n                new_entry = Entry::rebuild(\n                    device,\n                    quirks_mode,\n                    collection,\n                    guard,\n                    old_entry,\n                    difference,\n                )?;\n                e.insert(new_entry.clone());\n            },\n            HashMapEntry::Occupied(mut e) => {\n                // Avoid reusing our old entry (this can happen if we get\n                // invalidated due to CSSOM mutations and our old stylesheet\n                // contents were already unique, for example).\n                if !std::ptr::eq(&**e.get(), old_entry) {\n                    if log_enabled!(log::Level::Debug) {\n                        debug!(\"cache hit for:\");\n                        for sheet in collection.sheets() {\n                            debug!(\" > {:?}\", sheet);\n                        }\n                    }\n                    // The line below ensures the \"committed\" bit is updated\n                    // properly.\n                    collection.each(|_, _, _| true);\n                    return Ok(Some(e.get().clone()));\n                }\n\n                debug!(\"> Picking the slow path due to same entry as old\");\n                new_entry = Entry::rebuild(\n                    device,\n                    quirks_mode,\n                    collection,\n                    guard,\n                    old_entry,\n                    difference,\n                )?;\n                e.insert(new_entry.clone());\n            },\n        }\n\n        Ok(Some(new_entry))\n    }\n\n    /// Returns all the cascade datas that are not being used (that is, that are\n    /// held alive just by this cache).\n    ///\n    /// We return them instead of dropping in place because some of them may\n    /// keep alive some other documents (like the SVG documents kept alive by\n    /// URL references), and thus we don't want to drop them while locking the\n    /// cache to not deadlock.\n    fn take_unused(&mut self) -> SmallVec<[Arc<Entry>; 3]> {\n        let mut unused = SmallVec::new();\n        self.entries.retain(|_key, value| {\n            // is_unique() returns false for static references, but we never\n            // have static references to UserAgentCascadeDatas.  If we did, it\n            // may not make sense to put them in the cache in the first place.\n            if !value.is_unique() {\n                return true;\n            }\n            unused.push(value.clone());\n            false\n        });\n        unused\n    }\n\n    fn take_all(&mut self) -> FxHashMap<CascadeDataCacheKey, Arc<Entry>> {\n        mem::take(&mut self.entries)\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        sizes.mOther += self.entries.shallow_size_of(ops);\n        for (_key, arc) in self.entries.iter() {\n            // These are primary Arc references that can be measured\n            // unconditionally.\n            sizes.mOther += arc.unconditional_shallow_size_of(ops);\n            arc.add_size_of(ops, sizes);\n        }\n    }\n}\n\n/// Measure heap usage of UA_CASCADE_DATA_CACHE.\n#[cfg(feature = \"gecko\")]\npub fn add_size_of_ua_cache(ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n    UA_CASCADE_DATA_CACHE\n        .lock()\n        .unwrap()\n        .add_size_of(ops, sizes);\n}\n\n/// A cache of computed user-agent data, to be shared across documents.\nstatic UA_CASCADE_DATA_CACHE: LazyLock<Mutex<UserAgentCascadeDataCache>> =\n    LazyLock::new(|| Mutex::new(UserAgentCascadeDataCache::new()));\n\nimpl CascadeDataCacheEntry for UserAgentCascadeData {\n    fn rebuild<S>(\n        device: &Device,\n        quirks_mode: QuirksMode,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        old: &Self,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<Arc<Self>, AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        // TODO: Maybe we should support incremental rebuilds, though they seem uncommon and\n        // rebuild() doesn't deal with precomputed_pseudo_element_decls for now so...\n        let mut new_data = servo_arc::UniqueArc::new(Self {\n            cascade_data: CascadeData::new(),\n            precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),\n        });\n\n        for (index, sheet) in collection.sheets().enumerate() {\n            let new_data = &mut *new_data;\n            new_data.cascade_data.add_stylesheet(\n                device,\n                quirks_mode,\n                sheet,\n                index,\n                guard,\n                SheetRebuildKind::Full,\n                Some(&mut new_data.precomputed_pseudo_element_decls),\n                None,\n            )?;\n        }\n\n        new_data.cascade_data.did_finish_rebuild();\n        difference.update(\n            &old.cascade_data.extra_data.position_try_rules,\n            &new_data.cascade_data.extra_data.position_try_rules,\n        );\n\n        Ok(new_data.shareable())\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        self.cascade_data.add_size_of(ops, sizes);\n        sizes.mPrecomputedPseudos += self.precomputed_pseudo_element_decls.size_of(ops);\n    }\n}\n\ntype UserAgentCascadeDataCache = CascadeDataCache<UserAgentCascadeData>;\n\ntype PrecomputedPseudoElementDeclarations = PerPseudoElementMap<Vec<ApplicableDeclarationBlock>>;\n\n#[derive(Default)]\nstruct UserAgentCascadeData {\n    cascade_data: CascadeData,\n\n    /// Applicable declarations for a given non-eagerly cascaded pseudo-element.\n    ///\n    /// These are eagerly computed once, and then used to resolve the new\n    /// computed values on the fly on layout.\n    ///\n    /// These are only filled from UA stylesheets.\n    precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations,\n}\n\n/// The empty UA cascade data for un-filled stylists.\nstatic EMPTY_UA_CASCADE_DATA: LazyLock<Arc<UserAgentCascadeData>> = LazyLock::new(|| {\n    let arc = Arc::new(UserAgentCascadeData::default());\n    arc.mark_as_intentionally_leaked();\n    arc\n});\n\n/// All the computed information for all the stylesheets that apply to the\n/// document.\n#[derive(MallocSizeOf)]\npub struct DocumentCascadeData {\n    #[ignore_malloc_size_of = \"Arc, owned by UserAgentCascadeDataCache or empty\"]\n    user_agent: Arc<UserAgentCascadeData>,\n    user: CascadeData,\n    author: CascadeData,\n    per_origin: PerOrigin<()>,\n}\n\nimpl Default for DocumentCascadeData {\n    fn default() -> Self {\n        Self {\n            user_agent: EMPTY_UA_CASCADE_DATA.clone(),\n            user: Default::default(),\n            author: Default::default(),\n            per_origin: Default::default(),\n        }\n    }\n}\n\n/// An iterator over the cascade data of a given document.\npub struct DocumentCascadeDataIter<'a> {\n    iter: PerOriginIter<'a, ()>,\n    cascade_data: &'a DocumentCascadeData,\n}\n\nimpl<'a> Iterator for DocumentCascadeDataIter<'a> {\n    type Item = (&'a CascadeData, Origin);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let (_, origin) = self.iter.next()?;\n        Some((self.cascade_data.borrow_for_origin(origin), origin))\n    }\n}\n\nimpl DocumentCascadeData {\n    /// Borrows the cascade data for a given origin.\n    #[inline]\n    pub fn borrow_for_origin(&self, origin: Origin) -> &CascadeData {\n        match origin {\n            Origin::UserAgent => &self.user_agent.cascade_data,\n            Origin::Author => &self.author,\n            Origin::User => &self.user,\n        }\n    }\n\n    fn iter_origins(&self) -> DocumentCascadeDataIter<'_> {\n        DocumentCascadeDataIter {\n            iter: self.per_origin.iter_origins(),\n            cascade_data: self,\n        }\n    }\n\n    fn iter_origins_rev(&self) -> DocumentCascadeDataIter<'_> {\n        DocumentCascadeDataIter {\n            iter: self.per_origin.iter_origins_rev(),\n            cascade_data: self,\n        }\n    }\n\n    fn custom_media_for_sheet(\n        &self,\n        s: &StylistSheet,\n        guard: &SharedRwLockReadGuard,\n    ) -> &CustomMediaMap {\n        let origin = s.contents(guard).origin;\n        &self.borrow_for_origin(origin).custom_media\n    }\n\n    /// Rebuild the cascade data for the given document stylesheets, and\n    /// optionally with a set of user agent stylesheets.  Returns Err(..)\n    /// to signify OOM.\n    fn rebuild<'a, S>(\n        &mut self,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        mut flusher: DocumentStylesheetFlusher<'a, S>,\n        guards: &StylesheetGuards,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<(), AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        // First do UA sheets.\n        {\n            let origin_flusher = flusher.flush_origin(Origin::UserAgent);\n            // Dirty check is just a minor optimization (no need to grab the\n            // lock if nothing has changed).\n            if origin_flusher.dirty() {\n                let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();\n                let new_data = ua_cache.lookup(\n                    device,\n                    quirks_mode,\n                    origin_flusher,\n                    guards.ua_or_user,\n                    &self.user_agent,\n                    difference,\n                )?;\n                if let Some(new_data) = new_data {\n                    self.user_agent = new_data;\n                }\n                let _unused_entries = ua_cache.take_unused();\n                // See the comments in take_unused() as for why the following line.\n                std::mem::drop(ua_cache);\n            }\n        }\n\n        // Now do the user sheets.\n        self.user.rebuild(\n            device,\n            quirks_mode,\n            flusher.flush_origin(Origin::User),\n            guards.ua_or_user,\n            difference,\n        )?;\n\n        // And now the author sheets.\n        self.author.rebuild(\n            device,\n            quirks_mode,\n            flusher.flush_origin(Origin::Author),\n            guards.author,\n            difference,\n        )?;\n\n        Ok(())\n    }\n\n    /// Measures heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        self.user.add_size_of(ops, sizes);\n        self.author.add_size_of(ops, sizes);\n    }\n}\n\n/// Whether author styles are enabled.\n///\n/// This is used to support Gecko.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]\npub enum AuthorStylesEnabled {\n    Yes,\n    No,\n}\n\n/// A wrapper over a DocumentStylesheetSet that can be `Sync`, since it's only\n/// used and exposed via mutable methods in the `Stylist`.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[derive(Deref, DerefMut)]\nstruct StylistStylesheetSet(DocumentStylesheetSet<StylistSheet>);\n// Read above to see why this is fine.\nunsafe impl Sync for StylistStylesheetSet {}\n\nimpl StylistStylesheetSet {\n    fn new() -> Self {\n        StylistStylesheetSet(DocumentStylesheetSet::new())\n    }\n}\n\n/// This structure holds all the selectors and device characteristics\n/// for a given document. The selectors are converted into `Rule`s\n/// and sorted into `SelectorMap`s keyed off stylesheet origin and\n/// pseudo-element (see `CascadeData`).\n///\n/// This structure is effectively created once per pipeline, in the\n/// LayoutThread corresponding to that pipeline.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct Stylist {\n    /// Device that the stylist is currently evaluating against.\n    ///\n    /// This field deserves a bigger comment due to the different use that Gecko\n    /// and Servo give to it (that we should eventually unify).\n    ///\n    /// With Gecko, the device is never changed. Gecko manually tracks whether\n    /// the device data should be reconstructed, and \"resets\" the state of the\n    /// device.\n    ///\n    /// On Servo, on the other hand, the device is a really cheap representation\n    /// that is recreated each time some constraint changes and calling\n    /// `set_device`.\n    device: Device,\n\n    /// The list of stylesheets.\n    stylesheets: StylistStylesheetSet,\n\n    /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM).\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"XXX: how to handle this?\")]\n    author_data_cache: CascadeDataCache<CascadeData>,\n\n    /// If true, the quirks-mode stylesheet is applied.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"defined in selectors\")]\n    quirks_mode: QuirksMode,\n\n    /// Selector maps for all of the style sheets in the stylist, after\n    /// evalutaing media rules against the current device, split out per\n    /// cascade level.\n    cascade_data: DocumentCascadeData,\n\n    /// Whether author styles are enabled.\n    author_styles_enabled: AuthorStylesEnabled,\n\n    /// The rule tree, that stores the results of selector matching.\n    rule_tree: RuleTree,\n\n    /// The set of registered custom properties from script.\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#dom-window-registeredpropertyset-slot>\n    script_custom_properties: CustomPropertyScriptRegistry,\n\n    /// Initial values for registered custom properties.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n    initial_values_for_custom_properties: ComputedCustomProperties,\n\n    /// Flags set from computing registered custom property initial values.\n    initial_values_for_custom_properties_flags: ComputedValueFlags,\n\n    /// The total number of times the stylist has been rebuilt.\n    num_rebuilds: usize,\n}\n\n/// What cascade levels to include when styling elements.\n#[derive(Clone, Copy, PartialEq)]\npub enum RuleInclusion {\n    /// Include rules for style sheets at all cascade levels.  This is the\n    /// normal rule inclusion mode.\n    All,\n    /// Only include rules from UA and user level sheets.  Used to implement\n    /// `getDefaultComputedStyle`.\n    DefaultOnly,\n}\n\n#[cfg(feature = \"gecko\")]\nimpl From<StyleRuleInclusion> for RuleInclusion {\n    fn from(value: StyleRuleInclusion) -> Self {\n        match value {\n            StyleRuleInclusion::All => RuleInclusion::All,\n            StyleRuleInclusion::DefaultOnly => RuleInclusion::DefaultOnly,\n        }\n    }\n}\n\n/// `:scope` selector, depending on the use case, can match a shadow host.\n/// If used outside of `@scope`, it cannot possibly match the host.\n/// Even when inside of `@scope`, it's conditional if the selector will\n/// match the shadow host.\n#[derive(Clone, Copy, Eq, PartialEq)]\nenum ScopeMatchesShadowHost {\n    NotApplicable,\n    No,\n    Yes,\n}\n\nimpl Default for ScopeMatchesShadowHost {\n    fn default() -> Self {\n        Self::NotApplicable\n    }\n}\n\nimpl ScopeMatchesShadowHost {\n    fn nest_for_scope(&mut self, matches_shadow_host: bool) {\n        match *self {\n            Self::NotApplicable => {\n                // We're at the outermost `@scope`.\n                *self = if matches_shadow_host {\n                    Self::Yes\n                } else {\n                    Self::No\n                };\n            },\n            Self::Yes if !matches_shadow_host => {\n                // Inner `@scope` will not be able to match the shadow host.\n                *self = Self::No;\n            },\n            _ => (),\n        }\n    }\n}\n\n/// Nested declarations have effectively two behaviors:\n///  * Inside style rules (where they behave as the containing selector).\n///  * Inside @scope (where they behave as :where(:scope)).\n/// It is a bit unfortunate ideally we wouldn't need this, because scope also pushes to the\n/// ancestor_selector_lists, but the behavior isn't quite the same as wrapping in `&`, see\n/// https://github.com/w3c/csswg-drafts/issues/10431\n#[derive(Copy, Clone)]\nenum NestedDeclarationsContext {\n    Style,\n    Scope,\n}\n\n/// A struct containing state related to scope rules\nstruct ContainingScopeRuleState {\n    id: ScopeConditionId,\n    inner_dependencies: Vec<Dependency>,\n    matches_shadow_host: ScopeMatchesShadowHost,\n}\n\nimpl Default for ContainingScopeRuleState {\n    fn default() -> Self {\n        Self {\n            id: ScopeConditionId::none(),\n            inner_dependencies: Vec::new(),\n            matches_shadow_host: Default::default(),\n        }\n    }\n}\n\nimpl ContainingScopeRuleState {\n    fn save(&self) -> SavedContainingScopeRuleState {\n        SavedContainingScopeRuleState {\n            id: self.id,\n            matches_shadow_host: self.matches_shadow_host,\n            inner_dependencies_len: self.inner_dependencies.len(),\n        }\n    }\n\n    fn restore(\n        &mut self,\n        saved: &SavedContainingScopeRuleState,\n    ) -> Option<(Vec<Dependency>, ScopeConditionId)> {\n        debug_assert!(self.inner_dependencies.len() >= saved.inner_dependencies_len);\n\n        if self.id == saved.id {\n            return None;\n        }\n\n        let scope_id = self.id;\n        let inner_deps = self\n            .inner_dependencies\n            .drain(saved.inner_dependencies_len..)\n            .collect();\n\n        self.id = saved.id;\n        self.matches_shadow_host = saved.matches_shadow_host;\n\n        Some((inner_deps, scope_id))\n    }\n}\n\nstruct SavedContainingScopeRuleState {\n    id: ScopeConditionId,\n    matches_shadow_host: ScopeMatchesShadowHost,\n    inner_dependencies_len: usize,\n}\n\n/// A struct containing state from ancestor rules like @layer / @import /\n/// @container / nesting / @scope.\nstruct ContainingRuleState {\n    layer_name: LayerName,\n    layer_id: LayerId,\n    container_condition_id: ContainerConditionId,\n    cascade_flags: RuleCascadeFlags,\n    containing_scope_rule_state: ContainingScopeRuleState,\n    ancestor_selector_lists: SmallVec<[SelectorList<SelectorImpl>; 2]>,\n    nested_declarations_context: NestedDeclarationsContext,\n}\n\nimpl Default for ContainingRuleState {\n    fn default() -> Self {\n        Self {\n            layer_name: LayerName::new_empty(),\n            layer_id: LayerId::root(),\n            container_condition_id: ContainerConditionId::none(),\n            cascade_flags: RuleCascadeFlags::empty(),\n            ancestor_selector_lists: Default::default(),\n            containing_scope_rule_state: Default::default(),\n            nested_declarations_context: NestedDeclarationsContext::Style,\n        }\n    }\n}\n\nstruct SavedContainingRuleState {\n    ancestor_selector_lists_len: usize,\n    layer_name_len: usize,\n    layer_id: LayerId,\n    container_condition_id: ContainerConditionId,\n    cascade_flags: RuleCascadeFlags,\n    saved_containing_scope_rule_state: SavedContainingScopeRuleState,\n    nested_declarations_context: NestedDeclarationsContext,\n}\n\nimpl ContainingRuleState {\n    fn save(&self) -> SavedContainingRuleState {\n        SavedContainingRuleState {\n            ancestor_selector_lists_len: self.ancestor_selector_lists.len(),\n            layer_name_len: self.layer_name.0.len(),\n            layer_id: self.layer_id,\n            container_condition_id: self.container_condition_id,\n            cascade_flags: self.cascade_flags,\n            saved_containing_scope_rule_state: self.containing_scope_rule_state.save(),\n            nested_declarations_context: self.nested_declarations_context,\n        }\n    }\n\n    fn restore(\n        &mut self,\n        saved: &SavedContainingRuleState,\n    ) -> Option<(Vec<Dependency>, ScopeConditionId)> {\n        debug_assert!(self.layer_name.0.len() >= saved.layer_name_len);\n        debug_assert!(self.ancestor_selector_lists.len() >= saved.ancestor_selector_lists_len);\n\n        self.ancestor_selector_lists\n            .truncate(saved.ancestor_selector_lists_len);\n        self.layer_name.0.truncate(saved.layer_name_len);\n        self.layer_id = saved.layer_id;\n        self.container_condition_id = saved.container_condition_id;\n        self.cascade_flags = saved.cascade_flags;\n        self.nested_declarations_context = saved.nested_declarations_context;\n\n        self.containing_scope_rule_state\n            .restore(&saved.saved_containing_scope_rule_state)\n    }\n\n    fn scope_is_effective(&self) -> bool {\n        self.containing_scope_rule_state.id != ScopeConditionId::none()\n    }\n\n    fn cascade_flags(&self) -> RuleCascadeFlags {\n        self.cascade_flags\n    }\n}\n\ntype ReplacedSelectors = SmallVec<[Selector<SelectorImpl>; 4]>;\n\nimpl Stylist {\n    /// Construct a new `Stylist`, using given `Device` and `QuirksMode`.\n    /// If more members are added here, think about whether they should\n    /// be reset in clear().\n    #[inline]\n    pub fn new(device: Device, quirks_mode: QuirksMode) -> Self {\n        Self {\n            device,\n            quirks_mode,\n            stylesheets: StylistStylesheetSet::new(),\n            author_data_cache: CascadeDataCache::new(),\n            cascade_data: Default::default(),\n            author_styles_enabled: AuthorStylesEnabled::Yes,\n            rule_tree: RuleTree::new(),\n            script_custom_properties: Default::default(),\n            initial_values_for_custom_properties: Default::default(),\n            initial_values_for_custom_properties_flags: Default::default(),\n            num_rebuilds: 0,\n        }\n    }\n\n    /// Returns the document cascade data.\n    #[inline]\n    pub fn cascade_data(&self) -> &DocumentCascadeData {\n        &self.cascade_data\n    }\n\n    /// Returns whether author styles are enabled or not.\n    #[inline]\n    pub fn author_styles_enabled(&self) -> AuthorStylesEnabled {\n        self.author_styles_enabled\n    }\n\n    /// Iterate through all the cascade datas from the document.\n    #[inline]\n    pub fn iter_origins(&self) -> DocumentCascadeDataIter<'_> {\n        self.cascade_data.iter_origins()\n    }\n\n    /// Does what the name says, to prevent author_data_cache to grow without\n    /// bound.\n    pub fn remove_unique_author_data_cache_entries(&mut self) {\n        self.author_data_cache.take_unused();\n    }\n\n    /// Returns the custom property registration for this property's name.\n    /// https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration\n    pub fn get_custom_property_registration(&self, name: &Atom) -> &PropertyDescriptors {\n        if let Some(registration) = self.custom_property_script_registry().get(name) {\n            return &registration.descriptors;\n        }\n        for (data, _) in self.iter_origins() {\n            if let Some(registration) = data.custom_property_registrations.get(name) {\n                return &registration.descriptors;\n            }\n        }\n        PropertyDescriptors::unregistered()\n    }\n\n    /// Returns custom properties with their registered initial values.\n    pub fn get_custom_property_initial_values(&self) -> &ComputedCustomProperties {\n        &self.initial_values_for_custom_properties\n    }\n\n    /// Returns flags set from computing the registered custom property initial values.\n    pub fn get_custom_property_initial_values_flags(&self) -> ComputedValueFlags {\n        self.initial_values_for_custom_properties_flags\n    }\n\n    /// Rebuild custom properties with their registered initial values.\n    /// https://drafts.css-houdini.org/css-properties-values-api-1/#determining-registration\n    pub fn rebuild_initial_values_for_custom_properties(&mut self) {\n        let mut initial_values = ComputedCustomProperties::default();\n        let initial_values_flags;\n        {\n            let mut seen_names = PrecomputedHashSet::default();\n            let mut rule_cache_conditions = RuleCacheConditions::default();\n            let context = computed::Context::new_for_initial_at_property_value(\n                self,\n                &mut rule_cache_conditions,\n            );\n\n            for (k, v) in self.custom_property_script_registry().properties().iter() {\n                seen_names.insert(k.clone());\n                let Ok(value) = v.compute_initial_value(&context) else {\n                    continue;\n                };\n                let map = if v.descriptors.inherits() {\n                    &mut initial_values.inherited\n                } else {\n                    &mut initial_values.non_inherited\n                };\n                map.insert(k, value);\n            }\n            for (data, _) in self.iter_origins() {\n                for (k, v) in data.custom_property_registrations.iter() {\n                    if seen_names.insert(k.clone()) {\n                        let last_value = &v.last().unwrap().0;\n                        let Ok(value) = last_value.compute_initial_value(&context) else {\n                            continue;\n                        };\n                        let map = if last_value.descriptors.inherits() {\n                            &mut initial_values.inherited\n                        } else {\n                            &mut initial_values.non_inherited\n                        };\n                        map.insert(k, value);\n                    }\n                }\n            }\n            initial_values_flags = context.builder.flags();\n        }\n        self.initial_values_for_custom_properties_flags = initial_values_flags;\n        self.initial_values_for_custom_properties = initial_values;\n    }\n\n    /// Rebuilds (if needed) the CascadeData given a sheet collection.\n    pub fn rebuild_author_data<S>(\n        &mut self,\n        old_data: &CascadeData,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<Option<Arc<CascadeData>>, AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        self.author_data_cache.lookup(\n            &self.device,\n            self.quirks_mode,\n            collection,\n            guard,\n            old_data,\n            difference,\n        )\n    }\n\n    /// Iterate over the extra data in origin order.\n    #[inline]\n    pub fn iter_extra_data_origins(&self) -> ExtraStyleDataIterator<'_> {\n        ExtraStyleDataIterator(self.cascade_data.iter_origins())\n    }\n\n    /// Iterate over the extra data in reverse origin order.\n    #[inline]\n    pub fn iter_extra_data_origins_rev(&self) -> ExtraStyleDataIterator<'_> {\n        ExtraStyleDataIterator(self.cascade_data.iter_origins_rev())\n    }\n\n    /// Returns the number of selectors.\n    pub fn num_selectors(&self) -> usize {\n        self.cascade_data\n            .iter_origins()\n            .map(|(d, _)| d.num_selectors)\n            .sum()\n    }\n\n    /// Returns the number of declarations.\n    pub fn num_declarations(&self) -> usize {\n        self.cascade_data\n            .iter_origins()\n            .map(|(d, _)| d.num_declarations)\n            .sum()\n    }\n\n    /// Returns the number of times the stylist has been rebuilt.\n    pub fn num_rebuilds(&self) -> usize {\n        self.num_rebuilds\n    }\n\n    /// Returns the number of revalidation_selectors.\n    pub fn num_revalidation_selectors(&self) -> usize {\n        self.cascade_data\n            .iter_origins()\n            .map(|(data, _)| data.selectors_for_cache_revalidation.len())\n            .sum()\n    }\n\n    /// Returns the number of entries in invalidation maps.\n    pub fn num_invalidations(&self) -> usize {\n        self.cascade_data\n            .iter_origins()\n            .map(|(data, _)| {\n                data.invalidation_map.len() + data.relative_selector_invalidation_map.len()\n            })\n            .sum()\n    }\n\n    /// Returns whether the given DocumentState bit is relied upon by a selector\n    /// of some rule.\n    pub fn has_document_state_dependency(&self, state: DocumentState) -> bool {\n        self.cascade_data\n            .iter_origins()\n            .any(|(d, _)| d.document_state_dependencies.intersects(state))\n    }\n\n    /// Flush the list of stylesheets if they changed, ensuring the stylist is\n    /// up-to-date.\n    pub fn flush(&mut self, guards: &StylesheetGuards) -> StylesheetInvalidationSet {\n        if !self.stylesheets.has_changed() {\n            return Default::default();\n        }\n\n        self.num_rebuilds += 1;\n\n        let (flusher, mut invalidations) = self.stylesheets.flush();\n\n        self.cascade_data\n            .rebuild(\n                &self.device,\n                self.quirks_mode,\n                flusher,\n                guards,\n                &mut invalidations.cascade_data_difference,\n            )\n            .unwrap_or_else(|_| {\n                warn!(\"OOM in Stylist::flush\");\n            });\n\n        self.rebuild_initial_values_for_custom_properties();\n        invalidations\n    }\n\n    /// Marks a given stylesheet origin as dirty, due to, for example, changes\n    /// in the declarations that affect a given rule.\n    ///\n    /// FIXME(emilio): Eventually it'd be nice for this to become more\n    /// fine-grained.\n    pub fn force_stylesheet_origins_dirty(&mut self, origins: OriginSet) {\n        self.stylesheets.force_dirty(origins)\n    }\n\n    /// Sets whether author style is enabled or not.\n    pub fn set_author_styles_enabled(&mut self, enabled: AuthorStylesEnabled) {\n        self.author_styles_enabled = enabled;\n    }\n\n    /// Returns whether we've recorded any stylesheet change so far.\n    pub fn stylesheets_have_changed(&self) -> bool {\n        self.stylesheets.has_changed()\n    }\n\n    /// Insert a given stylesheet before another stylesheet in the document.\n    pub fn insert_stylesheet_before(\n        &mut self,\n        sheet: StylistSheet,\n        before_sheet: StylistSheet,\n        guard: &SharedRwLockReadGuard,\n    ) {\n        let custom_media = self.cascade_data.custom_media_for_sheet(&sheet, guard);\n        self.stylesheets.insert_stylesheet_before(\n            Some(&self.device),\n            custom_media,\n            sheet,\n            before_sheet,\n            guard,\n        )\n    }\n\n    /// Appends a new stylesheet to the current set.\n    pub fn append_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {\n        let custom_media = self.cascade_data.custom_media_for_sheet(&sheet, guard);\n        self.stylesheets\n            .append_stylesheet(Some(&self.device), custom_media, sheet, guard)\n    }\n\n    /// Remove a given stylesheet to the current set.\n    pub fn remove_stylesheet(&mut self, sheet: StylistSheet, guard: &SharedRwLockReadGuard) {\n        let custom_media = self.cascade_data.custom_media_for_sheet(&sheet, guard);\n        self.stylesheets\n            .remove_stylesheet(Some(&self.device), custom_media, sheet, guard)\n    }\n\n    /// Notify of a change of a given rule.\n    pub fn rule_changed(\n        &mut self,\n        sheet: &StylistSheet,\n        rule: &CssRule,\n        guard: &SharedRwLockReadGuard,\n        change_kind: RuleChangeKind,\n        ancestors: &[CssRuleRef],\n    ) {\n        let custom_media = self.cascade_data.custom_media_for_sheet(&sheet, guard);\n        self.stylesheets.rule_changed(\n            Some(&self.device),\n            custom_media,\n            sheet,\n            rule,\n            guard,\n            change_kind,\n            ancestors,\n        )\n    }\n\n    /// Get the total stylesheet count for a given origin.\n    #[inline]\n    pub fn sheet_count(&self, origin: Origin) -> usize {\n        self.stylesheets.sheet_count(origin)\n    }\n\n    /// Get the index-th stylesheet for a given origin.\n    #[inline]\n    pub fn sheet_at(&self, origin: Origin, index: usize) -> Option<&StylistSheet> {\n        self.stylesheets.get(origin, index)\n    }\n\n    /// Returns whether for any of the applicable style rule data a given\n    /// condition is true.\n    pub fn any_applicable_rule_data<E, F>(&self, element: E, mut f: F) -> bool\n    where\n        E: TElement,\n        F: FnMut(&CascadeData) -> bool,\n    {\n        if f(&self.cascade_data.user_agent.cascade_data) {\n            return true;\n        }\n\n        let mut maybe = false;\n\n        let doc_author_rules_apply =\n            element.each_applicable_non_document_style_rule_data(|data, _| {\n                maybe = maybe || f(&*data);\n            });\n\n        if maybe || f(&self.cascade_data.user) {\n            return true;\n        }\n\n        doc_author_rules_apply && f(&self.cascade_data.author)\n    }\n\n    /// Execute callback for all applicable style rule data.\n    pub fn for_each_cascade_data_with_scope<'a, E, F>(&'a self, element: E, mut f: F)\n    where\n        E: TElement + 'a,\n        F: FnMut(&'a CascadeData, Option<E>),\n    {\n        f(&self.cascade_data.user_agent.cascade_data, None);\n        element.each_applicable_non_document_style_rule_data(|data, scope| {\n            f(data, Some(scope));\n        });\n        f(&self.cascade_data.user, None);\n        f(&self.cascade_data.author, None);\n    }\n\n    /// Computes the style for a given \"precomputed\" pseudo-element, taking the\n    /// universal rules and applying them.\n    pub fn precomputed_values_for_pseudo<E>(\n        &self,\n        guards: &StylesheetGuards,\n        pseudo: &PseudoElement,\n        parent: Option<&ComputedValues>,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        debug_assert!(pseudo.is_precomputed());\n\n        let rule_node = self.rule_node_for_precomputed_pseudo(guards, pseudo, vec![]);\n\n        self.precomputed_values_for_pseudo_with_rule_node::<E>(guards, pseudo, parent, rule_node)\n    }\n\n    /// Computes the style for a given \"precomputed\" pseudo-element with\n    /// given rule node.\n    ///\n    /// TODO(emilio): The type parameter could go away with a void type\n    /// implementing TElement.\n    pub fn precomputed_values_for_pseudo_with_rule_node<E>(\n        &self,\n        guards: &StylesheetGuards,\n        pseudo: &PseudoElement,\n        parent: Option<&ComputedValues>,\n        rules: StrongRuleNode,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        self.compute_pseudo_element_style_with_inputs::<E>(\n            CascadeInputs {\n                rules: Some(rules),\n                visited_rules: None,\n                flags: Default::default(),\n                included_cascade_flags: RuleCascadeFlags::empty(),\n            },\n            pseudo,\n            guards,\n            parent,\n            /* element */ None,\n        )\n    }\n\n    /// Returns the rule node for a given precomputed pseudo-element.\n    ///\n    /// If we want to include extra declarations to this precomputed\n    /// pseudo-element, we can provide a vector of ApplicableDeclarationBlocks\n    /// to extra_declarations. This is useful for @page rules.\n    pub fn rule_node_for_precomputed_pseudo(\n        &self,\n        guards: &StylesheetGuards,\n        pseudo: &PseudoElement,\n        mut extra_declarations: Vec<ApplicableDeclarationBlock>,\n    ) -> StrongRuleNode {\n        let mut declarations_with_extra;\n        let declarations = match self\n            .cascade_data\n            .user_agent\n            .precomputed_pseudo_element_decls\n            .get(pseudo)\n        {\n            Some(declarations) => {\n                if !extra_declarations.is_empty() {\n                    declarations_with_extra = declarations.clone();\n                    declarations_with_extra.append(&mut extra_declarations);\n                    &*declarations_with_extra\n                } else {\n                    &**declarations\n                }\n            },\n            None => &[],\n        };\n\n        self.rule_tree.insert_ordered_rules_with_important(\n            declarations.into_iter().map(|a| a.clone().for_rule_tree()),\n            guards,\n        )\n    }\n\n    /// Returns the style for an anonymous box of the given type.\n    ///\n    /// TODO(emilio): The type parameter could go away with a void type\n    /// implementing TElement.\n    #[cfg(feature = \"servo\")]\n    pub fn style_for_anonymous<E>(\n        &self,\n        guards: &StylesheetGuards,\n        pseudo: &PseudoElement,\n        parent_style: &ComputedValues,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        self.precomputed_values_for_pseudo::<E>(guards, &pseudo, Some(parent_style))\n    }\n\n    /// Computes a pseudo-element style lazily during layout.\n    ///\n    /// This can only be done for a certain set of pseudo-elements, like\n    /// :selection.\n    ///\n    /// Check the documentation on lazy pseudo-elements in\n    /// docs/components/style.md\n    pub fn lazily_compute_pseudo_element_style<E>(\n        &self,\n        guards: &StylesheetGuards,\n        element: E,\n        pseudo: &PseudoElement,\n        rule_inclusion: RuleInclusion,\n        originating_element_style: &ComputedValues,\n        is_probe: bool,\n        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,\n    ) -> Option<Arc<ComputedValues>>\n    where\n        E: TElement,\n    {\n        let cascade_inputs = self.lazy_pseudo_rules(\n            guards,\n            element,\n            originating_element_style,\n            pseudo,\n            is_probe,\n            rule_inclusion,\n            matching_fn,\n        )?;\n\n        Some(self.compute_pseudo_element_style_with_inputs(\n            cascade_inputs,\n            pseudo,\n            guards,\n            Some(originating_element_style),\n            Some(element),\n        ))\n    }\n\n    /// Computes a pseudo-element style lazily using the given CascadeInputs.\n    /// This can be used for truly lazy pseudo-elements or to avoid redoing\n    /// selector matching for eager pseudo-elements when we need to recompute\n    /// their style with a new parent style.\n    pub fn compute_pseudo_element_style_with_inputs<E>(\n        &self,\n        inputs: CascadeInputs,\n        pseudo: &PseudoElement,\n        guards: &StylesheetGuards,\n        parent_style: Option<&ComputedValues>,\n        element: Option<E>,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        // FIXME(emilio): The lack of layout_parent_style here could be\n        // worrying, but we're probably dropping the display fixup for\n        // pseudos other than before and after, so it's probably ok.\n        //\n        // (Though the flags don't indicate so!)\n        //\n        // It'd be fine to assert that this isn't called with a parent style\n        // where display contents is in effect, but in practice this is hard to\n        // do for stuff like :-moz-fieldset-content with a\n        // <fieldset style=\"display: contents\">. That is, the computed value of\n        // display for the fieldset is \"contents\", even though it's not the used\n        // value, so we don't need to adjust in a different way anyway.\n        self.cascade_style_and_visited(\n            element,\n            Some(pseudo),\n            &inputs,\n            guards,\n            parent_style,\n            parent_style,\n            FirstLineReparenting::No,\n            &PositionTryFallbacksTryTactic::default(),\n            /* rule_cache = */ None,\n            &mut RuleCacheConditions::default(),\n        )\n    }\n\n    /// Computes a fallback style lazily given the current and parent styles, and name.\n    #[cfg(feature = \"gecko\")]\n    pub fn resolve_position_try<E>(\n        &self,\n        style: &ComputedValues,\n        guards: &StylesheetGuards,\n        element: E,\n        fallback_item: &PositionTryFallbacksItem,\n    ) -> Option<Arc<ComputedValues>>\n    where\n        E: TElement,\n    {\n        let name_and_try_tactic = match *fallback_item {\n            PositionTryFallbacksItem::PositionArea(area) => {\n                // We don't bother passing the parent_style argument here since\n                // we probably don't need it. If we do, we could wrap this up in\n                // a style_resolver::with_default_parent_styles call, as below.\n                let mut builder =\n                    StyleBuilder::for_derived_style(&self.device, Some(self), style, None);\n                builder.rules = style.rules.clone();\n                builder.mutate_position().set_position_area(area);\n                return Some(builder.build());\n            },\n            PositionTryFallbacksItem::IdentAndOrTactic(ref name_and_try_tactic) => {\n                name_and_try_tactic\n            },\n        };\n\n        let fallback_rule = if !name_and_try_tactic.ident.is_empty() {\n            Some(self.lookup_position_try(&name_and_try_tactic.ident.0, element)?)\n        } else {\n            None\n        };\n        let fallback_block = fallback_rule\n            .as_ref()\n            .map(|r| &r.read_with(guards.author).block);\n        let pseudo = style\n            .pseudo()\n            .or_else(|| element.implemented_pseudo_element());\n        let inputs = {\n            let mut inputs = CascadeInputs::new_from_style(style);\n            // @position-try doesn't care about any :visited-dependent property.\n            inputs.visited_rules = None;\n            let rules = inputs.rules.as_ref().unwrap_or(self.rule_tree.root());\n            let mut important_rules_changed = false;\n            if let Some(fallback_block) = fallback_block {\n                let new_rules = self.rule_tree.update_rule_at_level(\n                    CascadeLevel::new(CascadeOrigin::PositionFallback),\n                    LayerOrder::root(),\n                    Some(fallback_block.borrow_arc()),\n                    rules,\n                    guards,\n                    &mut important_rules_changed,\n                );\n                if new_rules.is_some() {\n                    inputs.rules = new_rules;\n                } else {\n                    // This will return an identical style to `style`. We could consider optimizing\n                    // this a bit more but for now just perform the cascade, this can only happen with\n                    // the same position-try name repeated multiple times anyways.\n                }\n            }\n            inputs\n        };\n        crate::style_resolver::with_default_parent_styles(\n            element,\n            |parent_style, layout_parent_style| {\n                Some(self.cascade_style_and_visited(\n                    Some(element),\n                    pseudo.as_ref(),\n                    &inputs,\n                    guards,\n                    parent_style,\n                    layout_parent_style,\n                    FirstLineReparenting::No,\n                    &name_and_try_tactic.try_tactic,\n                    /* rule_cache = */ None,\n                    &mut RuleCacheConditions::default(),\n                ))\n            },\n        )\n    }\n\n    /// Computes a style using the given CascadeInputs.  This can be used to\n    /// compute a style any time we know what rules apply and just need to use\n    /// the given parent styles.\n    ///\n    /// parent_style is the style to inherit from for properties affected by\n    /// first-line ancestors.\n    ///\n    /// parent_style_ignoring_first_line is the style to inherit from for\n    /// properties not affected by first-line ancestors.\n    ///\n    /// layout_parent_style is the style used for some property fixups.  It's\n    /// the style of the nearest ancestor with a layout box.\n    pub fn cascade_style_and_visited<E>(\n        &self,\n        element: Option<E>,\n        pseudo: Option<&PseudoElement>,\n        inputs: &CascadeInputs,\n        guards: &StylesheetGuards,\n        parent_style: Option<&ComputedValues>,\n        layout_parent_style: Option<&ComputedValues>,\n        first_line_reparenting: FirstLineReparenting,\n        try_tactic: &PositionTryFallbacksTryTactic,\n        rule_cache: Option<&RuleCache>,\n        rule_cache_conditions: &mut RuleCacheConditions,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        debug_assert!(pseudo.is_some() || element.is_some(), \"Huh?\");\n\n        // We need to compute visited values if we have visited rules or if our\n        // parent has visited values.\n        let visited_rules = match inputs.visited_rules.as_ref() {\n            Some(rules) => Some(rules),\n            None => {\n                if parent_style.and_then(|s| s.visited_style()).is_some() {\n                    Some(inputs.rules.as_ref().unwrap_or(self.rule_tree.root()))\n                } else {\n                    None\n                }\n            },\n        };\n\n        let mut implemented_pseudo = None;\n        // Read the comment on `precomputed_values_for_pseudo` to see why it's\n        // difficult to assert that display: contents nodes never arrive here\n        // (tl;dr: It doesn't apply for replaced elements and such, but the\n        // computed value is still \"contents\").\n        //\n        // FIXME(emilio): We should assert that it holds if pseudo.is_none()!\n        properties::cascade::<E>(\n            &self,\n            pseudo.or_else(|| {\n                implemented_pseudo = element.unwrap().implemented_pseudo_element();\n                implemented_pseudo.as_ref()\n            }),\n            inputs.rules.as_ref().unwrap_or(self.rule_tree.root()),\n            guards,\n            parent_style,\n            layout_parent_style,\n            first_line_reparenting,\n            try_tactic,\n            visited_rules,\n            inputs.flags,\n            inputs.included_cascade_flags,\n            rule_cache,\n            rule_cache_conditions,\n            element,\n        )\n    }\n\n    /// Computes the cascade inputs for a lazily-cascaded pseudo-element.\n    ///\n    /// See the documentation on lazy pseudo-elements in\n    /// docs/components/style.md\n    fn lazy_pseudo_rules<E>(\n        &self,\n        guards: &StylesheetGuards,\n        element: E,\n        originating_element_style: &ComputedValues,\n        pseudo: &PseudoElement,\n        is_probe: bool,\n        rule_inclusion: RuleInclusion,\n        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,\n    ) -> Option<CascadeInputs>\n    where\n        E: TElement,\n    {\n        debug_assert!(pseudo.is_lazy());\n\n        let mut selector_caches = SelectorCaches::default();\n        // No need to bother setting the selector flags when we're computing\n        // default styles.\n        let needs_selector_flags = if rule_inclusion == RuleInclusion::DefaultOnly {\n            NeedsSelectorFlags::No\n        } else {\n            NeedsSelectorFlags::Yes\n        };\n\n        let mut declarations = ApplicableDeclarationList::new();\n        let mut matching_context = MatchingContext::<'_, E::Impl>::new(\n            MatchingMode::ForStatelessPseudoElement,\n            None,\n            &mut selector_caches,\n            self.quirks_mode,\n            needs_selector_flags,\n            MatchingForInvalidation::No,\n        );\n\n        matching_context.pseudo_element_matching_fn = matching_fn;\n        matching_context.extra_data.originating_element_style = Some(originating_element_style);\n\n        self.push_applicable_declarations(\n            element,\n            Some(&pseudo),\n            None,\n            None,\n            /* animation_declarations = */ Default::default(),\n            rule_inclusion,\n            &mut declarations,\n            &mut matching_context,\n        );\n\n        if declarations.is_empty() && is_probe {\n            return None;\n        }\n\n        let rules = self.rule_tree.compute_rule_node(&mut declarations, guards);\n\n        let mut visited_rules = None;\n        if originating_element_style.visited_style().is_some() {\n            let mut declarations = ApplicableDeclarationList::new();\n            let mut selector_caches = SelectorCaches::default();\n\n            let mut matching_context = MatchingContext::<'_, E::Impl>::new_for_visited(\n                MatchingMode::ForStatelessPseudoElement,\n                None,\n                &mut selector_caches,\n                VisitedHandlingMode::RelevantLinkVisited,\n                self.quirks_mode,\n                needs_selector_flags,\n                MatchingForInvalidation::No,\n            );\n            matching_context.pseudo_element_matching_fn = matching_fn;\n            matching_context.extra_data.originating_element_style = Some(originating_element_style);\n\n            self.push_applicable_declarations(\n                element,\n                Some(&pseudo),\n                None,\n                None,\n                /* animation_declarations = */ Default::default(),\n                rule_inclusion,\n                &mut declarations,\n                &mut matching_context,\n            );\n            if !declarations.is_empty() {\n                let rule_node = self.rule_tree.insert_ordered_rules_with_important(\n                    declarations.drain(..).map(|a| a.for_rule_tree()),\n                    guards,\n                );\n                if rule_node != *self.rule_tree.root() {\n                    visited_rules = Some(rule_node);\n                }\n            }\n        }\n\n        Some(CascadeInputs {\n            rules: Some(rules),\n            visited_rules,\n            flags: matching_context.extra_data.cascade_input_flags,\n            included_cascade_flags: RuleCascadeFlags::empty(),\n        })\n    }\n\n    /// Set a given device, which may change the styles that apply to the\n    /// document.\n    ///\n    /// Returns the sheet origins that were actually affected.\n    ///\n    /// This means that we may need to rebuild style data even if the\n    /// stylesheets haven't changed.\n    ///\n    /// Also, the device that arrives here may need to take the viewport rules\n    /// into account.\n    pub fn set_device(&mut self, device: Device, guards: &StylesheetGuards) -> OriginSet {\n        self.device = device;\n        self.media_features_change_changed_style(guards, &self.device)\n    }\n\n    /// Returns whether, given a media feature change, any previously-applicable\n    /// style has become non-applicable, or vice-versa for each origin, using\n    /// `device`.\n    pub fn media_features_change_changed_style(\n        &self,\n        guards: &StylesheetGuards,\n        device: &Device,\n    ) -> OriginSet {\n        debug!(\"Stylist::media_features_change_changed_style {:?}\", device);\n\n        let mut origins = OriginSet::empty();\n        let stylesheets = self.stylesheets.iter();\n\n        for (stylesheet, origin) in stylesheets {\n            if origins.contains(origin.into()) {\n                continue;\n            }\n\n            let guard = guards.for_origin(origin);\n            let origin_cascade_data = self.cascade_data.borrow_for_origin(origin);\n\n            let affected_changed = !origin_cascade_data.media_feature_affected_matches(\n                stylesheet,\n                guard,\n                device,\n                self.quirks_mode,\n            );\n\n            if affected_changed {\n                origins |= origin;\n            }\n        }\n\n        origins\n    }\n\n    /// Returns the Quirks Mode of the document.\n    pub fn quirks_mode(&self) -> QuirksMode {\n        self.quirks_mode\n    }\n\n    /// Sets the quirks mode of the document.\n    pub fn set_quirks_mode(&mut self, quirks_mode: QuirksMode) {\n        if self.quirks_mode == quirks_mode {\n            return;\n        }\n        self.quirks_mode = quirks_mode;\n        self.force_stylesheet_origins_dirty(OriginSet::all());\n    }\n\n    /// Returns the applicable CSS declarations for the given element.\n    pub fn push_applicable_declarations<E>(\n        &self,\n        element: E,\n        pseudo_element: Option<&PseudoElement>,\n        style_attribute: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,\n        smil_override: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,\n        animation_declarations: AnimationDeclarations,\n        rule_inclusion: RuleInclusion,\n        applicable_declarations: &mut ApplicableDeclarationList,\n        context: &mut MatchingContext<E::Impl>,\n    ) where\n        E: TElement,\n    {\n        let mut cur = element;\n        let mut pseudos = SmallVec::new();\n        if let Some(pseudo) = pseudo_element {\n            pseudos.push(pseudo.clone());\n        }\n        while let Some(p) = cur.implemented_pseudo_element() {\n            pseudos.push(p);\n            let Some(parent_pseudo) = cur.pseudo_element_originating_element() else {\n                break;\n            };\n            cur = parent_pseudo;\n        }\n        RuleCollector::new(\n            self,\n            element,\n            pseudos,\n            style_attribute,\n            smil_override,\n            animation_declarations,\n            rule_inclusion,\n            applicable_declarations,\n            context,\n        )\n        .collect_all();\n    }\n\n    /// Given an id, returns whether there might be any rules for that id in any\n    /// of our rule maps.\n    #[inline]\n    pub fn may_have_rules_for_id<E>(&self, id: &WeakAtom, element: E) -> bool\n    where\n        E: TElement,\n    {\n        // If id needs to be compared case-insensitively, the logic below\n        // wouldn't work. Just conservatively assume it may have such rules.\n        match self.quirks_mode().classes_and_ids_case_sensitivity() {\n            CaseSensitivity::AsciiCaseInsensitive => return true,\n            CaseSensitivity::CaseSensitive => {},\n        }\n\n        self.any_applicable_rule_data(element, |data| data.mapped_ids.contains(id))\n    }\n\n    /// Looks up a CascadeData-dependent rule for a given element.\n    ///\n    /// NOTE(emilio): This is a best-effort thing, the right fix is a bit TBD because it involves\n    /// \"recording\" which tree the name came from, see [1][2].\n    ///\n    /// [1]: https://github.com/w3c/csswg-drafts/issues/1995\n    /// [2]: https://bugzil.la/1458189\n    #[inline]\n    fn lookup_element_dependent_at_rule<'a, T, F, E>(\n        &'a self,\n        element: E,\n        find_in: F,\n    ) -> Option<&'a T>\n    where\n        E: TElement + 'a,\n        F: Fn(&'a CascadeData) -> Option<&'a T>,\n    {\n        macro_rules! try_find_in {\n            ($data:expr) => {\n                if let Some(thing) = find_in(&$data) {\n                    return Some(thing);\n                }\n            };\n        }\n\n        let mut result = None;\n        let doc_rules_apply =\n            element.each_applicable_non_document_style_rule_data(|data, _host| {\n                if result.is_none() {\n                    result = find_in(data);\n                }\n            });\n\n        if result.is_some() {\n            return result;\n        }\n\n        if doc_rules_apply {\n            try_find_in!(self.cascade_data.author);\n        }\n        try_find_in!(self.cascade_data.user);\n        try_find_in!(self.cascade_data.user_agent.cascade_data);\n\n        None\n    }\n\n    /// Returns the registered `@keyframes` animation for the specified name.\n    #[inline]\n    pub fn lookup_keyframes<'a, E>(\n        &'a self,\n        name: &Atom,\n        element: E,\n    ) -> Option<&'a KeyframesAnimation>\n    where\n        E: TElement + 'a,\n    {\n        self.lookup_element_dependent_at_rule(element, |data| data.animations.get(name))\n    }\n\n    /// Returns the last @view-transition rule\n    /// <https://drafts.csswg.org/css-view-transitions-2/#resolve-view-transition-rule>\n    #[inline]\n    pub fn last_view_transition_rule(&self) -> Option<&Arc<ViewTransitionRule>> {\n        // Iterate the effective rules sorted by origin and level\n        self.iter_extra_data_origins()\n            .flat_map(|(d, _)| d.view_transitions.iter())\n            .last()\n            .map(|(rule, _)| rule)\n    }\n\n    /// Returns the registered `@position-try-rule` animation for the specified name.\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    fn lookup_position_try<'a, E>(\n        &'a self,\n        name: &Atom,\n        element: E,\n    ) -> Option<&'a Arc<Locked<PositionTryRule>>>\n    where\n        E: TElement + 'a,\n    {\n        self.lookup_element_dependent_at_rule(element, |data| {\n            data.extra_data.position_try_rules.get(name)\n        })\n    }\n\n    /// Computes the match results of a given element against the set of\n    /// revalidation selectors.\n    pub fn match_revalidation_selectors<E>(\n        &self,\n        element: E,\n        bloom: Option<&BloomFilter>,\n        selector_caches: &mut SelectorCaches,\n        needs_selector_flags: NeedsSelectorFlags,\n    ) -> RevalidationResult\n    where\n        E: TElement,\n    {\n        let mut matching_context = MatchingContext::new_for_revalidation(\n            bloom,\n            selector_caches,\n            self.quirks_mode,\n            needs_selector_flags,\n        );\n\n        // Note that, by the time we're revalidating, we're guaranteed that the\n        // candidate and the entry have the same id, classes, and local name.\n        // This means we're guaranteed to get the same rulehash buckets for all\n        // the lookups, which means that the bitvecs are comparable. We verify\n        // this in the caller by asserting that the bitvecs are same-length.\n        let mut result = RevalidationResult::default();\n        let mut relevant_attributes = &mut result.relevant_attributes;\n        let selectors_matched = &mut result.selectors_matched;\n\n        let matches_document_rules =\n            element.each_applicable_non_document_style_rule_data(|data, host| {\n                matching_context.with_shadow_host(Some(host), |matching_context| {\n                    data.selectors_for_cache_revalidation.lookup(\n                        element,\n                        self.quirks_mode,\n                        Some(&mut relevant_attributes),\n                        |selector_and_hashes| {\n                            selectors_matched.push(matches_selector(\n                                &selector_and_hashes.selector,\n                                selector_and_hashes.selector_offset,\n                                Some(&selector_and_hashes.hashes),\n                                &element,\n                                matching_context,\n                            ));\n                            true\n                        },\n                    );\n                })\n            });\n\n        for (data, origin) in self.cascade_data.iter_origins() {\n            if origin == Origin::Author && !matches_document_rules {\n                continue;\n            }\n\n            data.selectors_for_cache_revalidation.lookup(\n                element,\n                self.quirks_mode,\n                Some(&mut relevant_attributes),\n                |selector_and_hashes| {\n                    selectors_matched.push(matches_selector(\n                        &selector_and_hashes.selector,\n                        selector_and_hashes.selector_offset,\n                        Some(&selector_and_hashes.hashes),\n                        &element,\n                        &mut matching_context,\n                    ));\n                    true\n                },\n            );\n        }\n\n        result\n    }\n\n    /// Computes currently active scopes for the given element for revalidation purposes.\n    pub fn revalidate_scopes<E: TElement>(\n        &self,\n        element: &E,\n        selector_caches: &mut SelectorCaches,\n        needs_selector_flags: NeedsSelectorFlags,\n    ) -> ScopeRevalidationResult {\n        let mut matching_context = MatchingContext::new(\n            MatchingMode::Normal,\n            None,\n            selector_caches,\n            self.quirks_mode,\n            needs_selector_flags,\n            MatchingForInvalidation::No,\n        );\n\n        let mut result = ScopeRevalidationResult::default();\n        let matches_document_rules =\n            element.each_applicable_non_document_style_rule_data(|data, host| {\n                matching_context.with_shadow_host(Some(host), |matching_context| {\n                    data.revalidate_scopes(element, matching_context, &mut result);\n                })\n            });\n\n        for (data, origin) in self.cascade_data.iter_origins() {\n            if origin == Origin::Author && !matches_document_rules {\n                continue;\n            }\n\n            data.revalidate_scopes(element, &mut matching_context, &mut result);\n        }\n\n        result\n    }\n\n    /// Computes styles for a given declaration with parent_style.\n    ///\n    /// FIXME(emilio): the lack of pseudo / cascade flags look quite dubious,\n    /// hopefully this is only used for some canvas font stuff.\n    ///\n    /// TODO(emilio): The type parameter can go away when\n    /// https://github.com/rust-lang/rust/issues/35121 is fixed.\n    pub fn compute_for_declarations<E>(\n        &self,\n        guards: &StylesheetGuards,\n        parent_style: &ComputedValues,\n        declarations: Arc<Locked<PropertyDeclarationBlock>>,\n    ) -> Arc<ComputedValues>\n    where\n        E: TElement,\n    {\n        let block = declarations.read_with(guards.author);\n\n        // We don't bother inserting these declarations in the rule tree, since\n        // it'd be quite useless and slow.\n        //\n        // TODO(emilio): Now that we fixed bug 1493420, we should consider\n        // reversing this as it shouldn't be slow anymore, and should avoid\n        // generating two instantiations of apply_declarations.\n        properties::apply_declarations::<E, _>(\n            &self,\n            /* pseudo = */ None,\n            self.rule_tree.root(),\n            guards,\n            block.declaration_importance_iter().map(|(declaration, _)| {\n                (\n                    declaration,\n                    CascadePriority::new(\n                        CascadeLevel::same_tree_author_normal(),\n                        LayerOrder::root(),\n                        RuleCascadeFlags::empty(),\n                    ),\n                )\n            }),\n            Some(parent_style),\n            Some(parent_style),\n            FirstLineReparenting::No,\n            &PositionTryFallbacksTryTactic::default(),\n            CascadeMode::Unvisited {\n                visited_rules: None,\n            },\n            Default::default(),\n            RuleCascadeFlags::empty(),\n            /* rule_cache = */ None,\n            &mut Default::default(),\n            /* element = */ None,\n        )\n    }\n\n    /// Accessor for a shared reference to the device.\n    #[inline]\n    pub fn device(&self) -> &Device {\n        &self.device\n    }\n\n    /// Accessor for a mutable reference to the device.\n    #[inline]\n    pub fn device_mut(&mut self) -> &mut Device {\n        &mut self.device\n    }\n\n    /// Accessor for a shared reference to the rule tree.\n    #[inline]\n    pub fn rule_tree(&self) -> &RuleTree {\n        &self.rule_tree\n    }\n\n    /// Returns the script-registered custom property registry.\n    #[inline]\n    pub fn custom_property_script_registry(&self) -> &CustomPropertyScriptRegistry {\n        &self.script_custom_properties\n    }\n\n    /// Returns the script-registered custom property registry, as a mutable ref.\n    #[inline]\n    pub fn custom_property_script_registry_mut(&mut self) -> &mut CustomPropertyScriptRegistry {\n        &mut self.script_custom_properties\n    }\n\n    /// Measures heap usage.\n    #[cfg(feature = \"gecko\")]\n    pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        self.cascade_data.add_size_of(ops, sizes);\n        self.author_data_cache.add_size_of(ops, sizes);\n        sizes.mRuleTree += self.rule_tree.size_of(ops);\n\n        // We may measure other fields in the future if DMD says it's worth it.\n    }\n\n    /// Shutdown the static data that this module stores.\n    pub fn shutdown() {\n        let _entries = UA_CASCADE_DATA_CACHE.lock().unwrap().take_all();\n    }\n}\n\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum RegisterCustomPropertyResult {\n    SuccessfullyRegistered,\n    InvalidName,\n    AlreadyRegistered,\n    InvalidSyntax,\n    NoInitialValue,\n    InvalidInitialValue,\n    InitialValueNotComputationallyIndependent,\n}\n\nimpl Stylist {\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-registerproperty-function>\n    pub fn register_custom_property(\n        &mut self,\n        url_data: &UrlExtraData,\n        name: &str,\n        syntax: &str,\n        inherits: bool,\n        initial_value: Option<&str>,\n    ) -> RegisterCustomPropertyResult {\n        use RegisterCustomPropertyResult::*;\n\n        // If name is not a custom property name string, throw a SyntaxError and exit this algorithm.\n        let Ok(name) = parse_name(name).map(Atom::from) else {\n            return InvalidName;\n        };\n\n        // If property set already contains an entry with name as its property name (compared\n        // codepoint-wise), throw an InvalidModificationError and exit this algorithm.\n        if self.custom_property_script_registry().get(&name).is_some() {\n            return AlreadyRegistered;\n        }\n        // Attempt to consume a syntax definition from syntax. If it returns failure, throw a\n        // SyntaxError. Otherwise, let syntax definition be the returned syntax definition.\n        let Ok(syntax) = Descriptor::from_str(syntax, /* preserve_specified = */ false) else {\n            return InvalidSyntax;\n        };\n\n        let initial_value = match initial_value {\n            Some(value) => {\n                let mut input = ParserInput::new(value);\n                let parsed = Parser::new(&mut input)\n                    .parse_entirely(|input| {\n                        input.skip_whitespace();\n                        SpecifiedValue::parse(input, None, url_data).map(Arc::new)\n                    })\n                    .ok();\n                if parsed.is_none() {\n                    return InvalidInitialValue;\n                }\n                parsed\n            },\n            None => None,\n        };\n\n        if let Err(error) =\n            PropertyRegistration::validate_initial_value(&syntax, initial_value.as_deref())\n        {\n            return match error {\n                PropertyRegistrationError::InitialValueNotComputationallyIndependent => {\n                    InitialValueNotComputationallyIndependent\n                },\n                PropertyRegistrationError::InvalidInitialValue => InvalidInitialValue,\n                PropertyRegistrationError::NoInitialValue => NoInitialValue,\n            };\n        }\n\n        let property_registration = PropertyRegistration {\n            name: PropertyRuleName(name),\n            descriptors: PropertyDescriptors {\n                syntax: Some(syntax),\n                inherits: Some(if inherits {\n                    Inherits::True\n                } else {\n                    Inherits::False\n                }),\n                initial_value,\n            },\n            url_data: url_data.clone(),\n            source_location: SourceLocation { line: 0, column: 0 },\n        };\n        self.custom_property_script_registry_mut()\n            .register(property_registration);\n        self.rebuild_initial_values_for_custom_properties();\n\n        SuccessfullyRegistered\n    }\n}\n\n/// A vector that is sorted in layer order.\n#[derive(Clone, Debug, Deref, MallocSizeOf)]\npub struct LayerOrderedVec<T>(Vec<(T, LayerId)>);\nimpl<T> Default for LayerOrderedVec<T> {\n    fn default() -> Self {\n        Self(Default::default())\n    }\n}\n\n/// A map that is sorted in layer order.\n#[derive(Clone, Debug, Deref, MallocSizeOf)]\npub struct LayerOrderedMap<T>(PrecomputedHashMap<Atom, SmallVec<[(T, LayerId); 1]>>);\nimpl<T> Default for LayerOrderedMap<T> {\n    fn default() -> Self {\n        Self(Default::default())\n    }\n}\n\nimpl<T: 'static> LayerOrderedVec<T> {\n    fn clear(&mut self) {\n        self.0.clear();\n    }\n    fn push(&mut self, v: T, id: LayerId) {\n        self.0.push((v, id));\n    }\n    fn sort(&mut self, layers: &[CascadeLayer]) {\n        self.0\n            .sort_by_key(|&(_, ref id)| layers[id.0 as usize].order)\n    }\n}\n\nimpl<T: 'static> LayerOrderedMap<T> {\n    fn shrink_if_needed(&mut self) {\n        self.0.shrink_if_needed();\n    }\n    fn clear(&mut self) {\n        self.0.clear();\n    }\n    fn try_insert(&mut self, name: Atom, v: T, id: LayerId) -> Result<(), AllocErr> {\n        self.try_insert_with(name, v, id, |_, _| Ordering::Equal)\n    }\n    fn try_insert_with(\n        &mut self,\n        name: Atom,\n        v: T,\n        id: LayerId,\n        cmp: impl Fn(&T, &T) -> Ordering,\n    ) -> Result<(), AllocErr> {\n        self.0.try_reserve(1)?;\n        let vec = self.0.entry(name).or_default();\n        if let Some(&mut (ref mut val, ref last_id)) = vec.last_mut() {\n            if *last_id == id {\n                if cmp(&val, &v) != Ordering::Greater {\n                    *val = v;\n                }\n                return Ok(());\n            }\n        }\n        vec.push((v, id));\n        Ok(())\n    }\n    fn sort(&mut self, layers: &[CascadeLayer]) {\n        self.sort_with(layers, |_, _| Ordering::Equal)\n    }\n    fn sort_with(&mut self, layers: &[CascadeLayer], cmp: impl Fn(&T, &T) -> Ordering) {\n        for (_, v) in self.0.iter_mut() {\n            v.sort_by(|&(ref v1, ref id1), &(ref v2, ref id2)| {\n                let order1 = layers[id1.0 as usize].order;\n                let order2 = layers[id2.0 as usize].order;\n                order1.cmp(&order2).then_with(|| cmp(v1, v2))\n            })\n        }\n    }\n    /// Get an entry on the LayerOrderedMap by name.\n    pub fn get(&self, name: &Atom) -> Option<&T> {\n        let vec = self.0.get(name)?;\n        Some(&vec.last()?.0)\n    }\n}\n\n/// Wrapper to allow better tracking of memory usage by page rule lists.\n///\n/// This includes the layer ID for use with the named page table.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct PageRuleData {\n    /// Layer ID for sorting page rules after matching.\n    pub layer: LayerId,\n    /// Page rule\n    #[ignore_malloc_size_of = \"Arc, stylesheet measures as primary ref\"]\n    pub rule: Arc<Locked<PageRule>>,\n}\n\n/// Stores page rules indexed by page names.\n#[derive(Clone, Debug, Default, MallocSizeOf)]\npub struct PageRuleMap {\n    /// Page rules, indexed by page name. An empty atom indicates no page name.\n    pub rules: PrecomputedHashMap<Atom, SmallVec<[PageRuleData; 1]>>,\n}\n\nimpl PageRuleMap {\n    #[inline]\n    fn clear(&mut self) {\n        self.rules.clear();\n    }\n\n    /// Uses page-name and pseudo-classes to match all applicable\n    /// page-rules and append them to the matched_rules vec.\n    /// This will ensure correct rule order for cascading.\n    pub fn match_and_append_rules(\n        &self,\n        matched_rules: &mut Vec<ApplicableDeclarationBlock>,\n        origin: Origin,\n        guards: &StylesheetGuards,\n        cascade_data: &DocumentCascadeData,\n        name: &Option<Atom>,\n        pseudos: PagePseudoClassFlags,\n    ) {\n        let level = match origin {\n            Origin::UserAgent => CascadeLevel::new(CascadeOrigin::UA),\n            Origin::User => CascadeLevel::new(CascadeOrigin::User),\n            Origin::Author => CascadeLevel::same_tree_author_normal(),\n        };\n        let cascade_data = cascade_data.borrow_for_origin(origin);\n        let start = matched_rules.len();\n\n        self.match_and_add_rules(\n            matched_rules,\n            level,\n            guards,\n            cascade_data,\n            &atom!(\"\"),\n            pseudos,\n        );\n        if let Some(name) = name {\n            self.match_and_add_rules(matched_rules, level, guards, cascade_data, name, pseudos);\n        }\n\n        // Because page-rules do not have source location information stored,\n        // use stable sort to ensure source locations are preserved.\n        matched_rules[start..].sort_by_key(|block| block.sort_key());\n    }\n\n    fn match_and_add_rules(\n        &self,\n        extra_declarations: &mut Vec<ApplicableDeclarationBlock>,\n        level: CascadeLevel,\n        guards: &StylesheetGuards,\n        cascade_data: &CascadeData,\n        name: &Atom,\n        pseudos: PagePseudoClassFlags,\n    ) {\n        let rules = match self.rules.get(name) {\n            Some(rules) => rules,\n            None => return,\n        };\n        for data in rules.iter() {\n            let rule = data.rule.read_with(level.guard(&guards));\n            let specificity = match rule.match_specificity(pseudos) {\n                Some(specificity) => specificity,\n                None => continue,\n            };\n            let block = rule.block.clone();\n            extra_declarations.push(ApplicableDeclarationBlock::new(\n                StyleSource::from_declarations(block),\n                0,\n                level,\n                specificity,\n                cascade_data.layer_order_for(data.layer),\n                ScopeProximity::infinity(), // Page rule can't have nested rules anyway.\n                RuleCascadeFlags::empty(),\n            ));\n        }\n    }\n}\n\nimpl MallocShallowSizeOf for PageRuleMap {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.rules.shallow_size_of(ops)\n    }\n}\n\ntype PositionTryMap = LayerOrderedMap<Arc<Locked<PositionTryRule>>>;\n\n/// This struct holds data which users of Stylist may want to extract from stylesheets which can be\n/// done at the same time as updating.\n#[derive(Clone, Debug, Default)]\npub struct ExtraStyleData {\n    /// A list of effective font-face rules and their origin.\n    pub font_faces: LayerOrderedVec<Arc<Locked<FontFaceRule>>>,\n\n    /// A list of effective font-feature-values rules.\n    pub font_feature_values: LayerOrderedVec<Arc<FontFeatureValuesRule>>,\n\n    /// A list of effective font-palette-values rules.\n    pub font_palette_values: LayerOrderedVec<Arc<FontPaletteValuesRule>>,\n\n    /// A map of effective counter-style rules.\n    pub counter_styles: LayerOrderedMap<Arc<Locked<CounterStyleRule>>>,\n\n    /// A map of effective @position-try rules.\n    pub position_try_rules: PositionTryMap,\n\n    /// A map of effective page rules.\n    pub pages: PageRuleMap,\n\n    /// A list of effective @view-transition rules.\n    pub view_transitions: LayerOrderedVec<Arc<ViewTransitionRule>>,\n}\n\nimpl ExtraStyleData {\n    /// Add the given @font-face rule.\n    fn add_font_face(&mut self, rule: &Arc<Locked<FontFaceRule>>, layer: LayerId) {\n        self.font_faces.push(rule.clone(), layer);\n    }\n\n    /// Add the given @font-feature-values rule.\n    fn add_font_feature_values(&mut self, rule: &Arc<FontFeatureValuesRule>, layer: LayerId) {\n        self.font_feature_values.push(rule.clone(), layer);\n    }\n\n    /// Add the given @font-palette-values rule.\n    fn add_font_palette_values(&mut self, rule: &Arc<FontPaletteValuesRule>, layer: LayerId) {\n        self.font_palette_values.push(rule.clone(), layer);\n    }\n\n    /// Add the given @counter-style rule.\n    fn add_counter_style(\n        &mut self,\n        guard: &SharedRwLockReadGuard,\n        rule: &Arc<Locked<CounterStyleRule>>,\n        layer: LayerId,\n    ) -> Result<(), AllocErr> {\n        let name = rule.read_with(guard).name().0.clone();\n        self.counter_styles.try_insert(name, rule.clone(), layer)\n    }\n\n    /// Add the given @position-try rule.\n    fn add_position_try(\n        &mut self,\n        name: Atom,\n        rule: Arc<Locked<PositionTryRule>>,\n        layer: LayerId,\n    ) -> Result<(), AllocErr> {\n        self.position_try_rules.try_insert(name, rule, layer)\n    }\n\n    /// Add the given @page rule.\n    fn add_page(\n        &mut self,\n        guard: &SharedRwLockReadGuard,\n        rule: &Arc<Locked<PageRule>>,\n        layer: LayerId,\n    ) -> Result<(), AllocErr> {\n        let page_rule = rule.read_with(guard);\n        let mut add_rule = |name| {\n            let vec = self.pages.rules.entry(name).or_default();\n            vec.push(PageRuleData {\n                layer,\n                rule: rule.clone(),\n            });\n        };\n        if page_rule.selectors.0.is_empty() {\n            add_rule(atom!(\"\"));\n        } else {\n            for selector in page_rule.selectors.as_slice() {\n                add_rule(selector.name.0.clone());\n            }\n        }\n        Ok(())\n    }\n\n    fn add_view_transition(&mut self, rule: &Arc<ViewTransitionRule>, layer: LayerId) {\n        self.view_transitions.push(rule.clone(), layer)\n    }\n\n    fn sort_by_layer(&mut self, layers: &[CascadeLayer]) {\n        self.font_faces.sort(layers);\n        self.font_feature_values.sort(layers);\n        self.font_palette_values.sort(layers);\n        self.counter_styles.sort(layers);\n        self.position_try_rules.sort(layers);\n        self.view_transitions.sort(layers);\n    }\n\n    fn clear(&mut self) {\n        self.font_faces.clear();\n        self.font_feature_values.clear();\n        self.font_palette_values.clear();\n        self.counter_styles.clear();\n        self.position_try_rules.clear();\n        self.pages.clear();\n    }\n}\n\n// Don't let a prefixed keyframes animation override\n// a non-prefixed one.\nfn compare_keyframes_in_same_layer(v1: &KeyframesAnimation, v2: &KeyframesAnimation) -> Ordering {\n    if v1.vendor_prefix.is_some() == v2.vendor_prefix.is_some() {\n        Ordering::Equal\n    } else if v2.vendor_prefix.is_some() {\n        Ordering::Greater\n    } else {\n        Ordering::Less\n    }\n}\n\n/// An iterator over the different ExtraStyleData.\npub struct ExtraStyleDataIterator<'a>(DocumentCascadeDataIter<'a>);\n\nimpl<'a> Iterator for ExtraStyleDataIterator<'a> {\n    type Item = (&'a ExtraStyleData, Origin);\n\n    fn next(&mut self) -> Option<Self::Item> {\n        self.0.next().map(|d| (&d.0.extra_data, d.1))\n    }\n}\n\nimpl MallocSizeOf for ExtraStyleData {\n    /// Measure heap usage.\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut n = 0;\n        n += self.font_faces.shallow_size_of(ops);\n        n += self.font_feature_values.shallow_size_of(ops);\n        n += self.font_palette_values.shallow_size_of(ops);\n        n += self.counter_styles.shallow_size_of(ops);\n        n += self.position_try_rules.shallow_size_of(ops);\n        n += self.pages.shallow_size_of(ops);\n        n\n    }\n}\n\n/// SelectorMapEntry implementation for use in our revalidation selector map.\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(Clone, Debug)]\nstruct RevalidationSelectorAndHashes {\n    #[cfg_attr(\n        feature = \"gecko\",\n        ignore_malloc_size_of = \"CssRules have primary refs, we measure there\"\n    )]\n    selector: Selector<SelectorImpl>,\n    selector_offset: usize,\n    hashes: AncestorHashes,\n}\n\nimpl RevalidationSelectorAndHashes {\n    fn new(selector: Selector<SelectorImpl>, hashes: AncestorHashes) -> Self {\n        let selector_offset = {\n            // We basically want to check whether the first combinator is a\n            // pseudo-element combinator.  If it is, we want to use the offset\n            // one past it.  Otherwise, our offset is 0.\n            let mut index = 0;\n            let mut iter = selector.iter();\n\n            // First skip over the first ComplexSelector.\n            //\n            // We can't check what sort of what combinator we have until we do\n            // that.\n            for _ in &mut iter {\n                index += 1; // Simple selector\n            }\n\n            match iter.next_sequence() {\n                Some(Combinator::PseudoElement) => index + 1, // +1 for the combinator\n                _ => 0,\n            }\n        };\n\n        RevalidationSelectorAndHashes {\n            selector,\n            selector_offset,\n            hashes,\n        }\n    }\n}\n\nimpl SelectorMapEntry for RevalidationSelectorAndHashes {\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl> {\n        self.selector.iter_from(self.selector_offset)\n    }\n}\n\n/// A selector visitor implementation that collects all the state the Stylist\n/// cares about a selector.\nstruct StylistSelectorVisitor<'a> {\n    /// Whether we've past the rightmost compound selector, not counting\n    /// pseudo-elements.\n    passed_rightmost_selector: bool,\n\n    /// Whether the selector needs revalidation for the style sharing cache.\n    needs_revalidation: &'a mut bool,\n\n    /// Flags for which selector list-containing components the visitor is\n    /// inside of, if any\n    in_selector_list_of: SelectorListKind,\n\n    /// The filter with all the id's getting referenced from rightmost\n    /// selectors.\n    mapped_ids: &'a mut PrecomputedHashSet<Atom>,\n\n    /// The filter with the IDs getting referenced from the selector list of\n    /// :nth-child(... of <selector list>) selectors.\n    nth_of_mapped_ids: &'a mut PrecomputedHashSet<Atom>,\n\n    /// The filter with the local names of attributes there are selectors for.\n    attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,\n\n    /// The filter with the classes getting referenced from the selector list of\n    /// :nth-child(... of <selector list>) selectors.\n    nth_of_class_dependencies: &'a mut PrecomputedHashSet<Atom>,\n\n    /// The filter with the local names of attributes there are selectors for\n    /// within the selector list of :nth-child(... of <selector list>)\n    /// selectors.\n    nth_of_attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,\n\n    /// The filter with the local names of custom states in selectors for\n    /// within the selector list of :nth-child(... of <selector list>)\n    /// selectors.\n    nth_of_custom_state_dependencies: &'a mut PrecomputedHashSet<AtomIdent>,\n\n    /// All the states selectors in the page reference.\n    state_dependencies: &'a mut ElementState,\n\n    /// All the state selectors in the page reference within the selector list\n    /// of :nth-child(... of <selector list>) selectors.\n    nth_of_state_dependencies: &'a mut ElementState,\n\n    /// All the document states selectors in the page reference.\n    document_state_dependencies: &'a mut DocumentState,\n}\n\nfn component_needs_revalidation(\n    c: &Component<SelectorImpl>,\n    passed_rightmost_selector: bool,\n) -> bool {\n    match *c {\n        Component::ID(_) => {\n            // TODO(emilio): This could also check that the ID is not already in\n            // the rule hash. In that case, we could avoid making this a\n            // revalidation selector too.\n            //\n            // See https://bugzilla.mozilla.org/show_bug.cgi?id=1369611\n            passed_rightmost_selector\n        },\n        Component::AttributeInNoNamespaceExists { .. }\n        | Component::AttributeInNoNamespace { .. }\n        | Component::AttributeOther(_)\n        | Component::Empty\n        | Component::Nth(_)\n        | Component::NthOf(_)\n        | Component::Has(_) => true,\n        Component::NonTSPseudoClass(ref p) => p.needs_cache_revalidation(),\n        _ => false,\n    }\n}\n\nimpl<'a> StylistSelectorVisitor<'a> {\n    fn visit_nested_selector(\n        &mut self,\n        in_selector_list_of: SelectorListKind,\n        selector: &Selector<SelectorImpl>,\n    ) {\n        let old_passed_rightmost_selector = self.passed_rightmost_selector;\n        let old_in_selector_list_of = self.in_selector_list_of;\n\n        self.passed_rightmost_selector = false;\n        self.in_selector_list_of = in_selector_list_of;\n        let _ret = selector.visit(self);\n        debug_assert!(_ret, \"We never return false\");\n\n        self.passed_rightmost_selector = old_passed_rightmost_selector;\n        self.in_selector_list_of = old_in_selector_list_of;\n    }\n}\n\nimpl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {\n    type Impl = SelectorImpl;\n\n    fn visit_complex_selector(&mut self, combinator: Option<Combinator>) -> bool {\n        *self.needs_revalidation =\n            *self.needs_revalidation || combinator.map_or(false, |c| c.is_sibling());\n\n        // NOTE(emilio): this call happens before we visit any of the simple\n        // selectors in the next ComplexSelector, so we can use this to skip\n        // looking at them.\n        self.passed_rightmost_selector = self.passed_rightmost_selector\n            || !matches!(combinator, None | Some(Combinator::PseudoElement));\n\n        true\n    }\n\n    fn visit_selector_list(\n        &mut self,\n        list_kind: SelectorListKind,\n        list: &[Selector<Self::Impl>],\n    ) -> bool {\n        let in_selector_list_of = self.in_selector_list_of | list_kind;\n        for selector in list {\n            self.visit_nested_selector(in_selector_list_of, selector);\n        }\n        true\n    }\n\n    fn visit_relative_selector_list(\n        &mut self,\n        list: &[selectors::parser::RelativeSelector<Self::Impl>],\n    ) -> bool {\n        let in_selector_list_of = self.in_selector_list_of | SelectorListKind::HAS;\n        for selector in list {\n            self.visit_nested_selector(in_selector_list_of, &selector.selector);\n        }\n        true\n    }\n\n    fn visit_attribute_selector(\n        &mut self,\n        _ns: &NamespaceConstraint<&Namespace>,\n        name: &LocalName,\n        lower_name: &LocalName,\n    ) -> bool {\n        if self.in_selector_list_of.relevant_to_nth_of_dependencies() {\n            self.nth_of_attribute_dependencies.insert(name.clone());\n            if name != lower_name {\n                self.nth_of_attribute_dependencies\n                    .insert(lower_name.clone());\n            }\n        }\n\n        self.attribute_dependencies.insert(name.clone());\n        if name != lower_name {\n            self.attribute_dependencies.insert(lower_name.clone());\n        }\n\n        true\n    }\n\n    fn visit_simple_selector(&mut self, s: &Component<SelectorImpl>) -> bool {\n        *self.needs_revalidation = *self.needs_revalidation\n            || component_needs_revalidation(s, self.passed_rightmost_selector);\n\n        match *s {\n            Component::NonTSPseudoClass(NonTSPseudoClass::CustomState(ref name)) => {\n                // CustomStateSet is special cased as it is a functional pseudo\n                // class with unbounded inner values. This is different to\n                // other psuedo class like :emtpy or :dir() which can be packed\n                // into the ElementState bitflags. For CustomState, however,\n                // the state name should be checked for presence in the selector.\n                if self.in_selector_list_of.relevant_to_nth_of_dependencies() {\n                    self.nth_of_custom_state_dependencies.insert(name.0.clone());\n                }\n            },\n            Component::NonTSPseudoClass(ref p) => {\n                self.state_dependencies.insert(p.state_flag());\n                self.document_state_dependencies\n                    .insert(p.document_state_flag());\n\n                if self.in_selector_list_of.relevant_to_nth_of_dependencies() {\n                    self.nth_of_state_dependencies.insert(p.state_flag());\n                }\n            },\n            Component::ID(ref id) => {\n                // We want to stop storing mapped ids as soon as we've moved off\n                // the rightmost ComplexSelector that is not a pseudo-element.\n                //\n                // That can be detected by a visit_complex_selector call with a\n                // combinator other than None and PseudoElement.\n                //\n                // Importantly, this call happens before we visit any of the\n                // simple selectors in that ComplexSelector.\n                //\n                // NOTE(emilio): See the comment regarding on when this may\n                // break in visit_complex_selector.\n                if !self.passed_rightmost_selector {\n                    self.mapped_ids.insert(id.0.clone());\n                }\n\n                if self.in_selector_list_of.relevant_to_nth_of_dependencies() {\n                    self.nth_of_mapped_ids.insert(id.0.clone());\n                }\n            },\n            Component::Class(ref class)\n                if self.in_selector_list_of.relevant_to_nth_of_dependencies() =>\n            {\n                self.nth_of_class_dependencies.insert(class.0.clone());\n            },\n            _ => {},\n        }\n\n        true\n    }\n}\n\n/// A set of rules for element and pseudo-elements.\n#[derive(Clone, Debug, Default, MallocSizeOf)]\nstruct GenericElementAndPseudoRules<Map> {\n    /// Rules from stylesheets at this `CascadeData`'s origin.\n    element_map: Map,\n\n    /// Rules from stylesheets at this `CascadeData`'s origin that correspond\n    /// to a given pseudo-element.\n    ///\n    /// FIXME(emilio): There are a bunch of wasted entries here in practice.\n    /// Figure out a good way to do a `PerNonAnonBox` and `PerAnonBox` (for\n    /// `precomputed_values_for_pseudo`) without duplicating a lot of code.\n    pseudos_map: PerPseudoElementMap<Self>,\n}\n\nimpl<Map: Default + MallocSizeOf> GenericElementAndPseudoRules<Map> {\n    #[inline(always)]\n    fn for_insertion<'a>(&mut self, pseudo_elements: &[&'a PseudoElement]) -> &mut Map {\n        let mut current = self;\n        for &pseudo_element in pseudo_elements {\n            debug_assert!(\n                !pseudo_element.is_precomputed()\n                    && !pseudo_element.is_unknown_webkit_pseudo_element(),\n                \"Precomputed pseudos should end up in precomputed_pseudo_element_decls, \\\n                 and unknown webkit pseudos should be discarded before getting here\"\n            );\n\n            current = current\n                .pseudos_map\n                .get_or_insert_with(pseudo_element, Default::default);\n        }\n\n        &mut current.element_map\n    }\n\n    #[inline]\n    fn rules(&self, pseudo_elements: &[PseudoElement]) -> Option<&Map> {\n        let mut current = self;\n        for pseudo in pseudo_elements {\n            current = current.pseudos_map.get(&pseudo)?;\n        }\n        Some(&current.element_map)\n    }\n\n    /// Measures heap usage.\n    #[cfg(feature = \"gecko\")]\n    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        sizes.mElementAndPseudosMaps += self.element_map.size_of(ops);\n\n        for elem in self.pseudos_map.iter() {\n            sizes.mElementAndPseudosMaps += MallocSizeOf::size_of(elem, ops);\n        }\n    }\n}\n\ntype ElementAndPseudoRules = GenericElementAndPseudoRules<SelectorMap<Rule>>;\ntype PartMap = PrecomputedHashMap<Atom, SmallVec<[Rule; 1]>>;\ntype PartElementAndPseudoRules = GenericElementAndPseudoRules<PartMap>;\n\nimpl ElementAndPseudoRules {\n    // TODO(emilio): Should we retain storage of these?\n    fn clear(&mut self) {\n        self.element_map.clear();\n        self.pseudos_map.clear();\n    }\n\n    fn shrink_if_needed(&mut self) {\n        self.element_map.shrink_if_needed();\n        for pseudo in self.pseudos_map.iter_mut() {\n            pseudo.shrink_if_needed();\n        }\n    }\n}\n\nimpl PartElementAndPseudoRules {\n    // TODO(emilio): Should we retain storage of these?\n    fn clear(&mut self) {\n        self.element_map.clear();\n        self.pseudos_map.clear();\n    }\n}\n\n/// The id of a given layer, a sequentially-increasing identifier.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]\npub struct LayerId(u16);\n\nimpl LayerId {\n    /// The id of the root layer.\n    pub const fn root() -> Self {\n        Self(0)\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf)]\nstruct CascadeLayer {\n    id: LayerId,\n    order: LayerOrder,\n    children: Vec<LayerId>,\n}\n\nimpl CascadeLayer {\n    const fn root() -> Self {\n        Self {\n            id: LayerId::root(),\n            order: LayerOrder::root(),\n            children: vec![],\n        }\n    }\n}\n\n/// The id of a given container condition, a sequentially-increasing identifier\n/// for a given style set.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]\npub struct ContainerConditionId(u16);\n\nimpl ContainerConditionId {\n    /// A special id that represents no container rule.\n    pub const fn none() -> Self {\n        Self(0)\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf)]\nstruct ContainerConditionReference {\n    parent: ContainerConditionId,\n    /// Contains the container conditions of a particular rule.\n    ///\n    /// This should only ever be empty for the root rule, which acts as a\n    /// sentinel value.\n    #[ignore_malloc_size_of = \"Arc\"]\n    conditions: ArcSlice<ContainerCondition>,\n}\n\nimpl ContainerConditionReference {\n    /// Creates an empty, root container condition reference.\n    fn none() -> Self {\n        Self {\n            parent: ContainerConditionId::none(),\n            conditions: ArcSlice::default(),\n        }\n    }\n}\n\n/// The id of a given scope condition, a sequentially-increasing identifier\n/// for a given style set.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord)]\npub struct ScopeConditionId(u16);\n\nimpl ScopeConditionId {\n    /// Construct a new scope condition id.\n    pub fn new(id: u16) -> Self {\n        Self(id)\n    }\n\n    /// A special id that represents no scope rule.\n    pub const fn none() -> Self {\n        Self(0)\n    }\n}\n\n/// Data required to process this scope condition.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct ScopeConditionReference {\n    /// The ID of outer scope condition, `none()` otherwise.\n    parent: ScopeConditionId,\n    /// Start and end bounds of the scope. None implies sentinel data (i.e. Not a scope condition).\n    condition: Option<ScopeBoundsWithHashes>,\n    /// Implicit scope root of this scope condition, computed unconditionally,\n    /// even if the start bound may be Some.\n    #[ignore_malloc_size_of = \"Raw ptr behind the scenes\"]\n    implicit_scope_root: StylistImplicitScopeRoot,\n    /// Is the condition trivial? See `ScopeBoundsWithHashes::is_trivial`.\n    is_trivial: bool,\n}\n\nimpl ScopeConditionReference {\n    /// Create a new scope condition.\n    pub fn new(\n        parent: ScopeConditionId,\n        condition: Option<ScopeBoundsWithHashes>,\n        implicit_scope_root: ImplicitScopeRoot,\n        is_trivial: bool,\n    ) -> Self {\n        Self {\n            parent,\n            condition,\n            implicit_scope_root: StylistImplicitScopeRoot::Normal(implicit_scope_root),\n            is_trivial,\n        }\n    }\n\n    /// Create a sentinel scope condition.\n    pub const fn none() -> Self {\n        Self {\n            parent: ScopeConditionId::none(),\n            condition: None,\n            implicit_scope_root: StylistImplicitScopeRoot::default_const(),\n            is_trivial: true,\n        }\n    }\n}\n\n/// All potential sscope root candidates.\npub struct ScopeRootCandidates {\n    /// List of scope root candidates.\n    pub candidates: Vec<ScopeRootCandidate>,\n    /// Is the scope condition matching these candidates trivial? See `ScopeBoundsWithHashes::is_trivial`.\n    pub is_trivial: bool,\n}\n\nimpl Default for ScopeRootCandidates {\n    fn default() -> Self {\n        Self {\n            candidates: vec![],\n            is_trivial: true,\n        }\n    }\n}\n\nimpl ScopeRootCandidates {\n    fn empty(is_trivial: bool) -> Self {\n        Self {\n            candidates: vec![],\n            is_trivial,\n        }\n    }\n}\n\n/// Start and end bound of a scope, along with their selector hashes.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct ScopeBoundWithHashes {\n    // TODO(dshin): With replaced parent selectors, these may be unique...\n    #[ignore_malloc_size_of = \"Arc\"]\n    selectors: SelectorList<SelectorImpl>,\n    hashes: SmallVec<[AncestorHashes; 1]>,\n}\n\nimpl ScopeBoundWithHashes {\n    fn new(quirks_mode: QuirksMode, selectors: SelectorList<SelectorImpl>) -> Self {\n        let mut hashes = SmallVec::with_capacity(selectors.len());\n        for selector in selectors.slice() {\n            hashes.push(AncestorHashes::new(selector, quirks_mode));\n        }\n        Self { selectors, hashes }\n    }\n\n    fn new_no_hash(selectors: SelectorList<SelectorImpl>) -> Self {\n        let hashes = selectors\n            .slice()\n            .iter()\n            .map(|_| AncestorHashes {\n                packed_hashes: [0, 0, 0],\n            })\n            .collect();\n        Self { selectors, hashes }\n    }\n}\n\n/// Bounds for this scope, along with corresponding selector hashes.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct ScopeBoundsWithHashes {\n    /// Start of the scope bound. If None, implies implicit scope root.\n    start: Option<ScopeBoundWithHashes>,\n    /// Optional end of the scope bound.\n    end: Option<ScopeBoundWithHashes>,\n}\n\nimpl ScopeBoundsWithHashes {\n    /// Create a new scope bound, hashing selectors for fast rejection.\n    fn new(\n        quirks_mode: QuirksMode,\n        start: Option<SelectorList<SelectorImpl>>,\n        end: Option<SelectorList<SelectorImpl>>,\n    ) -> Self {\n        Self {\n            start: start.map(|selectors| ScopeBoundWithHashes::new(quirks_mode, selectors)),\n            end: end.map(|selectors| ScopeBoundWithHashes::new(quirks_mode, selectors)),\n        }\n    }\n\n    /// Create a new scope bound, but not hashing any selector.\n    pub fn new_no_hash(\n        start: Option<SelectorList<SelectorImpl>>,\n        end: Option<SelectorList<SelectorImpl>>,\n    ) -> Self {\n        Self {\n            start: start.map(|selectors| ScopeBoundWithHashes::new_no_hash(selectors)),\n            end: end.map(|selectors| ScopeBoundWithHashes::new_no_hash(selectors)),\n        }\n    }\n\n    fn selectors_for<'a>(\n        bound_with_hashes: Option<&'a ScopeBoundWithHashes>,\n    ) -> impl Iterator<Item = &'a Selector<SelectorImpl>> {\n        bound_with_hashes\n            .map(|b| b.selectors.slice().iter())\n            .into_iter()\n            .flatten()\n    }\n\n    fn start_selectors<'a>(&'a self) -> impl Iterator<Item = &'a Selector<SelectorImpl>> {\n        Self::selectors_for(self.start.as_ref())\n    }\n\n    fn end_selectors<'a>(&'a self) -> impl Iterator<Item = &'a Selector<SelectorImpl>> {\n        Self::selectors_for(self.end.as_ref())\n    }\n\n    fn is_trivial(&self) -> bool {\n        fn scope_bound_is_trivial(bound: &Option<ScopeBoundWithHashes>, default: bool) -> bool {\n            bound.as_ref().map_or(default, |bound| {\n                scope_selector_list_is_trivial(&bound.selectors)\n            })\n        }\n\n        // Given an implicit scope, we are unable to tell if the cousins share the same implicit root.\n        scope_bound_is_trivial(&self.start, false) && scope_bound_is_trivial(&self.end, true)\n    }\n}\n\n/// Find all scope conditions for a given condition ID, indexing into the given list of scope conditions.\npub fn scope_root_candidates<E>(\n    scope_conditions: &[ScopeConditionReference],\n    id: ScopeConditionId,\n    element: &E,\n    override_matches_shadow_host_for_part: bool,\n    scope_subject_map: &ScopeSubjectMap,\n    context: &mut MatchingContext<SelectorImpl>,\n) -> ScopeRootCandidates\nwhere\n    E: TElement,\n{\n    let condition_ref = &scope_conditions[id.0 as usize];\n    let bounds = match condition_ref.condition {\n        None => return ScopeRootCandidates::default(),\n        Some(ref c) => c,\n    };\n    // Make sure the parent scopes ara evaluated first. This runs a bit counter to normal\n    // selector matching where rightmost selectors match first. However, this avoids having\n    // to traverse through descendants (i.e. Avoids tree traversal vs linear traversal).\n    let outer_result = scope_root_candidates(\n        scope_conditions,\n        condition_ref.parent,\n        element,\n        override_matches_shadow_host_for_part,\n        scope_subject_map,\n        context,\n    );\n\n    let is_trivial = condition_ref.is_trivial && outer_result.is_trivial;\n    let is_outermost_scope = condition_ref.parent == ScopeConditionId::none();\n    if !is_outermost_scope && outer_result.candidates.is_empty() {\n        return ScopeRootCandidates::empty(is_trivial);\n    }\n\n    let (root_target, matches_shadow_host) = if let Some(start) = bounds.start.as_ref() {\n        if let Some(filter) = context.bloom_filter {\n            // Use the bloom filter here. If our ancestors do not have the right hashes,\n            // there's no point in traversing up. Besides, the filter is built for this depth,\n            // so the filter contains more data than it should, the further we go up the ancestor\n            // chain. It wouldn't generate wrong results, but makes the traversal even more pointless.\n            if !start\n                .hashes\n                .iter()\n                .any(|entry| selector_may_match(entry, filter))\n            {\n                return ScopeRootCandidates::empty(is_trivial);\n            }\n        }\n        (\n            ScopeTarget::Selector(&start.selectors),\n            scope_start_matches_shadow_host(&start.selectors),\n        )\n    } else {\n        let implicit_root = condition_ref.implicit_scope_root;\n        match implicit_root {\n            StylistImplicitScopeRoot::Normal(r) => (\n                ScopeTarget::Implicit(r.element(context.current_host.clone())),\n                r.matches_shadow_host(),\n            ),\n            StylistImplicitScopeRoot::Cached(index) => {\n                let host = context\n                    .current_host\n                    .expect(\"Cached implicit scope for light DOM implicit scope\");\n                match E::implicit_scope_for_sheet_in_shadow_root(host, index) {\n                    None => return ScopeRootCandidates::empty(is_trivial),\n                    Some(root) => (\n                        ScopeTarget::Implicit(root.element(context.current_host.clone())),\n                        root.matches_shadow_host(),\n                    ),\n                }\n            },\n        }\n    };\n    // For `::part`, we need to be able to reach the outer tree. Parts without the corresponding\n    // `exportparts` attribute will be rejected at the selector matching time.\n    let matches_shadow_host = override_matches_shadow_host_for_part || matches_shadow_host;\n\n    let potential_scope_roots = if is_outermost_scope {\n        collect_scope_roots(\n            *element,\n            None,\n            context,\n            &root_target,\n            matches_shadow_host,\n            scope_subject_map,\n        )\n    } else {\n        let mut result = vec![];\n        for activation in outer_result.candidates {\n            let mut this_result = collect_scope_roots(\n                *element,\n                Some(activation.root),\n                context,\n                &root_target,\n                matches_shadow_host,\n                scope_subject_map,\n            );\n            result.append(&mut this_result);\n        }\n        result\n    };\n\n    if potential_scope_roots.is_empty() {\n        return ScopeRootCandidates::empty(is_trivial);\n    }\n\n    let candidates = if let Some(end) = bounds.end.as_ref() {\n        let mut result = vec![];\n        // If any scope-end selector matches, we're not in scope.\n        for scope_root in potential_scope_roots {\n            if end\n                .selectors\n                .slice()\n                .iter()\n                .zip(end.hashes.iter())\n                .all(|(selector, hashes)| {\n                    // Like checking for scope-start, use the bloom filter here.\n                    if let Some(filter) = context.bloom_filter {\n                        if !selector_may_match(hashes, filter) {\n                            // Selector this hash belongs to won't cause us to be out of this scope.\n                            return true;\n                        }\n                    }\n\n                    !element_is_outside_of_scope(\n                        selector,\n                        *element,\n                        scope_root.root,\n                        context,\n                        matches_shadow_host,\n                    )\n                })\n            {\n                result.push(scope_root);\n            }\n        }\n        result\n    } else {\n        potential_scope_roots\n    };\n\n    ScopeRootCandidates {\n        candidates,\n        is_trivial,\n    }\n}\n\n/// Implicit scope root, which may or may not be cached (i.e. For shadow DOM author\n/// styles that are cached and shared).\n#[derive(Copy, Clone, Debug, MallocSizeOf)]\nenum StylistImplicitScopeRoot {\n    Normal(ImplicitScopeRoot),\n    Cached(usize),\n}\n// Should be safe, only mutated through mutable methods in `Stylist`.\nunsafe impl Sync for StylistImplicitScopeRoot {}\n\nimpl StylistImplicitScopeRoot {\n    const fn default_const() -> Self {\n        // Use the \"safest\" fallback.\n        Self::Normal(ImplicitScopeRoot::DocumentElement)\n    }\n}\n\nimpl Default for StylistImplicitScopeRoot {\n    fn default() -> Self {\n        Self::default_const()\n    }\n}\n\n/// Data resulting from performing the CSS cascade that is specific to a given\n/// origin.\n///\n/// FIXME(emilio): Consider renaming and splitting in `CascadeData` and\n/// `InvalidationData`? That'd make `clear_cascade_data()` clearer.\n#[derive(Debug, Clone, MallocSizeOf)]\npub struct CascadeData {\n    /// The data coming from normal style rules that apply to elements at this\n    /// cascade level.\n    normal_rules: ElementAndPseudoRules,\n\n    /// The `:host` pseudo rules that are the rightmost selector (without\n    /// accounting for pseudo-elements), or `:scope` rules that may match\n    /// the featureless host.\n    featureless_host_rules: Option<Box<ElementAndPseudoRules>>,\n\n    /// The data coming from ::slotted() pseudo-element rules.\n    ///\n    /// We need to store them separately because an element needs to match\n    /// ::slotted() pseudo-element rules in different shadow roots.\n    ///\n    /// In particular, we need to go through all the style data in all the\n    /// containing style scopes starting from the closest assigned slot.\n    slotted_rules: Option<Box<ElementAndPseudoRules>>,\n\n    /// The data coming from ::part() pseudo-element rules.\n    ///\n    /// We need to store them separately because an element needs to match\n    /// ::part() pseudo-element rules in different shadow roots.\n    part_rules: Option<Box<PartElementAndPseudoRules>>,\n\n    /// The invalidation map for these rules.\n    invalidation_map: InvalidationMap,\n\n    /// The relative selector equivalent of the invalidation map.\n    relative_selector_invalidation_map: InvalidationMap,\n\n    additional_relative_selector_invalidation_map: AdditionalRelativeSelectorInvalidationMap,\n\n    /// The attribute local names that appear in attribute selectors.  Used\n    /// to avoid taking element snapshots when an irrelevant attribute changes.\n    /// (We don't bother storing the namespace, since namespaced attributes are\n    /// rare.)\n    attribute_dependencies: PrecomputedHashSet<LocalName>,\n\n    /// The classes that appear in the selector list of\n    /// :nth-child(... of <selector list>). Used to avoid restyling siblings of\n    /// an element when an irrelevant class changes.\n    nth_of_class_dependencies: PrecomputedHashSet<Atom>,\n\n    /// The attributes that appear in the selector list of\n    /// :nth-child(... of <selector list>). Used to avoid restyling siblings of\n    /// an element when an irrelevant attribute changes.\n    nth_of_attribute_dependencies: PrecomputedHashSet<LocalName>,\n\n    /// The custom states that appear in the selector list of\n    /// :nth-child(... of <selector list>). Used to avoid restyling siblings of\n    /// an element when an irrelevant custom state changes.\n    nth_of_custom_state_dependencies: PrecomputedHashSet<AtomIdent>,\n\n    /// The element state bits that are relied on by selectors.  Like\n    /// `attribute_dependencies`, this is used to avoid taking element snapshots\n    /// when an irrelevant element state bit changes.\n    state_dependencies: ElementState,\n\n    /// The element state bits that are relied on by selectors that appear in\n    /// the selector list of :nth-child(... of <selector list>).\n    nth_of_state_dependencies: ElementState,\n\n    /// The document state bits that are relied on by selectors.  This is used\n    /// to tell whether we need to restyle the entire document when a document\n    /// state bit changes.\n    document_state_dependencies: DocumentState,\n\n    /// The ids that appear in the rightmost complex selector of selectors (and\n    /// hence in our selector maps).  Used to determine when sharing styles is\n    /// safe: we disallow style sharing for elements whose id matches this\n    /// filter, and hence might be in one of our selector maps.\n    mapped_ids: PrecomputedHashSet<Atom>,\n\n    /// The IDs that appear in the selector list of\n    /// :nth-child(... of <selector list>). Used to avoid restyling siblings\n    /// of an element when an irrelevant ID changes.\n    nth_of_mapped_ids: PrecomputedHashSet<Atom>,\n\n    /// Selectors that require explicit cache revalidation (i.e. which depend\n    /// on state that is not otherwise visible to the cache, like attributes or\n    /// tree-structural state like child index and pseudos).\n    #[ignore_malloc_size_of = \"Arc\"]\n    selectors_for_cache_revalidation: SelectorMap<RevalidationSelectorAndHashes>,\n\n    /// A map with all the animations at this `CascadeData`'s origin, indexed\n    /// by name.\n    animations: LayerOrderedMap<KeyframesAnimation>,\n\n    /// A map with all the layer-ordered registrations from style at this `CascadeData`'s origin,\n    /// indexed by name.\n    #[ignore_malloc_size_of = \"Arc\"]\n    custom_property_registrations: LayerOrderedMap<Arc<PropertyRegistration>>,\n\n    /// Custom media query registrations.\n    custom_media: CustomMediaMap,\n\n    /// A map from cascade layer name to layer order.\n    layer_id: FxHashMap<LayerName, LayerId>,\n\n    /// The list of cascade layers, indexed by their layer id.\n    layers: SmallVec<[CascadeLayer; 1]>,\n\n    /// The list of container conditions, indexed by their id.\n    container_conditions: SmallVec<[ContainerConditionReference; 1]>,\n\n    /// The list of scope conditions, indexed by their id.\n    scope_conditions: SmallVec<[ScopeConditionReference; 1]>,\n\n    /// Map of unique selectors on scope start selectors' subjects.\n    scope_subject_map: ScopeSubjectMap,\n\n    /// Effective media query results cached from the last rebuild.\n    effective_media_query_results: EffectiveMediaQueryResults,\n\n    /// Extra data, like different kinds of rules, etc.\n    extra_data: ExtraStyleData,\n\n    /// A monotonically increasing counter to represent the order on which a\n    /// style rule appears in a stylesheet, needed to sort them by source order.\n    rules_source_order: u32,\n\n    /// The total number of selectors.\n    num_selectors: usize,\n\n    /// The total number of declarations.\n    num_declarations: usize,\n}\n\nstatic IMPLICIT_SCOPE: LazyLock<SelectorList<SelectorImpl>> = LazyLock::new(|| {\n    // Implicit scope, as per https://github.com/w3c/csswg-drafts/issues/10196\n    // Also, `&` is `:where(:scope)`, as per https://github.com/w3c/csswg-drafts/issues/9740\n    // ``:where(:scope)` effectively behaves the same as the implicit scope.\n    let list = SelectorList::implicit_scope();\n    list.mark_as_intentionally_leaked();\n    list\n});\n\nfn scope_start_matches_shadow_host(start: &SelectorList<SelectorImpl>) -> bool {\n    // TODO(emilio): Should we carry a MatchesFeaturelessHost rather than a bool around?\n    // Pre-existing behavior with multiple selectors matches this tho.\n    start\n        .slice()\n        .iter()\n        .any(|s| s.matches_featureless_host(true).may_match())\n}\n\n/// Replace any occurrence of parent selector in the given selector with a implicit scope selector.\npub fn replace_parent_selector_with_implicit_scope(\n    selectors: &SelectorList<SelectorImpl>,\n) -> SelectorList<SelectorImpl> {\n    selectors.replace_parent_selector(&IMPLICIT_SCOPE)\n}\n\nimpl CascadeData {\n    /// Creates an empty `CascadeData`.\n    pub fn new() -> Self {\n        Self {\n            normal_rules: ElementAndPseudoRules::default(),\n            featureless_host_rules: None,\n            slotted_rules: None,\n            part_rules: None,\n            invalidation_map: InvalidationMap::new(),\n            relative_selector_invalidation_map: InvalidationMap::new(),\n            additional_relative_selector_invalidation_map:\n                AdditionalRelativeSelectorInvalidationMap::new(),\n            nth_of_mapped_ids: PrecomputedHashSet::default(),\n            nth_of_class_dependencies: PrecomputedHashSet::default(),\n            nth_of_attribute_dependencies: PrecomputedHashSet::default(),\n            nth_of_custom_state_dependencies: PrecomputedHashSet::default(),\n            nth_of_state_dependencies: ElementState::empty(),\n            attribute_dependencies: PrecomputedHashSet::default(),\n            state_dependencies: ElementState::empty(),\n            document_state_dependencies: DocumentState::empty(),\n            mapped_ids: PrecomputedHashSet::default(),\n            selectors_for_cache_revalidation: SelectorMap::new(),\n            animations: Default::default(),\n            custom_property_registrations: Default::default(),\n            custom_media: Default::default(),\n            layer_id: Default::default(),\n            layers: smallvec::smallvec![CascadeLayer::root()],\n            container_conditions: smallvec::smallvec![ContainerConditionReference::none()],\n            scope_conditions: smallvec::smallvec![ScopeConditionReference::none()],\n            scope_subject_map: Default::default(),\n            extra_data: ExtraStyleData::default(),\n            effective_media_query_results: EffectiveMediaQueryResults::new(),\n            rules_source_order: 0,\n            num_selectors: 0,\n            num_declarations: 0,\n        }\n    }\n\n    /// Rebuild the cascade data from a given SheetCollection, incrementally if possible.\n    pub fn rebuild<'a, S>(\n        &mut self,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<(), AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        if !collection.dirty() {\n            return Ok(());\n        }\n\n        let validity = collection.data_validity();\n\n        let mut old_position_try_data = LayerOrderedMap::default();\n        if validity != DataValidity::Valid {\n            old_position_try_data = std::mem::take(&mut self.extra_data.position_try_rules);\n            self.clear_cascade_data();\n            if validity == DataValidity::FullyInvalid {\n                self.clear_invalidation_data();\n            }\n        }\n\n        let mut result = Ok(());\n\n        collection.each(|index, stylesheet, rebuild_kind| {\n            result = self.add_stylesheet(\n                device,\n                quirks_mode,\n                stylesheet,\n                index,\n                guard,\n                rebuild_kind,\n                /* precomputed_pseudo_element_decls = */ None,\n                if validity == DataValidity::Valid {\n                    Some(difference)\n                } else {\n                    None\n                },\n            );\n            result.is_ok()\n        });\n\n        self.did_finish_rebuild();\n\n        // For DataValidity::Valid, we pass the difference down to `add_stylesheet` so that we\n        // populate it with new data. Otherwise we need to diff with the old data.\n        if validity != DataValidity::Valid {\n            difference.update(&old_position_try_data, &self.extra_data.position_try_rules);\n        }\n\n        result\n    }\n\n    /// Returns the custom media query map.\n    pub fn custom_media_map(&self) -> &CustomMediaMap {\n        &self.custom_media\n    }\n\n    /// Returns the invalidation map.\n    pub fn invalidation_map(&self) -> &InvalidationMap {\n        &self.invalidation_map\n    }\n\n    /// Returns the relative selector invalidation map.\n    pub fn relative_selector_invalidation_map(&self) -> &InvalidationMap {\n        &self.relative_selector_invalidation_map\n    }\n\n    /// Returns the relative selector invalidation map data.\n    pub fn relative_invalidation_map_attributes(\n        &self,\n    ) -> &AdditionalRelativeSelectorInvalidationMap {\n        &self.additional_relative_selector_invalidation_map\n    }\n\n    /// Returns whether the given ElementState bit is relied upon by a selector\n    /// of some rule.\n    #[inline]\n    pub fn has_state_dependency(&self, state: ElementState) -> bool {\n        self.state_dependencies.intersects(state)\n    }\n\n    /// Returns whether the given Custom State is relied upon by a selector\n    /// of some rule in the selector list of :nth-child(... of <selector list>).\n    #[inline]\n    pub fn has_nth_of_custom_state_dependency(&self, state: &AtomIdent) -> bool {\n        self.nth_of_custom_state_dependencies.contains(state)\n    }\n\n    /// Returns whether the given ElementState bit is relied upon by a selector\n    /// of some rule in the selector list of :nth-child(... of <selector list>).\n    #[inline]\n    pub fn has_nth_of_state_dependency(&self, state: ElementState) -> bool {\n        self.nth_of_state_dependencies.intersects(state)\n    }\n\n    /// Returns whether the given attribute might appear in an attribute\n    /// selector of some rule.\n    #[inline]\n    pub fn might_have_attribute_dependency(&self, local_name: &LocalName) -> bool {\n        self.attribute_dependencies.contains(local_name)\n    }\n\n    /// Returns whether the given ID might appear in an ID selector in the\n    /// selector list of :nth-child(... of <selector list>).\n    #[inline]\n    pub fn might_have_nth_of_id_dependency(&self, id: &Atom) -> bool {\n        self.nth_of_mapped_ids.contains(id)\n    }\n\n    /// Returns whether the given class might appear in a class selector in the\n    /// selector list of :nth-child(... of <selector list>).\n    #[inline]\n    pub fn might_have_nth_of_class_dependency(&self, class: &Atom) -> bool {\n        self.nth_of_class_dependencies.contains(class)\n    }\n\n    /// Returns whether the given attribute might appear in an attribute\n    /// selector in the selector list of :nth-child(... of <selector list>).\n    #[inline]\n    pub fn might_have_nth_of_attribute_dependency(&self, local_name: &LocalName) -> bool {\n        self.nth_of_attribute_dependencies.contains(local_name)\n    }\n\n    /// Returns the normal rule map for a given pseudo-element.\n    #[inline]\n    pub fn normal_rules(&self, pseudo_elements: &[PseudoElement]) -> Option<&SelectorMap<Rule>> {\n        self.normal_rules.rules(pseudo_elements)\n    }\n\n    /// Returns the featureless pseudo rule map for a given pseudo-element.\n    #[inline]\n    pub fn featureless_host_rules(\n        &self,\n        pseudo_elements: &[PseudoElement],\n    ) -> Option<&SelectorMap<Rule>> {\n        self.featureless_host_rules\n            .as_ref()\n            .and_then(|d| d.rules(pseudo_elements))\n    }\n\n    /// Whether there's any featureless rule that could match in this scope.\n    pub fn any_featureless_host_rules(&self) -> bool {\n        self.featureless_host_rules.is_some()\n    }\n\n    /// Returns the slotted rule map for a given pseudo-element.\n    #[inline]\n    pub fn slotted_rules(&self, pseudo_elements: &[PseudoElement]) -> Option<&SelectorMap<Rule>> {\n        self.slotted_rules\n            .as_ref()\n            .and_then(|d| d.rules(pseudo_elements))\n    }\n\n    /// Whether there's any ::slotted rule that could match in this scope.\n    pub fn any_slotted_rule(&self) -> bool {\n        self.slotted_rules.is_some()\n    }\n\n    /// Returns the parts rule map for a given pseudo-element.\n    #[inline]\n    pub fn part_rules(&self, pseudo_elements: &[PseudoElement]) -> Option<&PartMap> {\n        self.part_rules\n            .as_ref()\n            .and_then(|d| d.rules(pseudo_elements))\n    }\n\n    /// Whether there's any ::part rule that could match in this scope.\n    pub fn any_part_rule(&self) -> bool {\n        self.part_rules.is_some()\n    }\n\n    #[inline]\n    fn layer_order_for(&self, id: LayerId) -> LayerOrder {\n        self.layers[id.0 as usize].order\n    }\n\n    pub(crate) fn container_condition_matches<E>(\n        &self,\n        mut id: ContainerConditionId,\n        stylist: &Stylist,\n        element: E,\n        context: &mut MatchingContext<E::Impl>,\n    ) -> bool\n    where\n        E: TElement,\n    {\n        loop {\n            let condition_ref = &self.container_conditions[id.0 as usize];\n            if condition_ref.conditions.is_empty() {\n                return true;\n            }\n            let matches = condition_ref.conditions.iter().any(|condition| {\n                condition\n                    .matches(\n                        stylist,\n                        element,\n                        context.extra_data.originating_element_style,\n                        &mut context.extra_data.cascade_input_flags,\n                    )\n                    .to_bool(/* unknown = */ false)\n            });\n            if !matches {\n                return false;\n            }\n            id = condition_ref.parent;\n        }\n    }\n\n    pub(crate) fn find_scope_proximity_if_matching<E: TElement>(\n        &self,\n        rule: &Rule,\n        element: E,\n        context: &mut MatchingContext<E::Impl>,\n    ) -> ScopeProximity {\n        context\n            .extra_data\n            .cascade_input_flags\n            .insert(ComputedValueFlags::CONSIDERED_NONTRIVIAL_SCOPED_STYLE);\n\n        // Whether the scope root matches a shadow host mostly olny depends on scope-intrinsic\n        // parameters (i.e. bounds/implicit scope) - except for the use of `::parts`, where\n        // matching crosses the shadow boundary.\n        let result = scope_root_candidates(\n            &self.scope_conditions,\n            rule.scope_condition_id,\n            &element,\n            rule.selector.is_part(),\n            &self.scope_subject_map,\n            context,\n        );\n        for candidate in result.candidates {\n            if context.nest_for_scope(Some(candidate.root), |context| {\n                matches_selector(&rule.selector, 0, Some(&rule.hashes), &element, context)\n            }) {\n                return candidate.proximity;\n            }\n        }\n        ScopeProximity::infinity()\n    }\n\n    fn did_finish_rebuild(&mut self) {\n        self.shrink_maps_if_needed();\n        self.compute_layer_order();\n    }\n\n    fn shrink_maps_if_needed(&mut self) {\n        self.normal_rules.shrink_if_needed();\n        if let Some(ref mut host_rules) = self.featureless_host_rules {\n            host_rules.shrink_if_needed();\n        }\n        if let Some(ref mut slotted_rules) = self.slotted_rules {\n            slotted_rules.shrink_if_needed();\n        }\n        self.animations.shrink_if_needed();\n        self.custom_property_registrations.shrink_if_needed();\n        self.invalidation_map.shrink_if_needed();\n        self.relative_selector_invalidation_map.shrink_if_needed();\n        self.additional_relative_selector_invalidation_map\n            .shrink_if_needed();\n        self.attribute_dependencies.shrink_if_needed();\n        self.nth_of_attribute_dependencies.shrink_if_needed();\n        self.nth_of_custom_state_dependencies.shrink_if_needed();\n        self.nth_of_class_dependencies.shrink_if_needed();\n        self.nth_of_mapped_ids.shrink_if_needed();\n        self.mapped_ids.shrink_if_needed();\n        self.layer_id.shrink_if_needed();\n        self.selectors_for_cache_revalidation.shrink_if_needed();\n        self.scope_subject_map.shrink_if_needed();\n    }\n\n    fn compute_layer_order(&mut self) {\n        debug_assert_ne!(\n            self.layers.len(),\n            0,\n            \"There should be at least the root layer!\"\n        );\n        if self.layers.len() == 1 {\n            return; // Nothing to do\n        }\n        let (first, remaining) = self.layers.split_at_mut(1);\n        let root = &mut first[0];\n        let mut order = LayerOrder::first();\n        compute_layer_order_for_subtree(root, remaining, &mut order);\n\n        // NOTE(emilio): This is a bit trickier than it should to avoid having\n        // to clone() around layer indices.\n        fn compute_layer_order_for_subtree(\n            parent: &mut CascadeLayer,\n            remaining_layers: &mut [CascadeLayer],\n            order: &mut LayerOrder,\n        ) {\n            for child in parent.children.iter() {\n                debug_assert!(\n                    parent.id < *child,\n                    \"Children are always registered after parents\"\n                );\n                let child_index = (child.0 - parent.id.0 - 1) as usize;\n                let (first, remaining) = remaining_layers.split_at_mut(child_index + 1);\n                let child = &mut first[child_index];\n                compute_layer_order_for_subtree(child, remaining, order);\n            }\n\n            if parent.id != LayerId::root() {\n                parent.order = *order;\n                order.inc();\n            }\n        }\n        self.extra_data.sort_by_layer(&self.layers);\n        self.animations\n            .sort_with(&self.layers, compare_keyframes_in_same_layer);\n        self.custom_property_registrations.sort(&self.layers)\n    }\n\n    /// Collects all the applicable media query results into `results`.\n    ///\n    /// This duplicates part of the logic in `add_stylesheet`, which is\n    /// a bit unfortunate.\n    ///\n    /// FIXME(emilio): With a bit of smartness in\n    /// `media_feature_affected_matches`, we could convert\n    /// `EffectiveMediaQueryResults` into a vector without too much effort.\n    fn collect_applicable_media_query_results_into<S>(\n        device: &Device,\n        stylesheet: &S,\n        guard: &SharedRwLockReadGuard,\n        results: &mut Vec<MediaListKey>,\n        contents_list: &mut StyleSheetContentList,\n        custom_media_map: &mut CustomMediaMap,\n    ) where\n        S: StylesheetInDocument + 'static,\n    {\n        if !stylesheet.enabled() {\n            return;\n        }\n        if !stylesheet.is_effective_for_device(device, &custom_media_map, guard) {\n            return;\n        }\n\n        debug!(\" + {:?}\", stylesheet);\n        let contents = stylesheet.contents(guard);\n        results.push(contents.to_media_list_key());\n\n        // Safety: StyleSheetContents are reference-counted with Arc.\n        contents_list.push(StylesheetContentsPtr(unsafe {\n            Arc::from_raw_addrefed(&*contents)\n        }));\n\n        let mut iter = stylesheet\n            .contents(guard)\n            .effective_rules(device, custom_media_map, guard);\n        while let Some(rule) = iter.next() {\n            match *rule {\n                CssRule::CustomMedia(ref custom_media) => {\n                    iter.custom_media()\n                        .insert(custom_media.name.0.clone(), custom_media.condition.clone());\n                },\n                CssRule::Import(ref lock) => {\n                    let import_rule = lock.read_with(guard);\n                    debug!(\" + {:?}\", import_rule.stylesheet.media(guard));\n                    results.push(import_rule.to_media_list_key());\n                },\n                CssRule::Media(ref media_rule) => {\n                    debug!(\" + {:?}\", media_rule.media_queries.read_with(guard));\n                    results.push(media_rule.to_media_list_key());\n                },\n                _ => {},\n            }\n        }\n    }\n\n    fn add_styles(\n        &mut self,\n        selectors: &SelectorList<SelectorImpl>,\n        declarations: &Arc<Locked<PropertyDeclarationBlock>>,\n        ancestor_selectors: Option<&SelectorList<SelectorImpl>>,\n        containing_rule_state: &ContainingRuleState,\n        mut replaced_selectors: Option<&mut ReplacedSelectors>,\n        guard: &SharedRwLockReadGuard,\n        rebuild_kind: SheetRebuildKind,\n        mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,\n        quirks_mode: QuirksMode,\n        mut collected_scope_dependencies: Option<&mut Vec<Dependency>>,\n    ) -> Result<(), AllocErr> {\n        self.num_declarations += declarations.read_with(guard).len();\n        for selector in selectors.slice() {\n            self.num_selectors += 1;\n\n            let pseudo_elements = selector.pseudo_elements();\n            let inner_pseudo_element = pseudo_elements.get(0);\n            if let Some(pseudo) = inner_pseudo_element {\n                if pseudo.is_precomputed() {\n                    debug_assert!(selector.is_universal());\n                    debug_assert!(ancestor_selectors.is_none());\n                    debug_assert_eq!(containing_rule_state.layer_id, LayerId::root());\n                    // Because we precompute pseudos, we cannot possibly calculate scope proximity.\n                    debug_assert!(!containing_rule_state.scope_is_effective());\n                    precomputed_pseudo_element_decls\n                        .as_mut()\n                        .expect(\"Expected precomputed declarations for the UA level\")\n                        .get_or_insert_with(pseudo, Vec::new)\n                        .push(ApplicableDeclarationBlock::new(\n                            StyleSource::from_declarations(declarations.clone()),\n                            self.rules_source_order,\n                            CascadeLevel::new(CascadeOrigin::UA),\n                            selector.specificity(),\n                            LayerOrder::root(),\n                            ScopeProximity::infinity(),\n                            RuleCascadeFlags::empty(),\n                        ));\n                    continue;\n                }\n                if pseudo_elements\n                    .iter()\n                    .any(|p| p.is_unknown_webkit_pseudo_element())\n                {\n                    continue;\n                }\n            }\n\n            debug_assert!(!pseudo_elements\n                .iter()\n                .any(|p| p.is_precomputed() || p.is_unknown_webkit_pseudo_element()));\n\n            let selector = match ancestor_selectors {\n                Some(ref s) => selector.replace_parent_selector(&s),\n                None => selector.clone(),\n            };\n\n            let hashes = AncestorHashes::new(&selector, quirks_mode);\n\n            let rule = Rule::new(\n                selector,\n                hashes,\n                StyleSource::from_declarations(declarations.clone()),\n                self.rules_source_order,\n                containing_rule_state.layer_id,\n                containing_rule_state.container_condition_id,\n                containing_rule_state.cascade_flags(),\n                containing_rule_state.containing_scope_rule_state.id,\n            );\n\n            if let Some(ref mut replaced_selectors) = replaced_selectors {\n                replaced_selectors.push(rule.selector.clone())\n            }\n\n            if rebuild_kind.should_rebuild_invalidation() {\n                let mut scope_dependencies = note_selector_for_invalidation(\n                    &rule.selector,\n                    quirks_mode,\n                    &mut self.invalidation_map,\n                    &mut self.relative_selector_invalidation_map,\n                    &mut self.additional_relative_selector_invalidation_map,\n                    None,\n                    None,\n                )?;\n                let mut needs_revalidation = false;\n                let mut visitor = StylistSelectorVisitor {\n                    needs_revalidation: &mut needs_revalidation,\n                    passed_rightmost_selector: false,\n                    in_selector_list_of: SelectorListKind::default(),\n                    mapped_ids: &mut self.mapped_ids,\n                    nth_of_mapped_ids: &mut self.nth_of_mapped_ids,\n                    attribute_dependencies: &mut self.attribute_dependencies,\n                    nth_of_class_dependencies: &mut self.nth_of_class_dependencies,\n                    nth_of_attribute_dependencies: &mut self.nth_of_attribute_dependencies,\n                    nth_of_custom_state_dependencies: &mut self.nth_of_custom_state_dependencies,\n                    state_dependencies: &mut self.state_dependencies,\n                    nth_of_state_dependencies: &mut self.nth_of_state_dependencies,\n                    document_state_dependencies: &mut self.document_state_dependencies,\n                };\n                rule.selector.visit(&mut visitor);\n\n                if needs_revalidation {\n                    self.selectors_for_cache_revalidation.insert(\n                        RevalidationSelectorAndHashes::new(\n                            rule.selector.clone(),\n                            rule.hashes.clone(),\n                        ),\n                        quirks_mode,\n                    )?;\n                }\n\n                match (\n                    scope_dependencies.as_mut(),\n                    collected_scope_dependencies.as_mut(),\n                ) {\n                    (Some(inner_scope_deps), Some(scope_deps)) => {\n                        scope_deps.append(inner_scope_deps)\n                    },\n                    _ => {},\n                }\n            }\n\n            // Part is special, since given it doesn't have any\n            // selectors inside, it's not worth using a whole\n            // SelectorMap for it.\n            if let Some(parts) = rule.selector.parts() {\n                // ::part() has all semantics, so we just need to\n                // put any of them in the selector map.\n                //\n                // We choose the last one quite arbitrarily,\n                // expecting it's slightly more likely to be more\n                // specific.\n                let map = self\n                    .part_rules\n                    .get_or_insert_with(|| Box::new(Default::default()))\n                    .for_insertion(&pseudo_elements);\n                map.try_reserve(1)?;\n                let vec = map.entry(parts.last().unwrap().clone().0).or_default();\n                vec.try_reserve(1)?;\n                vec.push(rule);\n            } else {\n                let scope_matches_shadow_host = containing_rule_state\n                    .containing_scope_rule_state\n                    .matches_shadow_host\n                    == ScopeMatchesShadowHost::Yes;\n                let matches_featureless_host_only = match rule\n                    .selector\n                    .matches_featureless_host(scope_matches_shadow_host)\n                {\n                    MatchesFeaturelessHost::Only => true,\n                    MatchesFeaturelessHost::Yes => {\n                        // We need to insert this in featureless_host_rules but also normal_rules.\n                        self.featureless_host_rules\n                            .get_or_insert_with(|| Box::new(Default::default()))\n                            .for_insertion(&pseudo_elements)\n                            .insert(rule.clone(), quirks_mode)?;\n                        false\n                    },\n                    MatchesFeaturelessHost::Never => false,\n                };\n\n                // NOTE(emilio): It's fine to look at :host and then at\n                // ::slotted(..), since :host::slotted(..) could never\n                // possibly match, as <slot> is not a valid shadow host.\n                // :scope may match featureless shadow host if the scope\n                // root is the shadow root.\n                // See https://github.com/w3c/csswg-drafts/issues/9025\n                let rules = if matches_featureless_host_only {\n                    self.featureless_host_rules\n                        .get_or_insert_with(|| Box::new(Default::default()))\n                } else if rule.selector.is_slotted() {\n                    self.slotted_rules\n                        .get_or_insert_with(|| Box::new(Default::default()))\n                } else {\n                    &mut self.normal_rules\n                }\n                .for_insertion(&pseudo_elements);\n                rules.insert(rule, quirks_mode)?;\n            }\n        }\n        self.rules_source_order += 1;\n        Ok(())\n    }\n\n    fn add_rule_list<S>(\n        &mut self,\n        rules: std::slice::Iter<CssRule>,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        stylesheet: &S,\n        sheet_index: usize,\n        guard: &SharedRwLockReadGuard,\n        rebuild_kind: SheetRebuildKind,\n        containing_rule_state: &mut ContainingRuleState,\n        mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,\n        mut difference: Option<&mut CascadeDataDifference>,\n    ) -> Result<(), AllocErr>\n    where\n        S: StylesheetInDocument + 'static,\n    {\n        for rule in rules {\n            // Handle leaf rules first, as those are by far the most common\n            // ones, and are always effective, so we can skip some checks.\n            let mut handled = true;\n            let mut list_for_nested_rules = None;\n            match *rule {\n                CssRule::Style(ref locked) => {\n                    let style_rule = locked.read_with(guard);\n                    let has_nested_rules = style_rule.rules.is_some();\n                    let mut replaced_selectors = ReplacedSelectors::new();\n                    let ancestor_selectors = containing_rule_state.ancestor_selector_lists.last();\n                    let collect_replaced_selectors =\n                        has_nested_rules && ancestor_selectors.is_some();\n                    let mut inner_dependencies: Option<Vec<Dependency>> = containing_rule_state\n                        .scope_is_effective()\n                        .then(|| Vec::new());\n                    self.add_styles(\n                        &style_rule.selectors,\n                        &style_rule.block,\n                        ancestor_selectors,\n                        containing_rule_state,\n                        if collect_replaced_selectors {\n                            Some(&mut replaced_selectors)\n                        } else {\n                            None\n                        },\n                        guard,\n                        rebuild_kind,\n                        precomputed_pseudo_element_decls.as_deref_mut(),\n                        quirks_mode,\n                        inner_dependencies.as_mut(),\n                    )?;\n                    if let Some(mut scope_dependencies) = inner_dependencies {\n                        containing_rule_state\n                            .containing_scope_rule_state\n                            .inner_dependencies\n                            .append(&mut scope_dependencies);\n                    }\n                    if has_nested_rules {\n                        handled = false;\n                        list_for_nested_rules = Some(if collect_replaced_selectors {\n                            SelectorList::from_iter(replaced_selectors.drain(..))\n                        } else {\n                            style_rule.selectors.clone()\n                        });\n                    }\n                },\n                CssRule::NestedDeclarations(ref rule) => {\n                    if let Some(ref ancestor_selectors) =\n                        containing_rule_state.ancestor_selector_lists.last()\n                    {\n                        let decls = &rule.read_with(guard).block;\n                        let selectors = match containing_rule_state.nested_declarations_context {\n                            NestedDeclarationsContext::Style => ancestor_selectors,\n                            NestedDeclarationsContext::Scope => &*IMPLICIT_SCOPE,\n                        };\n                        let mut inner_dependencies: Option<Vec<Dependency>> = containing_rule_state\n                            .scope_is_effective()\n                            .then(|| Vec::new());\n                        self.add_styles(\n                            selectors,\n                            decls,\n                            /* ancestor_selectors = */ None,\n                            containing_rule_state,\n                            /* replaced_selectors = */ None,\n                            guard,\n                            // We don't need to rebuild invalidation data, since our ancestor style\n                            // rule would've done this.\n                            SheetRebuildKind::CascadeOnly,\n                            precomputed_pseudo_element_decls.as_deref_mut(),\n                            quirks_mode,\n                            inner_dependencies.as_mut(),\n                        )?;\n                        if let Some(mut scope_dependencies) = inner_dependencies {\n                            containing_rule_state\n                                .containing_scope_rule_state\n                                .inner_dependencies\n                                .append(&mut scope_dependencies);\n                        }\n                    }\n                },\n                CssRule::Keyframes(ref keyframes_rule) => {\n                    debug!(\"Found valid keyframes rule: {:?}\", *keyframes_rule);\n                    let keyframes_rule = keyframes_rule.read_with(guard);\n                    let name = keyframes_rule.name.as_atom().clone();\n                    let animation = KeyframesAnimation::from_keyframes(\n                        &keyframes_rule.keyframes,\n                        keyframes_rule.vendor_prefix.clone(),\n                        guard,\n                    );\n                    self.animations.try_insert_with(\n                        name,\n                        animation,\n                        containing_rule_state.layer_id,\n                        compare_keyframes_in_same_layer,\n                    )?;\n                },\n                CssRule::Property(ref registration) => {\n                    self.custom_property_registrations.try_insert(\n                        registration.name.0.clone(),\n                        Arc::clone(registration),\n                        containing_rule_state.layer_id,\n                    )?;\n                },\n                CssRule::FontFace(ref rule) => {\n                    // NOTE(emilio): We don't care about container_condition_id\n                    // because:\n                    //\n                    //     Global, name-defining at-rules such as @keyframes or\n                    //     @font-face or @layer that are defined inside container\n                    //     queries are not constrained by the container query\n                    //     conditions.\n                    //\n                    // https://drafts.csswg.org/css-contain-3/#container-rule\n                    // (Same elsewhere)\n                    self.extra_data\n                        .add_font_face(rule, containing_rule_state.layer_id);\n                },\n                CssRule::FontFeatureValues(ref rule) => {\n                    self.extra_data\n                        .add_font_feature_values(rule, containing_rule_state.layer_id);\n                },\n                CssRule::FontPaletteValues(ref rule) => {\n                    self.extra_data\n                        .add_font_palette_values(rule, containing_rule_state.layer_id);\n                },\n                CssRule::CounterStyle(ref rule) => {\n                    self.extra_data.add_counter_style(\n                        guard,\n                        rule,\n                        containing_rule_state.layer_id,\n                    )?;\n                },\n                CssRule::PositionTry(ref rule) => {\n                    let name = rule.read_with(guard).name.0.clone();\n                    if let Some(ref mut difference) = difference {\n                        difference.changed_position_try_names.insert(name.clone());\n                    }\n                    self.extra_data.add_position_try(\n                        name,\n                        rule.clone(),\n                        containing_rule_state.layer_id,\n                    )?;\n                },\n                CssRule::Page(ref rule) => {\n                    self.extra_data\n                        .add_page(guard, rule, containing_rule_state.layer_id)?;\n                    handled = false;\n                },\n                CssRule::ViewTransition(ref rule) => {\n                    self.extra_data\n                        .add_view_transition(rule, containing_rule_state.layer_id);\n                },\n                _ => {\n                    handled = false;\n                },\n            }\n\n            if handled {\n                // Assert that there are no children, and that the rule is\n                // effective.\n                if cfg!(debug_assertions) {\n                    let mut effective = false;\n                    let children = EffectiveRulesIterator::<&CustomMediaMap>::children(\n                        rule,\n                        device,\n                        quirks_mode,\n                        &self.custom_media,\n                        guard,\n                        &mut effective,\n                    );\n                    debug_assert!(children.is_empty());\n                    debug_assert!(effective);\n                }\n                continue;\n            }\n\n            let mut effective = false;\n            let children = EffectiveRulesIterator::<&CustomMediaMap>::children(\n                rule,\n                device,\n                quirks_mode,\n                &self.custom_media,\n                guard,\n                &mut effective,\n            );\n            if !effective {\n                continue;\n            }\n\n            fn maybe_register_layer(data: &mut CascadeData, layer: &LayerName) -> LayerId {\n                // TODO: Measure what's more common / expensive, if\n                // layer.clone() or the double hash lookup in the insert\n                // case.\n                if let Some(id) = data.layer_id.get(layer) {\n                    return *id;\n                }\n                let id = LayerId(data.layers.len() as u16);\n\n                let parent_layer_id = if layer.layer_names().len() > 1 {\n                    let mut parent = layer.clone();\n                    parent.0.pop();\n\n                    *data\n                        .layer_id\n                        .get_mut(&parent)\n                        .expect(\"Parent layers should be registered before child layers\")\n                } else {\n                    LayerId::root()\n                };\n\n                data.layers[parent_layer_id.0 as usize].children.push(id);\n                data.layers.push(CascadeLayer {\n                    id,\n                    // NOTE(emilio): Order is evaluated after rebuild in\n                    // compute_layer_order.\n                    order: LayerOrder::first(),\n                    children: vec![],\n                });\n\n                data.layer_id.insert(layer.clone(), id);\n\n                id\n            }\n\n            fn maybe_register_layers(\n                data: &mut CascadeData,\n                name: Option<&LayerName>,\n                containing_rule_state: &mut ContainingRuleState,\n            ) {\n                let anon_name;\n                let name = match name {\n                    Some(name) => name,\n                    None => {\n                        anon_name = LayerName::new_anonymous();\n                        &anon_name\n                    },\n                };\n                for name in name.layer_names() {\n                    containing_rule_state.layer_name.0.push(name.clone());\n                    containing_rule_state.layer_id =\n                        maybe_register_layer(data, &containing_rule_state.layer_name);\n                }\n                debug_assert_ne!(containing_rule_state.layer_id, LayerId::root());\n            }\n\n            let saved_containing_rule_state = containing_rule_state.save();\n            match *rule {\n                CssRule::Import(ref lock) => {\n                    let import_rule = lock.read_with(guard);\n                    if rebuild_kind.should_rebuild_invalidation() {\n                        self.effective_media_query_results\n                            .saw_effective(import_rule);\n                    }\n                    match import_rule.layer {\n                        ImportLayer::Named(ref name) => {\n                            maybe_register_layers(self, Some(name), containing_rule_state)\n                        },\n                        ImportLayer::Anonymous => {\n                            maybe_register_layers(self, None, containing_rule_state)\n                        },\n                        ImportLayer::None => {},\n                    }\n                },\n                CssRule::Media(ref media_rule) => {\n                    if rebuild_kind.should_rebuild_invalidation() {\n                        self.effective_media_query_results\n                            .saw_effective(&**media_rule);\n                    }\n                },\n                CssRule::LayerBlock(ref rule) => {\n                    maybe_register_layers(self, rule.name.as_ref(), containing_rule_state);\n                },\n                CssRule::CustomMedia(ref custom_media) => {\n                    self.custom_media\n                        .insert(custom_media.name.0.clone(), custom_media.condition.clone());\n                },\n                CssRule::LayerStatement(ref rule) => {\n                    for name in &*rule.names {\n                        maybe_register_layers(self, Some(name), containing_rule_state);\n                        // Register each layer individually.\n                        containing_rule_state.restore(&saved_containing_rule_state);\n                    }\n                },\n                CssRule::Style(..) => {\n                    containing_rule_state.nested_declarations_context =\n                        NestedDeclarationsContext::Style;\n                    if let Some(s) = list_for_nested_rules {\n                        containing_rule_state.ancestor_selector_lists.push(s);\n                    }\n                },\n                CssRule::Container(ref rule) => {\n                    let id = ContainerConditionId(self.container_conditions.len() as u16);\n                    let condition = ContainerConditionReference {\n                        parent: containing_rule_state.container_condition_id,\n                        conditions: rule.conditions.0.clone(),\n                    };\n                    self.container_conditions.push(condition);\n                    containing_rule_state.container_condition_id = id;\n                },\n                CssRule::StartingStyle(..) => {\n                    containing_rule_state\n                        .cascade_flags\n                        .insert(RuleCascadeFlags::STARTING_STYLE);\n                },\n                CssRule::AppearanceBase(..) => {\n                    containing_rule_state\n                        .cascade_flags\n                        .insert(RuleCascadeFlags::APPEARANCE_BASE);\n                },\n                CssRule::Scope(ref rule) => {\n                    containing_rule_state.nested_declarations_context =\n                        NestedDeclarationsContext::Scope;\n                    let id = ScopeConditionId(self.scope_conditions.len() as u16);\n                    let mut matches_shadow_host = false;\n                    let implicit_scope_root = if let Some(start) = rule.bounds.start.as_ref() {\n                        matches_shadow_host = scope_start_matches_shadow_host(start);\n                        // Would be unused, but use the default as fallback.\n                        StylistImplicitScopeRoot::default()\n                    } else {\n                        // (Re)Moving stylesheets trigger a complete flush, so saving the implicit\n                        // root here should be safe.\n                        if let Some(root) = stylesheet.implicit_scope_root() {\n                            matches_shadow_host = root.matches_shadow_host();\n                            match root {\n                                ImplicitScopeRoot::InLightTree(_)\n                                | ImplicitScopeRoot::Constructed\n                                | ImplicitScopeRoot::DocumentElement => {\n                                    StylistImplicitScopeRoot::Normal(root)\n                                },\n                                ImplicitScopeRoot::ShadowHost(_)\n                                | ImplicitScopeRoot::InShadowTree(_) => {\n                                    // Style data can be shared between shadow trees, so we must\n                                    // query the implicit root for that specific tree.\n                                    // Shared stylesheet means shared sheet indices, so we can\n                                    // use that to locate the implicit root.\n                                    // Technically, this can also be applied to the light tree,\n                                    // but that requires also knowing about what cascade level we're at.\n                                    StylistImplicitScopeRoot::Cached(sheet_index)\n                                },\n                            }\n                        } else {\n                            // Could not find implicit scope root, but use the default as fallback.\n                            StylistImplicitScopeRoot::default()\n                        }\n                    };\n\n                    let replaced =\n                        {\n                            let start = rule.bounds.start.as_ref().map(|selector| {\n                                match containing_rule_state.ancestor_selector_lists.last() {\n                                    Some(s) => selector.replace_parent_selector(s),\n                                    None => selector.clone(),\n                                }\n                            });\n                            let implicit_scope_selector = &*IMPLICIT_SCOPE;\n                            let end = rule.bounds.end.as_ref().map(|selector| {\n                                selector.replace_parent_selector(implicit_scope_selector)\n                            });\n                            containing_rule_state\n                                .ancestor_selector_lists\n                                .push(implicit_scope_selector.clone());\n                            ScopeBoundsWithHashes::new(quirks_mode, start, end)\n                        };\n\n                    if let Some(selectors) = replaced.start.as_ref() {\n                        self.scope_subject_map\n                            .add_bound_start(&selectors.selectors, quirks_mode);\n                    }\n\n                    let is_trivial = replaced.is_trivial();\n                    self.scope_conditions.push(ScopeConditionReference {\n                        parent: containing_rule_state.containing_scope_rule_state.id,\n                        condition: Some(replaced),\n                        implicit_scope_root,\n                        is_trivial,\n                    });\n\n                    containing_rule_state\n                        .containing_scope_rule_state\n                        .matches_shadow_host\n                        .nest_for_scope(matches_shadow_host);\n                    containing_rule_state.containing_scope_rule_state.id = id;\n                    containing_rule_state\n                        .containing_scope_rule_state\n                        .inner_dependencies\n                        .reserve(children.iter().len());\n                },\n                // We don't care about any other rule.\n                _ => {},\n            }\n\n            if !children.is_empty() {\n                self.add_rule_list(\n                    children.iter(),\n                    device,\n                    quirks_mode,\n                    stylesheet,\n                    sheet_index,\n                    guard,\n                    rebuild_kind,\n                    containing_rule_state,\n                    precomputed_pseudo_element_decls.as_deref_mut(),\n                    difference.as_deref_mut(),\n                )?;\n            }\n\n            if let Some(scope_restore_data) =\n                containing_rule_state.restore(&saved_containing_rule_state)\n            {\n                let (cur_scope_inner_dependencies, scope_idx) = scope_restore_data;\n                let cur_scope = &self.scope_conditions[scope_idx.0 as usize];\n                if let Some(cond) = cur_scope.condition.as_ref() {\n                    let mut _unused = false;\n                    let visitor = StylistSelectorVisitor {\n                        needs_revalidation: &mut _unused,\n                        passed_rightmost_selector: true,\n                        in_selector_list_of: SelectorListKind::default(),\n                        mapped_ids: &mut self.mapped_ids,\n                        nth_of_mapped_ids: &mut self.nth_of_mapped_ids,\n                        attribute_dependencies: &mut self.attribute_dependencies,\n                        nth_of_class_dependencies: &mut self.nth_of_class_dependencies,\n                        nth_of_attribute_dependencies: &mut self.nth_of_attribute_dependencies,\n                        nth_of_custom_state_dependencies: &mut self\n                            .nth_of_custom_state_dependencies,\n                        state_dependencies: &mut self.state_dependencies,\n                        nth_of_state_dependencies: &mut self.nth_of_state_dependencies,\n                        document_state_dependencies: &mut self.document_state_dependencies,\n                    };\n\n                    let dependency_vector = build_scope_dependencies(\n                        quirks_mode,\n                        cur_scope_inner_dependencies,\n                        visitor,\n                        cond,\n                        &mut self.invalidation_map,\n                        &mut self.relative_selector_invalidation_map,\n                        &mut self.additional_relative_selector_invalidation_map,\n                    )?;\n\n                    containing_rule_state\n                        .containing_scope_rule_state\n                        .inner_dependencies\n                        .extend(dependency_vector);\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    // Returns Err(..) to signify OOM\n    fn add_stylesheet<S>(\n        &mut self,\n        device: &Device,\n        quirks_mode: QuirksMode,\n        stylesheet: &S,\n        sheet_index: usize,\n        guard: &SharedRwLockReadGuard,\n        rebuild_kind: SheetRebuildKind,\n        mut precomputed_pseudo_element_decls: Option<&mut PrecomputedPseudoElementDeclarations>,\n        mut difference: Option<&mut CascadeDataDifference>,\n    ) -> Result<(), AllocErr>\n    where\n        S: StylesheetInDocument + 'static,\n    {\n        if !stylesheet.enabled() {\n            return Ok(());\n        }\n\n        if !stylesheet.is_effective_for_device(device, &self.custom_media, guard) {\n            return Ok(());\n        }\n\n        let contents = stylesheet.contents(guard);\n        if rebuild_kind.should_rebuild_invalidation() {\n            self.effective_media_query_results.saw_effective(&*contents);\n        }\n\n        let mut state = ContainingRuleState::default();\n        self.add_rule_list(\n            contents.rules(guard).iter(),\n            device,\n            quirks_mode,\n            stylesheet,\n            sheet_index,\n            guard,\n            rebuild_kind,\n            &mut state,\n            precomputed_pseudo_element_decls.as_deref_mut(),\n            difference.as_deref_mut(),\n        )?;\n\n        Ok(())\n    }\n\n    /// Returns whether all the media-feature affected values matched before and\n    /// match now in the given stylesheet.\n    pub fn media_feature_affected_matches<S>(\n        &self,\n        stylesheet: &S,\n        guard: &SharedRwLockReadGuard,\n        device: &Device,\n        quirks_mode: QuirksMode,\n    ) -> bool\n    where\n        S: StylesheetInDocument + 'static,\n    {\n        use crate::invalidation::media_queries::PotentiallyEffectiveMediaRules;\n\n        let effective_now = stylesheet.is_effective_for_device(device, &self.custom_media, guard);\n\n        let contents = stylesheet.contents(guard);\n        let effective_then = self.effective_media_query_results.was_effective(contents);\n\n        if effective_now != effective_then {\n            debug!(\n                \" > Stylesheet {:?} changed -> {}, {}\",\n                stylesheet.media(guard),\n                effective_then,\n                effective_now\n            );\n            return false;\n        }\n\n        if !effective_now {\n            return true;\n        }\n\n        // We don't need a custom media map for PotentiallyEffectiveMediaRules.\n        let custom_media = CustomMediaMap::default();\n        let mut iter =\n            contents.iter_rules::<PotentiallyEffectiveMediaRules, _>(device, &custom_media, guard);\n        while let Some(rule) = iter.next() {\n            match *rule {\n                CssRule::Style(..)\n                | CssRule::NestedDeclarations(..)\n                | CssRule::Namespace(..)\n                | CssRule::FontFace(..)\n                | CssRule::Container(..)\n                | CssRule::CounterStyle(..)\n                | CssRule::Supports(..)\n                | CssRule::Keyframes(..)\n                | CssRule::Margin(..)\n                | CssRule::Page(..)\n                | CssRule::Property(..)\n                | CssRule::Document(..)\n                | CssRule::LayerBlock(..)\n                | CssRule::LayerStatement(..)\n                | CssRule::FontPaletteValues(..)\n                | CssRule::FontFeatureValues(..)\n                | CssRule::Scope(..)\n                | CssRule::StartingStyle(..)\n                | CssRule::AppearanceBase(..)\n                | CssRule::CustomMedia(..)\n                | CssRule::PositionTry(..)\n                | CssRule::ViewTransition(..) => {\n                    // Not affected by device changes. @custom-media is handled by the potential\n                    // @media rules referencing it being handled.\n                    continue;\n                },\n                CssRule::Import(ref lock) => {\n                    let import_rule = lock.read_with(guard);\n                    let effective_now = match import_rule.stylesheet.media(guard) {\n                        Some(m) => m.evaluate(\n                            device,\n                            quirks_mode,\n                            &mut CustomMediaEvaluator::new(&self.custom_media, guard),\n                        ),\n                        None => true,\n                    };\n                    let effective_then = self\n                        .effective_media_query_results\n                        .was_effective(import_rule);\n                    if effective_now != effective_then {\n                        debug!(\n                            \" > @import rule {:?} changed {} -> {}\",\n                            import_rule.stylesheet.media(guard),\n                            effective_then,\n                            effective_now\n                        );\n                        return false;\n                    }\n\n                    if !effective_now {\n                        iter.skip_children();\n                    }\n                },\n                CssRule::Media(ref media_rule) => {\n                    let mq = media_rule.media_queries.read_with(guard);\n                    let effective_now = mq.evaluate(\n                        device,\n                        quirks_mode,\n                        &mut CustomMediaEvaluator::new(&self.custom_media, guard),\n                    );\n                    let effective_then = self\n                        .effective_media_query_results\n                        .was_effective(&**media_rule);\n\n                    if effective_now != effective_then {\n                        debug!(\n                            \" > @media rule {:?} changed {} -> {}\",\n                            mq, effective_then, effective_now\n                        );\n                        return false;\n                    }\n\n                    if !effective_now {\n                        iter.skip_children();\n                    }\n                },\n            }\n        }\n\n        true\n    }\n\n    /// Returns the custom properties map.\n    pub fn custom_property_registrations(&self) -> &LayerOrderedMap<Arc<PropertyRegistration>> {\n        &self.custom_property_registrations\n    }\n\n    fn revalidate_scopes<E: TElement>(\n        &self,\n        element: &E,\n        matching_context: &mut MatchingContext<E::Impl>,\n        result: &mut ScopeRevalidationResult,\n    ) {\n        // TODO(dshin): A scope block may not contain style rule for this element, but we don't keep\n        // track of that, so we check _all_ scope conditions. It's possible for two comparable elements\n        // to share scope & relevant styles rules, but also differ in scopes that do not contain style\n        // rules relevant to them. So while we can be certain that an identical result share scoped styles\n        // (Given that other sharing conditions are met), it is uncertain if elements with non-matching\n        // results do not.\n        for condition_id in 1..self.scope_conditions.len() {\n            let condition = &self.scope_conditions[condition_id];\n            let matches = if condition.is_trivial {\n                // Just ignore this condition - for style sharing candidates, guaranteed\n                // the same match result.\n                continue;\n            } else {\n                let result = scope_root_candidates(\n                    &self.scope_conditions,\n                    ScopeConditionId(condition_id as u16),\n                    element,\n                    // This should be ok since we aren't sharing styles across shadow boundaries.\n                    false,\n                    &self.scope_subject_map,\n                    matching_context,\n                );\n                !result.candidates.is_empty()\n            };\n            result.scopes_matched.push(matches);\n        }\n    }\n\n    /// Clears the cascade data, but not the invalidation data.\n    fn clear_cascade_data(&mut self) {\n        self.normal_rules.clear();\n        if let Some(ref mut slotted_rules) = self.slotted_rules {\n            slotted_rules.clear();\n        }\n        if let Some(ref mut part_rules) = self.part_rules {\n            part_rules.clear();\n        }\n        if let Some(ref mut host_rules) = self.featureless_host_rules {\n            host_rules.clear();\n        }\n        self.animations.clear();\n        self.custom_property_registrations.clear();\n        self.layer_id.clear();\n        self.layers.clear();\n        self.layers.push(CascadeLayer::root());\n        self.custom_media.clear();\n        self.container_conditions.clear();\n        self.container_conditions\n            .push(ContainerConditionReference::none());\n        self.scope_conditions.clear();\n        self.scope_conditions.push(ScopeConditionReference::none());\n        self.extra_data.clear();\n        self.rules_source_order = 0;\n        self.num_selectors = 0;\n        self.num_declarations = 0;\n    }\n\n    fn clear_invalidation_data(&mut self) {\n        self.invalidation_map.clear();\n        self.relative_selector_invalidation_map.clear();\n        self.additional_relative_selector_invalidation_map.clear();\n        self.attribute_dependencies.clear();\n        self.nth_of_attribute_dependencies.clear();\n        self.nth_of_custom_state_dependencies.clear();\n        self.nth_of_class_dependencies.clear();\n        self.state_dependencies = ElementState::empty();\n        self.nth_of_state_dependencies = ElementState::empty();\n        self.document_state_dependencies = DocumentState::empty();\n        self.mapped_ids.clear();\n        self.nth_of_mapped_ids.clear();\n        self.selectors_for_cache_revalidation.clear();\n        self.effective_media_query_results.clear();\n        self.scope_subject_map.clear();\n    }\n}\n\nfn note_scope_selector_for_invalidation(\n    quirks_mode: QuirksMode,\n    scope_dependencies: &Arc<servo_arc::HeaderSlice<(), Dependency>>,\n    dependency_vector: &mut Vec<Dependency>,\n    invalidation_map: &mut InvalidationMap,\n    relative_selector_invalidation_map: &mut InvalidationMap,\n    additional_relative_selector_invalidation_map: &mut AdditionalRelativeSelectorInvalidationMap,\n    visitor: &mut StylistSelectorVisitor<'_>,\n    scope_kind: ScopeDependencyInvalidationKind,\n    s: &Selector<SelectorImpl>,\n) -> Result<(), AllocErr> {\n    let mut new_inner_dependencies = note_selector_for_invalidation(\n        &s.clone(),\n        quirks_mode,\n        invalidation_map,\n        relative_selector_invalidation_map,\n        additional_relative_selector_invalidation_map,\n        Some(&scope_dependencies),\n        Some(scope_kind),\n    )?;\n    s.visit(visitor);\n    new_inner_dependencies.as_mut().map(|dep| {\n        dependency_vector.append(dep);\n    });\n    Ok(())\n}\n\nfn build_scope_dependencies(\n    quirks_mode: QuirksMode,\n    mut cur_scope_inner_dependencies: Vec<Dependency>,\n    mut visitor: StylistSelectorVisitor<'_>,\n    cond: &ScopeBoundsWithHashes,\n    mut invalidation_map: &mut InvalidationMap,\n    mut relative_selector_invalidation_map: &mut InvalidationMap,\n    mut additional_relative_selector_invalidation_map: &mut AdditionalRelativeSelectorInvalidationMap,\n) -> Result<Vec<Dependency>, AllocErr> {\n    if cond.end.is_some() {\n        let deps =\n            ThinArc::from_header_and_iter((), cur_scope_inner_dependencies.clone().into_iter());\n        let mut end_dependency_vector = Vec::new();\n        for s in cond.end_selectors() {\n            note_scope_selector_for_invalidation(\n                quirks_mode,\n                &deps,\n                &mut end_dependency_vector,\n                &mut invalidation_map,\n                &mut relative_selector_invalidation_map,\n                &mut additional_relative_selector_invalidation_map,\n                &mut visitor,\n                ScopeDependencyInvalidationKind::ScopeEnd,\n                s,\n            )?;\n        }\n        cur_scope_inner_dependencies.append(&mut end_dependency_vector);\n    }\n    let inner_scope_dependencies =\n        ThinArc::from_header_and_iter((), cur_scope_inner_dependencies.into_iter());\n\n    Ok(if cond.start.is_some() {\n        let mut dependency_vector = Vec::new();\n        for s in cond.start_selectors() {\n            note_scope_selector_for_invalidation(\n                quirks_mode,\n                &inner_scope_dependencies,\n                &mut dependency_vector,\n                &mut invalidation_map,\n                &mut relative_selector_invalidation_map,\n                &mut additional_relative_selector_invalidation_map,\n                &mut visitor,\n                ScopeDependencyInvalidationKind::ExplicitScope,\n                s,\n            )?;\n        }\n        dependency_vector\n    } else {\n        vec![Dependency::new(\n            IMPLICIT_SCOPE.slice()[0].clone(),\n            0,\n            Some(inner_scope_dependencies),\n            DependencyInvalidationKind::Scope(ScopeDependencyInvalidationKind::ImplicitScope),\n        )]\n    })\n}\n\nimpl CascadeDataCacheEntry for CascadeData {\n    fn rebuild<S>(\n        device: &Device,\n        quirks_mode: QuirksMode,\n        collection: SheetCollectionFlusher<S>,\n        guard: &SharedRwLockReadGuard,\n        old: &Self,\n        difference: &mut CascadeDataDifference,\n    ) -> Result<Arc<Self>, AllocErr>\n    where\n        S: StylesheetInDocument + PartialEq + 'static,\n    {\n        debug_assert!(collection.dirty(), \"We surely need to do something?\");\n        // If we're doing a full rebuild anyways, don't bother cloning the data.\n        let mut updatable_entry = match collection.data_validity() {\n            DataValidity::Valid | DataValidity::CascadeInvalid => old.clone(),\n            DataValidity::FullyInvalid => Self::new(),\n        };\n        updatable_entry.rebuild(device, quirks_mode, collection, guard, difference)?;\n        Ok(Arc::new(updatable_entry))\n    }\n\n    #[cfg(feature = \"gecko\")]\n    fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {\n        self.normal_rules.add_size_of(ops, sizes);\n        if let Some(ref slotted_rules) = self.slotted_rules {\n            slotted_rules.add_size_of(ops, sizes);\n        }\n        if let Some(ref part_rules) = self.part_rules {\n            part_rules.add_size_of(ops, sizes);\n        }\n        if let Some(ref host_rules) = self.featureless_host_rules {\n            host_rules.add_size_of(ops, sizes);\n        }\n        sizes.mInvalidationMap += self.invalidation_map.size_of(ops);\n        sizes.mRevalidationSelectors += self.selectors_for_cache_revalidation.size_of(ops);\n        sizes.mOther += self.animations.size_of(ops);\n        sizes.mOther += self.effective_media_query_results.size_of(ops);\n        sizes.mOther += self.extra_data.size_of(ops);\n    }\n}\n\nimpl Default for CascadeData {\n    fn default() -> Self {\n        CascadeData::new()\n    }\n}\n\n/// A rule, that wraps a style rule, but represents a single selector of the\n/// rule.\n#[derive(Clone, Debug, MallocSizeOf)]\npub struct Rule {\n    /// The selector this struct represents. We store this and the\n    /// any_{important,normal} booleans inline in the Rule to avoid\n    /// pointer-chasing when gathering applicable declarations, which\n    /// can ruin performance when there are a lot of rules.\n    #[ignore_malloc_size_of = \"CssRules have primary refs, we measure there\"]\n    pub selector: Selector<SelectorImpl>,\n\n    /// The ancestor hashes associated with the selector.\n    pub hashes: AncestorHashes,\n\n    /// The source order this style rule appears in. Note that we only use\n    /// three bytes to store this value in ApplicableDeclarationsBlock, so\n    /// we could repurpose that storage here if we needed to.\n    pub source_order: u32,\n\n    /// The current layer id of this style rule.\n    pub layer_id: LayerId,\n\n    /// The current @container rule id.\n    pub container_condition_id: ContainerConditionId,\n\n    /// Flags for special cascade behaviors.\n    pub cascade_flags: RuleCascadeFlags,\n\n    /// The current @scope rule id.\n    pub scope_condition_id: ScopeConditionId,\n\n    /// The actual style rule.\n    #[ignore_malloc_size_of = \"Secondary ref. Primary ref is in StyleRule under Stylesheet.\"]\n    pub style_source: StyleSource,\n}\n\nimpl SelectorMapEntry for Rule {\n    fn selector(&self) -> SelectorIter<'_, SelectorImpl> {\n        self.selector.iter()\n    }\n}\n\nimpl Rule {\n    /// Returns the specificity of the rule.\n    pub fn specificity(&self) -> u32 {\n        self.selector.specificity()\n    }\n\n    /// Turns this rule into an `ApplicableDeclarationBlock` for the given\n    /// cascade level.\n    pub fn to_applicable_declaration_block(\n        &self,\n        level: CascadeLevel,\n        cascade_data: &CascadeData,\n        scope_proximity: ScopeProximity,\n    ) -> ApplicableDeclarationBlock {\n        ApplicableDeclarationBlock::new(\n            self.style_source.clone(),\n            self.source_order,\n            level,\n            self.specificity(),\n            cascade_data.layer_order_for(self.layer_id),\n            scope_proximity,\n            self.cascade_flags,\n        )\n    }\n\n    /// Creates a new Rule.\n    pub fn new(\n        selector: Selector<SelectorImpl>,\n        hashes: AncestorHashes,\n        style_source: StyleSource,\n        source_order: u32,\n        layer_id: LayerId,\n        container_condition_id: ContainerConditionId,\n        cascade_flags: RuleCascadeFlags,\n        scope_condition_id: ScopeConditionId,\n    ) -> Self {\n        Self {\n            selector,\n            hashes,\n            style_source,\n            source_order,\n            layer_id,\n            container_condition_id,\n            cascade_flags,\n            scope_condition_id,\n        }\n    }\n}\n\n// The size of this is critical to performance on the bloom-basic\n// microbenchmark.\n// When iterating over a large Rule array, we want to be able to fast-reject\n// selectors (with the inline hashes) with as few cache misses as possible.\nsize_of_test!(Rule, 40);\n\n/// A function to be able to test the revalidation stuff.\npub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {\n    let mut needs_revalidation = false;\n    let mut mapped_ids = Default::default();\n    let mut nth_of_mapped_ids = Default::default();\n    let mut attribute_dependencies = Default::default();\n    let mut nth_of_class_dependencies = Default::default();\n    let mut nth_of_attribute_dependencies = Default::default();\n    let mut nth_of_custom_state_dependencies = Default::default();\n    let mut state_dependencies = ElementState::empty();\n    let mut nth_of_state_dependencies = ElementState::empty();\n    let mut document_state_dependencies = DocumentState::empty();\n    let mut visitor = StylistSelectorVisitor {\n        passed_rightmost_selector: false,\n        needs_revalidation: &mut needs_revalidation,\n        in_selector_list_of: SelectorListKind::default(),\n        mapped_ids: &mut mapped_ids,\n        nth_of_mapped_ids: &mut nth_of_mapped_ids,\n        attribute_dependencies: &mut attribute_dependencies,\n        nth_of_class_dependencies: &mut nth_of_class_dependencies,\n        nth_of_attribute_dependencies: &mut nth_of_attribute_dependencies,\n        nth_of_custom_state_dependencies: &mut nth_of_custom_state_dependencies,\n        state_dependencies: &mut state_dependencies,\n        nth_of_state_dependencies: &mut nth_of_state_dependencies,\n        document_state_dependencies: &mut document_state_dependencies,\n    };\n    s.visit(&mut visitor);\n    needs_revalidation\n}\n"
  },
  {
    "path": "style/thread_state.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Supports dynamic assertions about what sort of thread is running and\n//! what state it's in.\n\n#![deny(missing_docs)]\n\nuse std::cell::Cell;\n\nbitflags! {\n    /// A thread state flag, used for multiple assertions.\n    #[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]\n    pub struct ThreadState: u32 {\n        /// Whether we're in a script thread.\n        const SCRIPT          = 0x01;\n        /// Whether we're in a layout thread.\n        const LAYOUT          = 0x02;\n\n        /// Whether we're in a script worker thread (actual web workers), or in\n        /// a layout worker thread.\n        const IN_WORKER       = 0x0100;\n\n        /// Whether the current thread is going through a GC.\n        const IN_GC           = 0x0200;\n    }\n}\n\nimpl ThreadState {\n    /// Whether the current thread is a worker thread.\n    pub fn is_worker(self) -> bool {\n        self.contains(ThreadState::IN_WORKER)\n    }\n\n    /// Whether the current thread is a script thread.\n    pub fn is_script(self) -> bool {\n        self.contains(ThreadState::SCRIPT)\n    }\n\n    /// Whether the current thread is a layout thread.\n    pub fn is_layout(self) -> bool {\n        self.contains(ThreadState::LAYOUT)\n    }\n}\n\nthread_local!(static STATE: Cell<Option<ThreadState>> = const { Cell::new(None) });\n\n/// Initializes the current thread state.\npub fn initialize(initialize_to: ThreadState) {\n    STATE.with(|state| {\n        if let Some(current_state) = state.get() {\n            if initialize_to != current_state {\n                panic!(\"Thread state already initialized as {:?}\", current_state);\n            }\n        }\n        state.set(Some(initialize_to));\n    });\n}\n\n/// Initializes the current thread as a layout worker thread.\npub fn initialize_layout_worker_thread() {\n    initialize(ThreadState::LAYOUT | ThreadState::IN_WORKER);\n}\n\n/// Gets the current thread state.\npub fn get() -> ThreadState {\n    STATE.with(|state| state.get().unwrap_or_default())\n}\n\n/// Enters into a given temporary state. Panics if re-entering.\npub fn enter(additional_flags: ThreadState) {\n    STATE.with(|state| {\n        let current_state = state.get().unwrap_or_default();\n        debug_assert!(!current_state.intersects(additional_flags));\n        state.set(Some(current_state | additional_flags));\n    })\n}\n\n/// Exits a given temporary state.\npub fn exit(flags_to_remove: ThreadState) {\n    STATE.with(|state| {\n        let current_state = state.get().unwrap_or_default();\n        debug_assert!(current_state.contains(flags_to_remove));\n        state.set(Some(current_state & !flags_to_remove));\n    })\n}\n"
  },
  {
    "path": "style/traversal.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Traversing the DOM tree; the bloom filter.\n\nuse crate::context::{ElementCascadeInputs, SharedStyleContext, StyleContext};\nuse crate::data::{ElementData, ElementStyles, RestyleKind};\nuse crate::dom::{NodeInfo, OpaqueNode, TElement, TNode};\nuse crate::invalidation::element::restyle_hints::RestyleHint;\nuse crate::matching::MatchMethods;\nuse crate::selector_parser::PseudoElement;\nuse crate::sharing::StyleSharingTarget;\nuse crate::style_resolver::{PseudoElementResolution, StyleResolverForElement};\nuse crate::stylist::RuleInclusion;\nuse crate::traversal_flags::TraversalFlags;\nuse selectors::matching::SelectorCaches;\n#[cfg(feature = \"gecko\")]\nuse selectors::parser::PseudoElement as PseudoElementTrait;\nuse smallvec::SmallVec;\nuse std::collections::HashMap;\n\n/// A cache from element reference to known-valid computed style.\npub type UndisplayedStyleCache =\n    HashMap<selectors::OpaqueElement, servo_arc::Arc<crate::properties::ComputedValues>>;\n\n/// A per-traversal-level chunk of data. This is sent down by the traversal, and\n/// currently only holds the dom depth for the bloom filter.\n///\n/// NB: Keep this as small as possible, please!\n#[derive(Clone, Copy, Debug)]\npub struct PerLevelTraversalData {\n    /// The current dom depth.\n    ///\n    /// This is kept with cooperation from the traversal code and the bloom\n    /// filter.\n    pub current_dom_depth: usize,\n}\n\n/// We use this structure, rather than just returning a boolean from pre_traverse,\n/// to enfore that callers process root invalidations before starting the traversal.\npub struct PreTraverseToken<E: TElement>(Option<E>);\nimpl<E: TElement> PreTraverseToken<E> {\n    /// Whether we should traverse children.\n    pub fn should_traverse(&self) -> bool {\n        self.0.is_some()\n    }\n\n    /// Returns the traversal root for the current traversal.\n    pub(crate) fn traversal_root(self) -> Option<E> {\n        self.0\n    }\n}\n\n/// A DOM Traversal trait, that is used to generically implement styling for\n/// Gecko and Servo.\npub trait DomTraversal<E: TElement>: Sync {\n    /// Process `node` on the way down, before its children have been processed.\n    ///\n    /// The callback is invoked for each child node that should be processed by\n    /// the traversal.\n    fn process_preorder<F>(\n        &self,\n        data: &PerLevelTraversalData,\n        context: &mut StyleContext<E>,\n        node: E::ConcreteNode,\n        note_child: F,\n    ) where\n        F: FnMut(E::ConcreteNode);\n\n    /// Process `node` on the way up, after its children have been processed.\n    ///\n    /// This is only executed if `needs_postorder_traversal` returns true.\n    fn process_postorder(&self, contect: &mut StyleContext<E>, node: E::ConcreteNode);\n\n    /// Boolean that specifies whether a bottom up traversal should be\n    /// performed.\n    ///\n    /// If it's false, then process_postorder has no effect at all.\n    fn needs_postorder_traversal() -> bool {\n        true\n    }\n\n    /// Handles the postorder step of the traversal, if it exists, by bubbling\n    /// up the parent chain.\n    ///\n    /// If we are the last child that finished processing, recursively process\n    /// our parent. Else, stop. Also, stop at the root.\n    ///\n    /// Thus, if we start with all the leaves of a tree, we end up traversing\n    /// the whole tree bottom-up because each parent will be processed exactly\n    /// once (by the last child that finishes processing).\n    ///\n    /// The only communication between siblings is that they both\n    /// fetch-and-subtract the parent's children count. This makes it safe to\n    /// call durign the parallel traversal.\n    fn handle_postorder_traversal(\n        &self,\n        context: &mut StyleContext<E>,\n        root: OpaqueNode,\n        mut node: E::ConcreteNode,\n        children_to_process: isize,\n    ) {\n        // If the postorder step is a no-op, don't bother.\n        if !Self::needs_postorder_traversal() {\n            return;\n        }\n\n        if children_to_process == 0 {\n            // We are a leaf. Walk up the chain.\n            loop {\n                self.process_postorder(context, node);\n                if node.opaque() == root {\n                    break;\n                }\n                let parent = node.traversal_parent().unwrap();\n                let remaining = parent.did_process_child();\n                if remaining != 0 {\n                    // The parent has other unprocessed descendants. We only\n                    // perform postorder processing after the last descendant\n                    // has been processed.\n                    break;\n                }\n\n                node = parent.as_node();\n            }\n        } else {\n            // Otherwise record the number of children to process when the time\n            // comes.\n            node.as_element()\n                .unwrap()\n                .store_children_to_process(children_to_process);\n        }\n    }\n\n    /// Style invalidations happen when traversing from a parent to its children.\n    /// However, this mechanism can't handle style invalidations on the root. As\n    /// such, we have a pre-traversal step to handle that part and determine whether\n    /// a full traversal is needed.\n    fn pre_traverse(root: E, shared_context: &SharedStyleContext) -> PreTraverseToken<E> {\n        use crate::invalidation::element::state_and_attributes::propagate_dirty_bit_up_to;\n\n        let traversal_flags = shared_context.traversal_flags;\n\n        let mut data = root.mutate_data();\n        let mut data = data.as_mut().map(|d| &mut **d);\n\n        if let Some(ref mut data) = data {\n            if !traversal_flags.for_animation_only() {\n                // Invalidate our style, and that of our siblings and\n                // descendants as needed.\n                let invalidation_result = data.invalidate_style_if_needed(\n                    root,\n                    shared_context,\n                    None,\n                    &mut SelectorCaches::default(),\n                );\n\n                if invalidation_result.has_invalidated_siblings() {\n                    let actual_root = root.as_node().parent_element_or_host().expect(\n                        \"How in the world can you invalidate \\\n                         siblings without a parent?\",\n                    );\n                    propagate_dirty_bit_up_to(actual_root, root);\n                    return PreTraverseToken(Some(actual_root));\n                }\n            }\n        }\n\n        let should_traverse =\n            Self::element_needs_traversal(root, traversal_flags, data.as_mut().map(|d| &**d));\n\n        // If we're not going to traverse at all, we may need to clear some state\n        // off the root (which would normally be done at the end of recalc_style_at).\n        if !should_traverse && data.is_some() {\n            clear_state_after_traversing(root, data.unwrap(), traversal_flags);\n        }\n\n        PreTraverseToken(if should_traverse { Some(root) } else { None })\n    }\n\n    /// Returns true if traversal should visit a text node. The style system\n    /// never processes text nodes, but Servo overrides this to visit them for\n    /// flow construction when necessary.\n    fn text_node_needs_traversal(node: E::ConcreteNode, _parent_data: &ElementData) -> bool {\n        debug_assert!(node.is_text_node());\n        false\n    }\n\n    /// Returns true if traversal is needed for the given element and subtree.\n    fn element_needs_traversal(\n        el: E,\n        traversal_flags: TraversalFlags,\n        data: Option<&ElementData>,\n    ) -> bool {\n        debug!(\n            \"element_needs_traversal({:?}, {:?}, {:?})\",\n            el, traversal_flags, data\n        );\n\n        // Unwrap the data.\n        let data = match data {\n            Some(d) if d.has_styles() => d,\n            _ => return true,\n        };\n\n        if traversal_flags.for_animation_only() {\n            // In case of animation-only traversal we need to traverse the element if the element\n            // has animation only dirty descendants bit, or animation-only restyle hint.\n            return el.has_animation_only_dirty_descendants()\n                || data.hint.has_animation_hint_or_recascade();\n        }\n\n        // If the dirty descendants bit is set, we need to traverse no matter\n        // what. Skip examining the ElementData.\n        if el.has_dirty_descendants() {\n            return true;\n        }\n\n        // If we have a restyle hint or need to recascade, we need to visit the\n        // element.\n        //\n        // Note that this is different than checking has_current_styles_for_traversal(),\n        // since that can return true even if we have a restyle hint indicating\n        // that the element's descendants (but not necessarily the element) need\n        // restyling.\n        if !data.hint.is_empty() {\n            return true;\n        }\n\n        // Servo uses the post-order traversal for flow construction, so we need\n        // to traverse any element with damage so that we can perform fixup /\n        // reconstruction on our way back up the tree.\n        if cfg!(feature = \"servo\") && !data.damage.is_empty() {\n            return true;\n        }\n\n        trace!(\"{:?} doesn't need traversal\", el);\n        false\n    }\n\n    /// Return the shared style context common to all worker threads.\n    fn shared_context(&self) -> &SharedStyleContext<'_>;\n}\n\n/// Manually resolve style by sequentially walking up the parent chain to the\n/// first styled Element, ignoring pending restyles. The resolved style is made\n/// available via a callback, and can be dropped by the time this function\n/// returns in the display:none subtree case.\npub fn resolve_style<E>(\n    context: &mut StyleContext<E>,\n    element: E,\n    rule_inclusion: RuleInclusion,\n    pseudo: Option<&PseudoElement>,\n    mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,\n) -> ElementStyles\nwhere\n    E: TElement,\n{\n    debug_assert!(\n        rule_inclusion == RuleInclusion::DefaultOnly\n            || pseudo.map_or(false, |p| p.is_before_or_after())\n            || element.borrow_data().map_or(true, |d| !d.has_styles()),\n        \"Why are we here?\"\n    );\n    debug_assert!(\n        rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),\n        \"can't use the cache for default styles only\"\n    );\n\n    let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();\n\n    // Clear the bloom filter, just in case the caller is reusing TLS.\n    context.thread_local.bloom_filter.clear();\n\n    let mut style = None;\n    let mut ancestor = element.traversal_parent();\n    while let Some(current) = ancestor {\n        if rule_inclusion == RuleInclusion::All {\n            if let Some(data) = current.borrow_data() {\n                if let Some(ancestor_style) = data.styles.get_primary() {\n                    style = Some(ancestor_style.clone());\n                    break;\n                }\n            }\n        }\n        if let Some(ref mut cache) = undisplayed_style_cache {\n            if let Some(s) = cache.get(&current.opaque()) {\n                style = Some(s.clone());\n                break;\n            }\n        }\n        ancestors_requiring_style_resolution.push(current);\n        ancestor = current.traversal_parent();\n    }\n\n    if let Some(ancestor) = ancestor {\n        context.thread_local.bloom_filter.rebuild(ancestor);\n        context.thread_local.bloom_filter.push(ancestor);\n    }\n\n    let mut layout_parent_style = style.clone();\n    while let Some(style) = layout_parent_style.take() {\n        if !style.is_display_contents() {\n            layout_parent_style = Some(style);\n            break;\n        }\n\n        ancestor = ancestor.unwrap().traversal_parent();\n        layout_parent_style =\n            ancestor.and_then(|a| a.borrow_data().map(|data| data.styles.primary().clone()));\n    }\n\n    for ancestor in ancestors_requiring_style_resolution.iter().rev() {\n        context.thread_local.bloom_filter.assert_complete(*ancestor);\n\n        // Actually `PseudoElementResolution` doesn't really matter here.\n        // (but it does matter below!).\n        let primary_style = StyleResolverForElement::new(\n            *ancestor,\n            context,\n            rule_inclusion,\n            PseudoElementResolution::IfApplicable,\n        )\n        .resolve_primary_style(style.as_deref(), layout_parent_style.as_deref());\n\n        let is_display_contents = primary_style.style().is_display_contents();\n\n        style = Some(primary_style.style.0);\n        if !is_display_contents {\n            layout_parent_style = style.clone();\n        }\n\n        if let Some(ref mut cache) = undisplayed_style_cache {\n            cache.insert(ancestor.opaque(), style.clone().unwrap());\n        }\n        context.thread_local.bloom_filter.push(*ancestor);\n    }\n\n    context.thread_local.bloom_filter.assert_complete(element);\n    let styles: ElementStyles = StyleResolverForElement::new(\n        element,\n        context,\n        rule_inclusion,\n        PseudoElementResolution::Force,\n    )\n    .resolve_style(style.as_deref(), layout_parent_style.as_deref())\n    .into();\n\n    if let Some(ref mut cache) = undisplayed_style_cache {\n        cache.insert(element.opaque(), styles.primary().clone());\n    }\n\n    styles\n}\n\n/// Calculates the style for a single node.\n#[inline]\n#[allow(unsafe_code)]\npub fn recalc_style_at<E, D, F>(\n    _traversal: &D,\n    traversal_data: &PerLevelTraversalData,\n    context: &mut StyleContext<E>,\n    element: E,\n    data: &mut ElementData,\n    note_child: F,\n) where\n    E: TElement,\n    D: DomTraversal<E>,\n    F: FnMut(E::ConcreteNode),\n{\n    let flags = context.shared.traversal_flags;\n    let is_initial_style = !data.has_styles();\n\n    context.thread_local.statistics.elements_traversed += 1;\n    debug_assert!(\n        flags.intersects(TraversalFlags::AnimationOnly)\n            || is_initial_style\n            || !element.has_snapshot()\n            || element.handled_snapshot(),\n        \"Should've handled snapshots here already\"\n    );\n\n    let restyle_kind = data.restyle_kind(&context.shared);\n    debug!(\n        \"recalc_style_at: {:?} (restyle_kind={:?}, dirty_descendants={:?}, data={:?})\",\n        element,\n        restyle_kind,\n        element.has_dirty_descendants(),\n        data\n    );\n\n    let mut child_restyle_hint = RestyleHint::empty();\n\n    // Compute style for this element if necessary.\n    if let Some(restyle_kind) = restyle_kind {\n        child_restyle_hint = compute_style(traversal_data, context, element, data, restyle_kind);\n\n        if !element.matches_user_and_content_rules() {\n            // We must always cascade native anonymous subtrees, since they\n            // may have pseudo-elements underneath that would inherit from the\n            // closest non-NAC ancestor instead of us.\n            child_restyle_hint |= RestyleHint::RECASCADE_SELF;\n        }\n\n        // If we're restyling this element to display:none, throw away all style\n        // data in the subtree, notify the caller to early-return.\n        if data.styles.is_display_none() {\n            debug!(\n                \"{:?} style is display:none - clearing data from descendants.\",\n                element\n            );\n            unsafe {\n                clear_descendant_data(element);\n            }\n        }\n\n        // Inform any paint worklets of changed style, to speculatively\n        // evaluate the worklet code. In the case that the size hasn't changed,\n        // this will result in increased concurrency between script and layout.\n        notify_paint_worklet(context, data);\n    } else {\n        debug_assert!(data.has_styles());\n        data.set_traversed_without_styling();\n    }\n\n    // Now that matching and cascading is done, clear the bits corresponding to\n    // those operations and compute the propagated restyle hint (unless we're\n    // not processing invalidations, in which case don't need to propagate it\n    // and must avoid clearing it).\n    debug_assert!(\n        flags.for_animation_only() || !data.hint.has_animation_hint(),\n        \"animation restyle hint should be handled during \\\n         animation-only restyles\"\n    );\n    let mut propagated_hint = data.hint.propagate(&flags);\n    trace!(\n        \"propagated_hint={:?}, restyle_requirement={:?}, \\\n         is_display_none={:?}, implementing_pseudo={:?}\",\n        propagated_hint,\n        child_restyle_hint,\n        data.styles.is_display_none(),\n        element.implemented_pseudo_element()\n    );\n\n    // Integrate the child cascade requirement into the propagated hint.\n    propagated_hint |= child_restyle_hint;\n\n    let has_dirty_descendants_for_this_restyle = if flags.for_animation_only() {\n        element.has_animation_only_dirty_descendants()\n    } else {\n        element.has_dirty_descendants()\n    };\n\n    // Before examining each child individually, try to prove that our children\n    // don't need style processing. They need processing if any of the following\n    // conditions hold:\n    //\n    //  * We have the dirty descendants bit.\n    //  * We're propagating a restyle hint.\n    //  * This is a servo non-incremental traversal.\n    //\n    // We only do this if we're not a display: none root, since in that case\n    // it's useless to style children.\n    let mut traverse_children = has_dirty_descendants_for_this_restyle\n        || !propagated_hint.is_empty();\n\n    traverse_children = traverse_children && !data.styles.is_display_none();\n\n    // Examine our children, and enqueue the appropriate ones for traversal.\n    if traverse_children {\n        note_children::<E, D, F>(\n            context,\n            element,\n            data,\n            propagated_hint,\n            is_initial_style,\n            note_child,\n        );\n    }\n\n    // FIXME(bholley): Make these assertions pass for servo.\n    if cfg!(feature = \"gecko\") && cfg!(debug_assertions) && data.styles.is_display_none() {\n        debug_assert!(!element.has_dirty_descendants());\n        debug_assert!(!element.has_animation_only_dirty_descendants());\n    }\n\n    clear_state_after_traversing(element, data, flags);\n}\n\nfn clear_state_after_traversing<E>(element: E, data: &mut ElementData, flags: TraversalFlags)\nwhere\n    E: TElement,\n{\n    if flags.intersects(TraversalFlags::FinalAnimationTraversal) {\n        debug_assert!(flags.for_animation_only());\n        data.clear_restyle_flags_and_damage();\n        unsafe {\n            element.unset_animation_only_dirty_descendants();\n        }\n    }\n}\n\nfn compute_style<E>(\n    traversal_data: &PerLevelTraversalData,\n    context: &mut StyleContext<E>,\n    element: E,\n    data: &mut ElementData,\n    kind: RestyleKind,\n) -> RestyleHint\nwhere\n    E: TElement,\n{\n    use crate::data::RestyleKind::*;\n\n    context.thread_local.statistics.elements_styled += 1;\n    debug!(\"compute_style: {:?} (kind={:?})\", element, kind);\n\n    if data.has_styles() {\n        data.set_restyled();\n    }\n\n    let mut important_rules_changed = false;\n    let new_styles = match kind {\n        MatchAndCascade => {\n            debug_assert!(\n                !context.shared.traversal_flags.for_animation_only() || !data.has_styles(),\n                \"MatchAndCascade shouldn't normally be processed during animation-only traversal\"\n            );\n            // Ensure the bloom filter is up to date.\n            context\n                .thread_local\n                .bloom_filter\n                .insert_parents_recovering(element, traversal_data.current_dom_depth);\n\n            context.thread_local.bloom_filter.assert_complete(element);\n            debug_assert_eq!(\n                context.thread_local.bloom_filter.matching_depth(),\n                traversal_data.current_dom_depth\n            );\n\n            // This is only relevant for animations as of right now.\n            important_rules_changed = true;\n\n            let mut target = StyleSharingTarget::new(element);\n\n            // Now that our bloom filter is set up, try the style sharing\n            // cache.\n            match target.share_style_if_possible(context) {\n                Some(shared_styles) => {\n                    context.thread_local.statistics.styles_shared += 1;\n                    shared_styles\n                },\n                None => {\n                    context.thread_local.statistics.elements_matched += 1;\n                    // Perform the matching and cascading.\n                    let new_styles = {\n                        let mut resolver = StyleResolverForElement::new(\n                            element,\n                            context,\n                            RuleInclusion::All,\n                            PseudoElementResolution::IfApplicable,\n                        );\n\n                        resolver.resolve_style_with_default_parents()\n                    };\n\n                    context.thread_local.sharing_cache.insert_if_possible(\n                        &element,\n                        &new_styles.primary,\n                        Some(&mut target),\n                        traversal_data.current_dom_depth,\n                        &context.shared,\n                    );\n\n                    new_styles\n                },\n            }\n        },\n        CascadeWithReplacements(flags) => {\n            // Skipping full matching, load cascade inputs from previous values.\n            let mut cascade_inputs = ElementCascadeInputs::new_from_element_data(data);\n            important_rules_changed = element.replace_rules(flags, context, &mut cascade_inputs);\n\n            let mut resolver = StyleResolverForElement::new(\n                element,\n                context,\n                RuleInclusion::All,\n                PseudoElementResolution::IfApplicable,\n            );\n\n            resolver.cascade_styles_with_default_parents(cascade_inputs)\n        },\n        CascadeOnly => {\n            // Skipping full matching, load cascade inputs from previous values.\n            let cascade_inputs = ElementCascadeInputs::new_from_element_data(data);\n\n            let new_styles = {\n                let mut resolver = StyleResolverForElement::new(\n                    element,\n                    context,\n                    RuleInclusion::All,\n                    PseudoElementResolution::IfApplicable,\n                );\n\n                resolver.cascade_styles_with_default_parents(cascade_inputs)\n            };\n\n            // Insert into the cache, but only if this style isn't reused from a\n            // sibling or cousin. Otherwise, recascading a bunch of identical\n            // elements would unnecessarily flood the cache with identical entries.\n            //\n            // This is analogous to the obvious \"don't insert an element that just\n            // got a hit in the style sharing cache\" behavior in the MatchAndCascade\n            // handling above.\n            //\n            // Note that, for the MatchAndCascade path, we still insert elements that\n            // shared styles via the rule node, because we know that there's something\n            // different about them that caused them to miss the sharing cache before\n            // selector matching. If we didn't, we would still end up with the same\n            // number of eventual styles, but would potentially miss out on various\n            // opportunities for skipping selector matching, which could hurt\n            // performance.\n            if !new_styles.primary.reused_via_rule_node {\n                context.thread_local.sharing_cache.insert_if_possible(\n                    &element,\n                    &new_styles.primary,\n                    None,\n                    traversal_data.current_dom_depth,\n                    &context.shared,\n                );\n            }\n\n            new_styles\n        },\n    };\n\n    element.finish_restyle(context, data, new_styles, important_rules_changed)\n}\n\n#[cfg(feature = \"servo\")]\nfn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)\nwhere\n    E: TElement,\n{\n    use crate::values::generics::image::Image;\n    use style_traits::ToCss;\n\n    // We speculatively evaluate any paint worklets during styling.\n    // This allows us to run paint worklets in parallel with style and layout.\n    // Note that this is wasted effort if the size of the node has\n    // changed, but in may cases it won't have.\n    if let Some(ref values) = data.styles.primary {\n        for image in &values.get_background().background_image.0 {\n            let (name, arguments) = match *image {\n                Image::PaintWorklet(ref worklet) => (&worklet.name, &worklet.arguments),\n                _ => continue,\n            };\n            let painter = match context.shared.registered_speculative_painters.get(name) {\n                Some(painter) => painter,\n                None => continue,\n            };\n            let properties = painter\n                .properties()\n                .iter()\n                .filter_map(|(name, id)| id.as_shorthand().err().map(|id| (name, id)))\n                .map(|(name, id)| (name.clone(), values.computed_value_to_string(id)))\n                .collect();\n            let arguments = arguments\n                .iter()\n                .map(|argument| argument.to_css_string())\n                .collect();\n            debug!(\"Notifying paint worklet {}.\", painter.name());\n            painter.speculatively_draw_a_paint_image(properties, arguments);\n        }\n    }\n}\n\n#[cfg(not(feature = \"servo\"))]\nfn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)\nwhere\n    E: TElement,\n{\n    // The CSS paint API is Servo-only at the moment\n}\n\nfn note_children<E, D, F>(\n    context: &mut StyleContext<E>,\n    element: E,\n    data: &ElementData,\n    propagated_hint: RestyleHint,\n    is_initial_style: bool,\n    mut note_child: F,\n) where\n    E: TElement,\n    D: DomTraversal<E>,\n    F: FnMut(E::ConcreteNode),\n{\n    trace!(\"note_children: {:?}\", element);\n    let flags = context.shared.traversal_flags;\n\n    // Loop over all the traversal children.\n    for child_node in element.traversal_children() {\n        let child = match child_node.as_element() {\n            Some(el) => el,\n            None => {\n                if D::text_node_needs_traversal(child_node, data) {\n                    note_child(child_node);\n                }\n                continue;\n            },\n        };\n\n        let mut child_data = child.mutate_data();\n        let mut child_data = child_data.as_mut().map(|d| &mut **d);\n        trace!(\n            \" > {:?} -> {:?} + {:?}, pseudo: {:?}\",\n            child,\n            child_data.as_ref().map(|d| d.hint),\n            propagated_hint,\n            child.implemented_pseudo_element()\n        );\n\n        if let Some(ref mut child_data) = child_data {\n            child_data.hint.insert(propagated_hint);\n\n            // Handle element snapshots and invalidation of descendants and siblings\n            // as needed.\n            //\n            // NB: This will be a no-op if there's no snapshot.\n            child_data.invalidate_style_if_needed(\n                child,\n                &context.shared,\n                Some(&context.thread_local.stack_limit_checker),\n                &mut context.thread_local.selector_caches,\n            );\n        }\n\n        if D::element_needs_traversal(child, flags, child_data.map(|d| &*d)) {\n            note_child(child_node);\n\n            // Set the dirty descendants bit on the parent as needed, so that we\n            // can find elements during the post-traversal.\n            //\n            // Note that these bits may be cleared again at the bottom of\n            // recalc_style_at if requested by the caller.\n            if !is_initial_style {\n                if flags.for_animation_only() {\n                    unsafe {\n                        element.set_animation_only_dirty_descendants();\n                    }\n                } else {\n                    unsafe {\n                        element.set_dirty_descendants();\n                    }\n                }\n            }\n        }\n    }\n}\n\n/// Clear style data for all the subtree under `root` (but not for root itself).\n///\n/// We use a list to avoid unbounded recursion, which we need to avoid in the\n/// parallel traversal because the rayon stacks are small.\npub unsafe fn clear_descendant_data<E>(root: E)\nwhere\n    E: TElement,\n{\n    let mut parents = SmallVec::<[E; 32]>::new();\n    parents.push(root);\n    while let Some(p) = parents.pop() {\n        for kid in p.traversal_children() {\n            if let Some(kid) = kid.as_element() {\n                // We maintain an invariant that, if an element has data, all its\n                // ancestors have data as well.\n                //\n                // By consequence, any element without data has no descendants with\n                // data.\n                if kid.has_data() {\n                    kid.clear_data();\n                    parents.push(kid);\n                }\n            }\n        }\n    }\n\n    // Make sure not to clear NODE_NEEDS_FRAME on the root.\n    root.clear_descendant_bits();\n}\n"
  },
  {
    "path": "style/traversal_flags.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Flags that control the traversal process.\n//!\n//! We CamelCase rather than UPPER_CASING so that we can grep for the same\n//! strings across gecko and servo.\n#![allow(non_upper_case_globals)]\n\nbitflags! {\n    /// Flags that control the traversal process.\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct TraversalFlags: u32 {\n        /// Traverse only elements for animation restyles.\n        const AnimationOnly = 1 << 0;\n        /// Traverse and update all elements with CSS animations since\n        /// @keyframes rules may have changed. Triggered by CSS rule changes.\n        const ForCSSRuleChanges = 1 << 1;\n        /// The final animation-only traversal, which shouldn't really care about other\n        /// style changes anymore.\n        const FinalAnimationTraversal = 1 << 2;\n        /// Allows the traversal to run in parallel if there are sufficient cores on\n        /// the machine.\n        const ParallelTraversal = 1 << 7;\n        /// Flush throttled animations. By default, we only update throttled animations\n        /// when we have other non-throttled work to do. With this flag, we\n        /// unconditionally tick and process them.\n        const FlushThrottledAnimations = 1 << 8;\n\n    }\n}\n\n/// Asserts that all TraversalFlags flags have a matching ServoTraversalFlags value in gecko.\n#[cfg(feature = \"gecko\")]\n#[inline]\npub fn assert_traversal_flags_match() {\n    use crate::gecko_bindings::structs;\n\n    macro_rules! check_traversal_flags {\n        ( $( $a:ident => $b:path ),*, ) => {\n            if cfg!(debug_assertions) {\n                let mut modes = TraversalFlags::all();\n                $(\n                    assert_eq!(structs::$a as usize, $b.bits() as usize, stringify!($b));\n                    modes.remove($b);\n                )*\n                assert_eq!(modes, TraversalFlags::empty(), \"all TraversalFlags bits should have an assertion\");\n            }\n        }\n    }\n\n    check_traversal_flags! {\n        ServoTraversalFlags_AnimationOnly => TraversalFlags::AnimationOnly,\n        ServoTraversalFlags_ForCSSRuleChanges => TraversalFlags::ForCSSRuleChanges,\n        ServoTraversalFlags_FinalAnimationTraversal => TraversalFlags::FinalAnimationTraversal,\n        ServoTraversalFlags_ParallelTraversal => TraversalFlags::ParallelTraversal,\n        ServoTraversalFlags_FlushThrottledAnimations => TraversalFlags::FlushThrottledAnimations,\n    }\n}\n\nimpl TraversalFlags {\n    /// Returns true if the traversal is for animation-only restyles.\n    #[inline]\n    pub fn for_animation_only(&self) -> bool {\n        self.contains(TraversalFlags::AnimationOnly)\n    }\n}\n"
  },
  {
    "path": "style/typed_om/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Typed OM.\n//!\n//! https://drafts.css-houdini.org/css-typed-om-1/\n\npub mod numeric_declaration;\npub mod numeric_values;\npub mod sum_value;\n"
  },
  {
    "path": "style/typed_om/numeric_declaration.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Typed OM Numeric Declaration.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::typed_om::numeric_values::NoCalcNumeric;\nuse crate::values::generics::calc::CalcUnits;\nuse crate::values::specified::calc::{AllowParse, CalcNode};\nuse crate::values::specified::NoCalcLength;\nuse cssparser::{Parser, Token};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// A numeric declaration, with or without a `calc()` expression.\n#[derive(Clone, ToTyped)]\npub enum NumericDeclaration {\n    /// A numeric value without a `calc()` expression.\n    NoCalc(NoCalcNumeric),\n\n    /// A numeric value represented by a `calc()` expression.\n    ///\n    /// <https://drafts.csswg.org/css-values/#calc-notation>\n    Calc(CalcNode),\n}\n\nimpl Parse for NumericDeclaration {\n    /// <https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-parse>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n\n        // Step 1.\n        let token = input.next()?;\n\n        // Step 2.\n        match *token {\n            Token::Dimension {\n                value, ref unit, ..\n            } => {\n                NoCalcLength::parse_dimension_with_context(context, value, unit)\n                    .map(NoCalcNumeric::Length)\n                    .map(Self::NoCalc)\n                    .map_err(|()| location.new_unexpected_token_error(token.clone()))\n\n                // TODO: Add support for other values.\n\n                // Step 3.\n\n                // TODO: A type should be created from unit and if that fails, the failure\n                // should be propagated here.\n            },\n\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                let allow_all_units = AllowParse::new(CalcUnits::ALL);\n                let node = CalcNode::parse(context, input, function, allow_all_units)?;\n\n                let allow_all_types = AllowedNumericType::All;\n                let _ = node\n                    .clone()\n                    .into_length_or_percentage(allow_all_types)\n                    .map_err(|()| {\n                        location.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                    })?;\n\n                // TODO: Add support for other values represented by a `calc()` expression.\n\n                Ok(Self::Calc(node))\n            },\n\n            ref token => return Err(location.new_unexpected_token_error(token.clone())),\n        }\n    }\n}\n"
  },
  {
    "path": "style/typed_om/numeric_values.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Typed OM Numeric Values.\n\nuse crate::derives::*;\nuse crate::values::specified::{NoCalcLength, Number, Percentage, Time};\nuse crate::values::CSSFloat;\nuse cssparser::match_ignore_ascii_case;\nuse style_traits::ParsingMode;\n\n/// A numeric value without a `calc` expression.\n#[derive(Clone, ToTyped)]\n#[repr(u8)]\npub enum NoCalcNumeric {\n    /// A `<length>` value.\n    ///\n    /// <https://drafts.csswg.org/css-values/#lengths>\n    Length(NoCalcLength),\n\n    /// A `<time>` value.\n    ///\n    /// <https://drafts.csswg.org/css-values/#time>\n    Time(Time),\n\n    /// A `<number>` value.\n    ///\n    /// <https://drafts.csswg.org/css-values/#number-value>\n    Number(Number),\n\n    /// A `<percentage>` value.\n    ///\n    /// <https://drafts.csswg.org/css-values/#percentages>\n    Percentage(Percentage),\n    // TODO: Add other values.\n}\n\nimpl NoCalcNumeric {\n    /// Return the unitless, raw value.\n    pub fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            Self::Length(v) => v.unitless_value(),\n            Self::Time(v) => v.unitless_value(),\n            Self::Number(v) => v.get(),\n            Self::Percentage(v) => v.get(),\n        }\n    }\n\n    /// Return the unit, as a string.\n    ///\n    /// TODO: Investigate returning SortKey or adding a new variant for\n    /// returning the unit as SortKey. Tracked in\n    /// <https://bugzilla.mozilla.org/show_bug.cgi?id=2015863>\n    pub fn unit(&self) -> &'static str {\n        match *self {\n            Self::Length(v) => v.unit(),\n            Self::Time(v) => v.unit(),\n            Self::Number(v) => v.unit(),\n            Self::Percentage(v) => v.unit(),\n        }\n    }\n\n    /// Return the canonical unit for this value, if one exists.\n    ///\n    /// TODO: Investigate returning SortKey. Tracked in\n    /// <https://bugzilla.mozilla.org/show_bug.cgi?id=2015863>\n    pub fn canonical_unit(&self) -> Option<&'static str> {\n        match *self {\n            Self::Length(v) => v.canonical_unit(),\n            Self::Time(v) => v.canonical_unit(),\n            Self::Number(v) => v.canonical_unit(),\n            Self::Percentage(v) => v.canonical_unit(),\n        }\n    }\n\n    /// Convert this value to the specified unit, if possible.\n    ///\n    /// TODO: Investigate using SortKey. Tracked in\n    /// <https://bugzilla.mozilla.org/show_bug.cgi?id=2015863>\n    pub fn to(&self, unit: &str) -> Result<Self, ()> {\n        match self {\n            Self::Length(v) => Ok(Self::Length(v.to(unit)?)),\n            Self::Time(v) => Ok(Self::Time(v.to(unit)?)),\n            Self::Number(v) => Ok(Self::Number(v.to(unit)?)),\n            Self::Percentage(v) => Ok(Self::Percentage(v.to(unit)?)),\n        }\n    }\n\n    /// Parse a given unit value.\n    pub fn parse_unit_value(value: CSSFloat, unit: &str) -> Result<Self, ()> {\n        if let Ok(length) = NoCalcLength::parse_dimension_with_flags(\n            ParsingMode::DEFAULT,\n            /* in_page_rule = */ false,\n            value,\n            unit,\n        ) {\n            return Ok(NoCalcNumeric::Length(length));\n        }\n\n        if let Ok(time) = Time::parse_dimension(value, unit) {\n            return Ok(NoCalcNumeric::Time(time));\n        }\n\n        match_ignore_ascii_case! { unit,\n            \"number\" => Ok(NoCalcNumeric::Number(Number::new(value))),\n            \"percent\" => Ok(NoCalcNumeric::Percentage(Percentage::new(value))),\n            _ => Err(()),\n        }\n\n        // TODO: Add support for other values.\n    }\n}\n"
  },
  {
    "path": "style/typed_om/sum_value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Typed OM Sum Value.\n\nuse crate::typed_om::numeric_values::NoCalcNumeric;\nuse std::collections::HashMap;\nuse style_traits::{CssString, NumericValue, UnitValue};\n\ntype UnitMap = HashMap<String, i32>;\n\n/// <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-sum-value>\n#[derive(Clone, Debug)]\nstruct SumValueItem {\n    value: f32,\n    unit_map: UnitMap,\n}\n\nimpl SumValueItem {\n    /// <https://drafts.css-houdini.org/css-typed-om-1/#create-a-cssunitvalue-from-a-sum-value-item>\n    fn to_unit_value(&self) -> Result<UnitValue, ()> {\n        // Step 1.\n        if self.unit_map.len() > 1 {\n            return Err(());\n        }\n\n        // Step 2.\n        if self.unit_map.is_empty() {\n            return Ok(UnitValue {\n                value: self.value,\n                unit: CssString::from(\"number\"),\n            });\n        }\n\n        // Step 3.\n        let (unit, power) = self.unit_map.iter().next().unwrap();\n        if *power != 1 {\n            return Err(());\n        }\n\n        // Step 4.\n        Ok(UnitValue {\n            value: self.value,\n            unit: CssString::from(unit),\n        })\n    }\n}\n\n/// <https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-sum-value>\n#[derive(Clone, Debug)]\npub struct SumValue(Vec<SumValueItem>);\n\nimpl SumValue {\n    /// <https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value>\n    pub fn try_from_numeric_value(value: &NumericValue) -> Result<Self, ()> {\n        match value {\n            // CSSUnitValue\n            NumericValue::Unit(unit_value) => {\n                // Step 1.\n                let mut value = unit_value.value;\n                let mut unit = unit_value.unit.to_string();\n\n                // Step2.\n                let numeric = NoCalcNumeric::parse_unit_value(value, unit.as_str())?;\n                if let Some(canonical_unit) = numeric.canonical_unit() {\n                    let canonical = numeric.to(canonical_unit)?;\n                    value = canonical.unitless_value();\n                    unit = canonical.unit().to_string();\n                }\n\n                // Step 3.\n                if unit.eq_ignore_ascii_case(\"number\") {\n                    return Ok(Self(vec![SumValueItem {\n                        value,\n                        unit_map: UnitMap::new(),\n                    }]));\n                }\n\n                // Step 4.\n                Ok(Self(vec![SumValueItem {\n                    value,\n                    unit_map: [(unit, 1)].into_iter().collect::<UnitMap>(),\n                }]))\n            },\n\n            // CSSMathSum\n            NumericValue::Sum(math_sum) => {\n                // Step 1.\n                let mut values: Vec<SumValueItem> = Vec::new();\n\n                // Step 2.\n                for item in &math_sum.values {\n                    // Step 2.1.\n                    let value = SumValue::try_from_numeric_value(item)?;\n\n                    // Step 2.2.\n                    for sub_value in value.0 {\n                        // Step 2.2.1.\n                        if let Some(item) = values\n                            .iter_mut()\n                            .find(|item| item.unit_map == sub_value.unit_map)\n                        {\n                            item.value += sub_value.value;\n                            continue;\n                        }\n\n                        // Step 2.2.2.\n                        values.push(sub_value);\n                    }\n                }\n\n                // Step 3.\n\n                // TODO: Create a type [1] from the unit map of each item of\n                // values, and add [2] all the types together. If the result is\n                // failure, return failure.\n                //\n                // [1] https://drafts.css-houdini.org/css-typed-om-1/#create-a-type-from-a-unit-map\n                // [2] https://drafts.css-houdini.org/css-typed-om-1/#cssnumericvalue-add-two-types\n\n                // Step 4.\n                Ok(SumValue(values))\n            },\n        }\n    }\n\n    /// Step 3 of:\n    /// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssnumericvalue-to\n    pub fn resolve_to_unit(&self, unit: &str) -> Result<UnitValue, ()> {\n        if self.0.len() != 1 {\n            return Err(());\n        }\n\n        let sole_item = &self.0[0];\n\n        let item = sole_item.to_unit_value()?;\n\n        let item = {\n            let numeric =\n                NoCalcNumeric::parse_unit_value(item.value, item.unit.to_string().as_str())?;\n            let converted = numeric.to(unit)?;\n\n            UnitValue {\n                value: converted.unitless_value(),\n                unit: CssString::from(converted.unit()),\n            }\n        };\n\n        Ok(item)\n    }\n}\n"
  },
  {
    "path": "style/use_counters/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Various stuff for CSS property use counters.\n\nuse crate::properties::{property_counts, CountedUnknownProperty, NonCustomPropertyId};\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\n#[cfg(target_pointer_width = \"64\")]\nconst BITS_PER_ENTRY: usize = 64;\n\n#[cfg(target_pointer_width = \"32\")]\nconst BITS_PER_ENTRY: usize = 32;\n\n/// One bit per each non-custom CSS property.\n#[derive(Debug, Default)]\npub struct CountedUnknownPropertyUseCounters {\n    storage:\n        [AtomicUsize; (property_counts::COUNTED_UNKNOWN + BITS_PER_ENTRY - 1) / BITS_PER_ENTRY],\n}\n\n/// One bit per each non-custom CSS property.\n#[derive(Debug, Default)]\npub struct NonCustomPropertyUseCounters {\n    storage: [AtomicUsize; (property_counts::NON_CUSTOM + BITS_PER_ENTRY - 1) / BITS_PER_ENTRY],\n}\n\n/// A custom style use counter that we may want to record.\n#[derive(Copy, Clone, Debug)]\n#[repr(u32)]\npub enum CustomUseCounter {\n    /// Whether we reference a non-local uri at all.\n    HasNonLocalUriDependency = 0,\n    /// Whether we are likely to be using relative URIs that depend on our path depth.\n    MaybeHasPathBaseUriDependency,\n    /// Whether we are likely to be using relative URIs that depend on our full URI.\n    MaybeHasFullBaseUriDependency,\n    /// Dummy value, used for indexing purposes.\n    Last,\n}\n\nimpl CustomUseCounter {\n    #[inline]\n    fn bit(self) -> usize {\n        self as usize\n    }\n}\n\n/// One bit for each custom use counter.\n#[derive(Debug, Default)]\npub struct CustomUseCounters {\n    storage:\n        [AtomicUsize; ((CustomUseCounter::Last as usize) + BITS_PER_ENTRY - 1) / BITS_PER_ENTRY],\n}\n\nmacro_rules! use_counters_methods {\n    ($id: ident) => {\n        /// Returns the bucket a given property belongs in, and the bitmask for that\n        /// property.\n        #[inline(always)]\n        fn bucket_and_pattern(id: $id) -> (usize, usize) {\n            let bit = id.bit();\n            let bucket = bit / BITS_PER_ENTRY;\n            let bit_in_bucket = bit % BITS_PER_ENTRY;\n            (bucket, 1 << bit_in_bucket)\n        }\n\n        /// Record that a given property ID has been parsed.\n        #[inline]\n        pub fn record(&self, id: $id) {\n            let (bucket, pattern) = Self::bucket_and_pattern(id);\n            let bucket = &self.storage[bucket];\n            bucket.fetch_or(pattern, Ordering::Relaxed);\n        }\n\n        /// Returns whether a given property ID has been recorded\n        /// earlier.\n        #[inline]\n        pub fn recorded(&self, id: $id) -> bool {\n            let (bucket, pattern) = Self::bucket_and_pattern(id);\n            self.storage[bucket].load(Ordering::Relaxed) & pattern != 0\n        }\n\n        /// Merge `other` into `self`.\n        #[inline]\n        fn merge(&self, other: &Self) {\n            for (bucket, other_bucket) in self.storage.iter().zip(other.storage.iter()) {\n                bucket.fetch_or(other_bucket.load(Ordering::Relaxed), Ordering::Relaxed);\n            }\n        }\n    };\n}\n\nimpl CountedUnknownPropertyUseCounters {\n    use_counters_methods!(CountedUnknownProperty);\n}\n\nimpl NonCustomPropertyUseCounters {\n    use_counters_methods!(NonCustomPropertyId);\n}\n\nimpl CustomUseCounters {\n    use_counters_methods!(CustomUseCounter);\n}\n\n/// The use-counter data related to a given document we want to store.\n#[derive(Debug, Default)]\npub struct UseCounters {\n    /// The counters for non-custom properties that have been parsed in the\n    /// document's stylesheets.\n    pub non_custom_properties: NonCustomPropertyUseCounters,\n    /// The counters for css properties which we haven't implemented yet.\n    pub counted_unknown_properties: CountedUnknownPropertyUseCounters,\n    /// Custom counters for virtually everything else.\n    pub custom: CustomUseCounters,\n}\n\nimpl UseCounters {\n    /// Merge the use counters.\n    ///\n    /// Used for parallel parsing, where we parse off-main-thread.\n    #[inline]\n    pub fn merge(&self, other: &Self) {\n        self.non_custom_properties\n            .merge(&other.non_custom_properties);\n        self.counted_unknown_properties\n            .merge(&other.counted_unknown_properties);\n        self.custom.merge(&other.custom);\n    }\n}\n\nimpl Clone for UseCounters {\n    fn clone(&self) -> Self {\n        let result = Self::default();\n        result.merge(self);\n        result\n    }\n}\n"
  },
  {
    "path": "style/values/animated/color.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animated types for CSS colors.\n\nuse style_traits::owned_slice::OwnedSlice;\n\nuse crate::color::mix::ColorInterpolationMethod;\nuse crate::color::AbsoluteColor;\nuse crate::values::animated::{Animate, Procedure, ToAnimatedZero};\nuse crate::values::computed::Percentage;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::color::{\n    ColorMixFlags, GenericColor, GenericColorMix, GenericColorMixItem,\n};\n\nimpl Animate for AbsoluteColor {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use crate::color::mix;\n\n        let (left_weight, right_weight) = procedure.weights();\n\n        Ok(mix::mix_many(\n            ColorInterpolationMethod::best_interpolation_between(self, other),\n            [\n                mix::ColorMixItem::new(*self, left_weight as f32),\n                mix::ColorMixItem::new(*other, right_weight as f32),\n            ],\n            ColorMixFlags::empty(),\n        ))\n    }\n}\n\nimpl ComputeSquaredDistance for AbsoluteColor {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let start = [\n            self.alpha,\n            self.components.0 * self.alpha,\n            self.components.1 * self.alpha,\n            self.components.2 * self.alpha,\n        ];\n        let end = [\n            other.alpha,\n            other.components.0 * other.alpha,\n            other.components.1 * other.alpha,\n            other.components.2 * other.alpha,\n        ];\n        start\n            .iter()\n            .zip(&end)\n            .map(|(this, other)| this.compute_squared_distance(other))\n            .sum()\n    }\n}\n\n/// An animated value for `<color>`.\npub type Color = GenericColor<Percentage>;\n\n/// An animated value for `<color-mix>`.\npub type ColorMix = GenericColorMix<Color, Percentage>;\n\nimpl Animate for Color {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let (left_weight, right_weight) = procedure.weights();\n\n        Ok(Self::from_color_mix(ColorMix {\n            interpolation: ColorInterpolationMethod::srgb(),\n            items: OwnedSlice::from_slice(&[\n                GenericColorMixItem {\n                    color: self.clone(),\n                    percentage: Percentage(left_weight as f32),\n                },\n                GenericColorMixItem {\n                    color: other.clone(),\n                    percentage: Percentage(right_weight as f32),\n                },\n            ]),\n            // See https://github.com/w3c/csswg-drafts/issues/7324\n            flags: ColorMixFlags::empty(),\n        }))\n    }\n}\n\nimpl ComputeSquaredDistance for Color {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let current_color = AbsoluteColor::TRANSPARENT_BLACK;\n        self.resolve_to_absolute(&current_color)\n            .compute_squared_distance(&other.resolve_to_absolute(&current_color))\n    }\n}\n\nimpl ToAnimatedZero for Color {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(Color::Absolute(AbsoluteColor::TRANSPARENT_BLACK))\n    }\n}\n"
  },
  {
    "path": "style/values/animated/effects.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animated types for CSS values related to effects.\n\nuse crate::values::animated::color::Color;\nuse crate::values::computed::length::Length;\n#[cfg(feature = \"gecko\")]\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Angle, NonNegativeLength, Number};\nuse crate::values::generics::effects::Filter as GenericFilter;\nuse crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;\n#[cfg(not(feature = \"gecko\"))]\nuse crate::values::Impossible;\n\n/// An animated value for the `drop-shadow()` filter.\npub type AnimatedSimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;\n\n/// An animated value for a single `filter`.\n#[cfg(feature = \"gecko\")]\npub type AnimatedFilter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, ComputedUrl>;\n\n/// An animated value for a single `filter`.\n#[cfg(not(feature = \"gecko\"))]\npub type AnimatedFilter = GenericFilter<Angle, Number, Length, AnimatedSimpleShadow, Impossible>;\n"
  },
  {
    "path": "style/values/animated/font.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animation implementation for various font-related types.\n\nuse super::{Animate, Procedure, ToAnimatedZero};\nuse crate::values::computed::font::FontVariationSettings;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\n\n/// <https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def>\n///\n/// Note that the ComputedValue implementation will already have sorted and de-dup'd\n/// the lists of settings, so we can just iterate over the two lists together and\n/// animate their individual values.\nimpl Animate for FontVariationSettings {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let result: Box<[_]> =\n            super::lists::by_computed_value::animate(&self.0, &other.0, procedure)?;\n        Ok(Self(result))\n    }\n}\n\nimpl ComputeSquaredDistance for FontVariationSettings {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        super::lists::by_computed_value::squared_distance(&self.0, &other.0)\n    }\n}\n\nimpl ToAnimatedZero for FontVariationSettings {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n"
  },
  {
    "path": "style/values/animated/grid.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animation implementation for various grid-related types.\n\n// Note: we can implement Animate on their generic types directly, but in this case we need to\n// make sure two trait bounds, L: Clone and I: PartialEq, are satisfied on almost all the\n// grid-related types and their other trait implementations because Animate needs them. So in\n// order to avoid adding these two trait bounds (or maybe more..) everywhere, we implement\n// Animate for the computed types, instead of the generic types.\n\nuse super::{Animate, Procedure, ToAnimatedZero};\nuse crate::values::computed::Integer;\nuse crate::values::computed::LengthPercentage;\nuse crate::values::computed::{GridTemplateComponent, TrackList, TrackSize};\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::grid as generics;\n\nfn discrete<T: Clone>(from: &T, to: &T, procedure: Procedure) -> Result<T, ()> {\n    if let Procedure::Interpolate { progress } = procedure {\n        Ok(if progress < 0.5 {\n            from.clone()\n        } else {\n            to.clone()\n        })\n    } else {\n        // The discrete animation is not additive, so per spec [1] we should use the |from|, which\n        // is the underlying value. However this mismatches our animation mechanism (see\n        // composite_endpoint() in servo/ports/geckolib/glues.rs), which uses the effect value\n        // (i.e. |to| value here) [2]. So in order to match the behavior of other properties and\n        // other browsers, we use |to| value for addition and accumulation, i.e. Vresult = Vb.\n        //\n        // [1] https://drafts.csswg.org/css-values-4/#not-additive\n        // [2] https://github.com/w3c/csswg-drafts/issues/9070\n        Ok(to.clone())\n    }\n}\n\nfn animate_with_discrete_fallback<T: Animate + Clone>(\n    from: &T,\n    to: &T,\n    procedure: Procedure,\n) -> Result<T, ()> {\n    from.animate(to, procedure)\n        .or_else(|_| discrete(from, to, procedure))\n}\n\nimpl Animate for TrackSize {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match (self, other) {\n            (&generics::TrackSize::Breadth(ref from), &generics::TrackSize::Breadth(ref to)) => {\n                animate_with_discrete_fallback(from, to, procedure)\n                    .map(generics::TrackSize::Breadth)\n            },\n            (\n                &generics::TrackSize::Minmax(ref from_min, ref from_max),\n                &generics::TrackSize::Minmax(ref to_min, ref to_max),\n            ) => Ok(generics::TrackSize::Minmax(\n                animate_with_discrete_fallback(from_min, to_min, procedure)?,\n                animate_with_discrete_fallback(from_max, to_max, procedure)?,\n            )),\n            (\n                &generics::TrackSize::FitContent(ref from),\n                &generics::TrackSize::FitContent(ref to),\n            ) => animate_with_discrete_fallback(from, to, procedure)\n                .map(generics::TrackSize::FitContent),\n            (_, _) => discrete(self, other, procedure),\n        }\n    }\n}\n\nimpl Animate for generics::TrackRepeat<LengthPercentage, Integer> {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        // If the keyword, auto-fit/fill, is the same it can result in different\n        // number of tracks. For both auto-fit/fill, the number of columns isn't\n        // known until you do layout since it depends on the container size, item\n        // placement and other factors, so we cannot do the correct interpolation\n        // by computed values. Therefore, return Err(()) if it's keywords. If it\n        // is Number, we support animation only if the count is the same and the\n        // length of track_sizes is the same.\n        // https://github.com/w3c/csswg-drafts/issues/3503\n        match (&self.count, &other.count) {\n            (&generics::RepeatCount::Number(from), &generics::RepeatCount::Number(to))\n                if from == to =>\n            {\n                ()\n            },\n            (_, _) => return Err(()),\n        }\n\n        let count = self.count;\n        let track_sizes = super::lists::by_computed_value::animate(\n            &self.track_sizes,\n            &other.track_sizes,\n            procedure,\n        )?;\n\n        // The length of |line_names| is always 0 or N+1, where N is the length\n        // of |track_sizes|. Besides, <line-names> is always discrete.\n        let line_names = discrete(&self.line_names, &other.line_names, procedure)?;\n\n        Ok(generics::TrackRepeat {\n            count,\n            line_names,\n            track_sizes,\n        })\n    }\n}\n\nimpl Animate for TrackList {\n    // Based on https://github.com/w3c/csswg-drafts/issues/3201:\n    // 1. Check interpolation type per track, so we need to handle discrete animations\n    //    in TrackSize, so any Err(()) returned from TrackSize doesn't make all TrackSize\n    //    fallback to discrete animation.\n    // 2. line-names is always discrete.\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.values.len() != other.values.len() {\n            return Err(());\n        }\n\n        if self.is_explicit() != other.is_explicit() {\n            return Err(());\n        }\n\n        // For now, repeat(auto-fill/auto-fit, ...) is not animatable.\n        // TrackRepeat will return Err(()) if we use keywords. Therefore, we can\n        // early return here to avoid traversing |values| in <auto-track-list>.\n        // This may be updated in the future.\n        // https://github.com/w3c/csswg-drafts/issues/3503\n        if self.has_auto_repeat() || other.has_auto_repeat() {\n            return Err(());\n        }\n\n        let values =\n            super::lists::by_computed_value::animate(&self.values, &other.values, procedure)?;\n\n        // The length of |line_names| is always 0 or N+1, where N is the length\n        // of |track_sizes|. Besides, <line-names> is always discrete.\n        let line_names = discrete(&self.line_names, &other.line_names, procedure)?;\n\n        Ok(TrackList {\n            values,\n            line_names,\n            auto_repeat_index: self.auto_repeat_index,\n        })\n    }\n}\n\nimpl ComputeSquaredDistance for GridTemplateComponent {\n    #[inline]\n    fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {\n        // TODO: Bug 1518585, we should implement ComputeSquaredDistance.\n        Err(())\n    }\n}\n\nimpl ToAnimatedZero for GridTemplateComponent {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        // It's not clear to get a zero grid track list based on the current definition\n        // of spec, so we return Err(()) directly.\n        Err(())\n    }\n}\n"
  },
  {
    "path": "style/values/animated/lists.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Lists have various ways of being animated, this module implements them.\n//!\n//! See https://drafts.csswg.org/web-animations-1/#animating-properties\n\n/// https://drafts.csswg.org/web-animations-1/#by-computed-value\npub mod by_computed_value {\n    use crate::values::{\n        animated::{Animate, Procedure},\n        distance::{ComputeSquaredDistance, SquaredDistance},\n    };\n    use std::iter::FromIterator;\n\n    #[allow(missing_docs)]\n    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>\n    where\n        T: Animate,\n        C: FromIterator<T>,\n    {\n        if left.len() != right.len() {\n            return Err(());\n        }\n        left.iter()\n            .zip(right.iter())\n            .map(|(left, right)| left.animate(right, procedure))\n            .collect()\n    }\n\n    #[allow(missing_docs)]\n    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>\n    where\n        T: ComputeSquaredDistance,\n    {\n        if left.len() != right.len() {\n            return Err(());\n        }\n        left.iter()\n            .zip(right.iter())\n            .map(|(left, right)| left.compute_squared_distance(right))\n            .sum()\n    }\n}\n\n/// This is the animation used for some of the types like shadows and filters, where the\n/// interpolation happens with the zero value if one of the sides is not present.\n///\n/// https://drafts.csswg.org/web-animations-1/#animating-shadow-lists\npub mod with_zero {\n    use crate::values::animated::ToAnimatedZero;\n    use crate::values::{\n        animated::{Animate, Procedure},\n        distance::{ComputeSquaredDistance, SquaredDistance},\n    };\n    use itertools::{EitherOrBoth, Itertools};\n    use std::iter::FromIterator;\n\n    #[allow(missing_docs)]\n    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>\n    where\n        T: Animate + Clone + ToAnimatedZero,\n        C: FromIterator<T>,\n    {\n        if procedure == Procedure::Add {\n            return Ok(left.iter().chain(right.iter()).cloned().collect());\n        }\n        left.iter()\n            .zip_longest(right.iter())\n            .map(|it| match it {\n                EitherOrBoth::Both(left, right) => left.animate(right, procedure),\n                EitherOrBoth::Left(left) => left.animate(&left.to_animated_zero()?, procedure),\n                EitherOrBoth::Right(right) => right.to_animated_zero()?.animate(right, procedure),\n            })\n            .collect()\n    }\n\n    #[allow(missing_docs)]\n    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>\n    where\n        T: ToAnimatedZero + ComputeSquaredDistance,\n    {\n        left.iter()\n            .zip_longest(right.iter())\n            .map(|it| match it {\n                EitherOrBoth::Both(left, right) => left.compute_squared_distance(right),\n                EitherOrBoth::Left(item) | EitherOrBoth::Right(item) => {\n                    item.to_animated_zero()?.compute_squared_distance(item)\n                },\n            })\n            .sum()\n    }\n}\n\n/// https://drafts.csswg.org/web-animations-1/#repeatable-list\npub mod repeatable_list {\n    use crate::values::{\n        animated::{Animate, Procedure},\n        distance::{ComputeSquaredDistance, SquaredDistance},\n    };\n    use std::iter::FromIterator;\n\n    #[allow(missing_docs)]\n    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>\n    where\n        T: Animate,\n        C: FromIterator<T>,\n    {\n        use num_integer::lcm;\n        // If the length of either list is zero, the least common multiple is undefined.\n        if left.is_empty() || right.is_empty() {\n            return Err(());\n        }\n        let len = lcm(left.len(), right.len());\n        left.iter()\n            .cycle()\n            .zip(right.iter().cycle())\n            .take(len)\n            .map(|(left, right)| left.animate(right, procedure))\n            .collect()\n    }\n\n    #[allow(missing_docs)]\n    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>\n    where\n        T: ComputeSquaredDistance,\n    {\n        use num_integer::lcm;\n        if left.is_empty() || right.is_empty() {\n            return Err(());\n        }\n        let len = lcm(left.len(), right.len());\n        left.iter()\n            .cycle()\n            .zip(right.iter().cycle())\n            .take(len)\n            .map(|(left, right)| left.compute_squared_distance(right))\n            .sum()\n    }\n}\n"
  },
  {
    "path": "style/values/animated/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animated values.\n//!\n//! Some values, notably colors, cannot be interpolated directly with their\n//! computed values and need yet another intermediate representation. This\n//! module's raison d'être is to ultimately contain all these types.\n\nuse crate::color::AbsoluteColor;\nuse crate::properties::{ComputedValues, PropertyId};\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Angle, Image, Length};\nuse crate::values::generics::{ClampToNonNegative, NonNegative};\nuse crate::values::specified::SVGPathData;\nuse crate::values::CSSFloat;\nuse app_units::Au;\nuse smallvec::SmallVec;\nuse std::cmp;\n\npub mod color;\npub mod effects;\nmod font;\nmod grid;\npub mod lists;\nmod svg;\npub mod transform;\n\n/// The category a property falls into for ordering purposes.\n///\n/// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes\n#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]\nenum PropertyCategory {\n    Custom,\n    PhysicalLonghand,\n    LogicalLonghand,\n    Shorthand,\n}\n\nimpl PropertyCategory {\n    fn of(id: &PropertyId) -> Self {\n        match *id {\n            PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {\n                Ok(id) => {\n                    if id.is_logical() {\n                        PropertyCategory::LogicalLonghand\n                    } else {\n                        PropertyCategory::PhysicalLonghand\n                    }\n                },\n                Err(..) => PropertyCategory::Shorthand,\n            },\n            PropertyId::Custom(..) => PropertyCategory::Custom,\n        }\n    }\n}\n\n/// A comparator to sort PropertyIds such that physical longhands are sorted\n/// before logical longhands and shorthands, shorthands with fewer components\n/// are sorted before shorthands with more components, and otherwise shorthands\n/// are sorted by IDL name as defined by [Web Animations][property-order].\n///\n/// Using this allows us to prioritize values specified by longhands (or smaller\n/// shorthand subsets) when longhands and shorthands are both specified on the\n/// one keyframe.\n///\n/// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes\npub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {\n    let a_category = PropertyCategory::of(a);\n    let b_category = PropertyCategory::of(b);\n\n    if a_category != b_category {\n        return a_category.cmp(&b_category);\n    }\n\n    if a_category != PropertyCategory::Shorthand {\n        return cmp::Ordering::Equal;\n    }\n\n    let a = a.as_shorthand().unwrap();\n    let b = b.as_shorthand().unwrap();\n    // Within shorthands, sort by the number of subproperties, then by IDL\n    // name.\n    let subprop_count_a = a.longhands().count();\n    let subprop_count_b = b.longhands().count();\n    subprop_count_a\n        .cmp(&subprop_count_b)\n        .then_with(|| a.idl_name_sort_order().cmp(&b.idl_name_sort_order()))\n}\n\n/// A helper function to animate two multiplicative factor.\npub fn animate_multiplicative_factor(\n    this: CSSFloat,\n    other: CSSFloat,\n    procedure: Procedure,\n) -> Result<CSSFloat, ()> {\n    Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.)\n}\n\n/// Animate from one value to another.\n///\n/// This trait is derivable with `#[derive(Animate)]`. The derived\n/// implementation uses a `match` expression with identical patterns for both\n/// `self` and `other`, calling `Animate::animate` on each fields of the values.\n/// If a field is annotated with `#[animation(constant)]`, the two values should\n/// be equal or an error is returned.\n///\n/// If a variant is annotated with `#[animation(error)]`, the corresponding\n/// `match` arm returns an error.\n///\n/// Trait bounds for type parameter `Foo` can be opted out of with\n/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for\n/// fields can be opted into with `#[animation(field_bound)]` on the field.\npub trait Animate: Sized {\n    /// Animate a value towards another one, given an animation procedure.\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>;\n}\n\n/// An animation procedure.\n///\n/// <https://drafts.csswg.org/web-animations/#procedures-for-animating-properties>\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum Procedure {\n    /// <https://drafts.csswg.org/web-animations/#animation-interpolation>\n    Interpolate { progress: f64 },\n    /// <https://drafts.csswg.org/web-animations/#animation-addition>\n    Add,\n    /// <https://drafts.csswg.org/web-animations/#animation-accumulation>\n    Accumulate { count: u64 },\n}\n\n/// The context needed to provide an animated value from a computed value.\npub struct Context<'a> {\n    /// The computed style we're taking the value from.\n    pub style: &'a ComputedValues,\n}\n\n/// Conversion between computed values and intermediate values for animations.\n///\n/// Notably, colors are represented as four floats during animations.\n///\n/// This trait is derivable with `#[derive(ToAnimatedValue)]`.\npub trait ToAnimatedValue {\n    /// The type of the animated value.\n    type AnimatedValue;\n\n    /// Converts this value to an animated value.\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue;\n\n    /// Converts back an animated value into a computed value.\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self;\n}\n\n/// Returns a value similar to `self` that represents zero.\n///\n/// This trait is derivable with `#[derive(ToAnimatedValue)]`. If a field is\n/// annotated with `#[animation(constant)]`, a clone of its value will be used\n/// instead of calling `ToAnimatedZero::to_animated_zero` on it.\n///\n/// If a variant is annotated with `#[animation(error)]`, the corresponding\n/// `match` arm is not generated.\n///\n/// Trait bounds for type parameter `Foo` can be opted out of with\n/// `#[animation(no_bound(Foo))]` on the type definition.\npub trait ToAnimatedZero: Sized {\n    /// Returns a value that, when added with an underlying value, will produce the underlying\n    /// value. This is used for SMIL animation's \"by-animation\" where SMIL first interpolates from\n    /// the zero value to the 'by' value, and then adds the result to the underlying value.\n    ///\n    /// This is not the necessarily the same as the initial value of a property. For example, the\n    /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the\n    /// underlying value will not produce the underlying value.\n    fn to_animated_zero(&self) -> Result<Self, ()>;\n}\n\nimpl Procedure {\n    /// Returns this procedure as a pair of weights.\n    ///\n    /// This is useful for animations that don't animate differently\n    /// depending on the used procedure.\n    #[inline]\n    pub fn weights(self) -> (f64, f64) {\n        match self {\n            Procedure::Interpolate { progress } => (1. - progress, progress),\n            Procedure::Add => (1., 1.),\n            Procedure::Accumulate { count } => (count as f64, 1.),\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-transitions/#animtype-number>\nimpl Animate for i32 {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32)\n    }\n}\n\n/// <https://drafts.csswg.org/css-transitions/#animtype-number>\nimpl Animate for f32 {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let ret = (*self as f64).animate(&(*other as f64), procedure)?;\n        Ok(ret.min(f32::MAX as f64).max(f32::MIN as f64) as f32)\n    }\n}\n\n/// <https://drafts.csswg.org/css-transitions/#animtype-number>\nimpl Animate for f64 {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let (self_weight, other_weight) = procedure.weights();\n\n        let ret = *self * self_weight + *other * other_weight;\n        Ok(ret.min(f64::MAX).max(f64::MIN))\n    }\n}\n\nimpl<T> Animate for Option<T>\nwhere\n    T: Animate,\n{\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match (self.as_ref(), other.as_ref()) {\n            (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)),\n            (None, None) => Ok(None),\n            _ => Err(()),\n        }\n    }\n}\n\nimpl<T: ToAnimatedValue + ClampToNonNegative> ToAnimatedValue for NonNegative<T> {\n    type AnimatedValue = NonNegative<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, cx: &crate::values::animated::Context) -> Self::AnimatedValue {\n        NonNegative(self.0.to_animated_value(cx))\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Self(<T as ToAnimatedValue>::from_animated_value(animated.0).clamp_to_non_negative())\n    }\n}\n\nimpl ToAnimatedValue for Au {\n    type AnimatedValue = Length;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        Length::new(self.to_f32_px()).to_animated_value(context)\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Au::from_f32_px(Length::from_animated_value(animated).px())\n    }\n}\n\nimpl<T: Animate> Animate for Box<T> {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(Box::new((**self).animate(&other, procedure)?))\n    }\n}\n\nimpl<T> ToAnimatedValue for Option<T>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = Option<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.map(|v| T::to_animated_value(v, context))\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated.map(T::from_animated_value)\n    }\n}\n\nimpl<T> ToAnimatedValue for Vec<T>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = Vec<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.into_iter()\n            .map(|v| v.to_animated_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated.into_iter().map(T::from_animated_value).collect()\n    }\n}\n\nimpl<T> ToAnimatedValue for thin_vec::ThinVec<T>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = thin_vec::ThinVec<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.into_iter()\n            .map(|v| v.to_animated_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated.into_iter().map(T::from_animated_value).collect()\n    }\n}\n\nimpl<T> ToAnimatedValue for Box<T>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = Box<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        Box::new((*self).to_animated_value(context))\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Box::new(T::from_animated_value(*animated))\n    }\n}\n\nimpl<T> ToAnimatedValue for Box<[T]>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = Box<[<T as ToAnimatedValue>::AnimatedValue]>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.into_vec()\n            .into_iter()\n            .map(|v| v.to_animated_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated\n            .into_vec()\n            .into_iter()\n            .map(T::from_animated_value)\n            .collect()\n    }\n}\n\nimpl<T> ToAnimatedValue for crate::OwnedSlice<T>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = crate::OwnedSlice<<T as ToAnimatedValue>::AnimatedValue>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.into_box().to_animated_value(context).into()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Self::from(Box::from_animated_value(animated.into_box()))\n    }\n}\n\nimpl<T> ToAnimatedValue for SmallVec<[T; 1]>\nwhere\n    T: ToAnimatedValue,\n{\n    type AnimatedValue = SmallVec<[T::AnimatedValue; 1]>;\n\n    #[inline]\n    fn to_animated_value(self, context: &Context) -> Self::AnimatedValue {\n        self.into_iter()\n            .map(|v| v.to_animated_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated.into_iter().map(T::from_animated_value).collect()\n    }\n}\n\nmacro_rules! trivial_to_animated_value {\n    ($ty:ty) => {\n        impl $crate::values::animated::ToAnimatedValue for $ty {\n            type AnimatedValue = Self;\n\n            #[inline]\n            fn to_animated_value(self, _: &Context) -> Self {\n                self\n            }\n\n            #[inline]\n            fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n                animated\n            }\n        }\n    };\n}\n\ntrivial_to_animated_value!(crate::Atom);\ntrivial_to_animated_value!(Angle);\ntrivial_to_animated_value!(ComputedUrl);\ntrivial_to_animated_value!(bool);\ntrivial_to_animated_value!(f32);\ntrivial_to_animated_value!(i32);\ntrivial_to_animated_value!(u8);\ntrivial_to_animated_value!(u32);\ntrivial_to_animated_value!(usize);\ntrivial_to_animated_value!(AbsoluteColor);\ntrivial_to_animated_value!(crate::values::generics::color::ColorMixFlags);\n// Note: This implementation is for ToAnimatedValue of ShapeSource.\n//\n// SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the\n// types, we have to do \"impl ToAnimatedValue for Box<[T]>\" first.\n// However, the general version of \"impl ToAnimatedValue for Box<[T]>\" needs to\n// clone |T| and convert it into |T::AnimatedValue|. However, for SVGPathData\n// that is unnecessary--moving |T| is sufficient. So here, we implement this\n// trait manually.\ntrivial_to_animated_value!(SVGPathData);\n// FIXME: Bug 1514342, Image is not animatable, but we still need to implement\n// this to avoid adding this derive to generic::Image and all its arms. We can\n// drop this after landing Bug 1514342.\ntrivial_to_animated_value!(Image);\n\nimpl ToAnimatedZero for Au {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(Au(0))\n    }\n}\n\nimpl ToAnimatedZero for f32 {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(0.)\n    }\n}\n\nimpl ToAnimatedZero for f64 {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(0.)\n    }\n}\n\nimpl ToAnimatedZero for i32 {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(0)\n    }\n}\n\nimpl<T> ToAnimatedZero for Box<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(Box::new((**self).to_animated_zero()?))\n    }\n}\n\nimpl<T> ToAnimatedZero for Option<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        match *self {\n            Some(ref value) => Ok(Some(value.to_animated_zero()?)),\n            None => Ok(None),\n        }\n    }\n}\n\nimpl<T> ToAnimatedZero for Vec<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        self.iter().map(|v| v.to_animated_zero()).collect()\n    }\n}\n\nimpl<T> ToAnimatedZero for thin_vec::ThinVec<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        self.iter().map(|v| v.to_animated_zero()).collect()\n    }\n}\n\nimpl<T> ToAnimatedZero for Box<[T]>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        self.iter().map(|v| v.to_animated_zero()).collect()\n    }\n}\n\nimpl<T> ToAnimatedZero for crate::OwnedSlice<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        self.iter().map(|v| v.to_animated_zero()).collect()\n    }\n}\n\nimpl<T> ToAnimatedZero for crate::ArcSlice<T>\nwhere\n    T: ToAnimatedZero,\n{\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        let v = self\n            .iter()\n            .map(|v| v.to_animated_zero())\n            .collect::<Result<Vec<_>, _>>()?;\n        Ok(crate::ArcSlice::from_iter(v.into_iter()))\n    }\n}\n"
  },
  {
    "path": "style/values/animated/svg.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animation implementations for various SVG-related types.\n\nuse super::{Animate, Procedure};\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::svg::SVGStrokeDashArray;\n\n/// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty>\nimpl<L> Animate for SVGStrokeDashArray<L>\nwhere\n    L: Clone + Animate,\n{\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {\n            // Non-additive.\n            return Err(());\n        }\n        match (self, other) {\n            (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {\n                Ok(SVGStrokeDashArray::Values(\n                    super::lists::repeatable_list::animate(this, other, procedure)?,\n                ))\n            },\n            _ => Err(()),\n        }\n    }\n}\n\nimpl<L> ComputeSquaredDistance for SVGStrokeDashArray<L>\nwhere\n    L: ComputeSquaredDistance,\n{\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        match (self, other) {\n            (&SVGStrokeDashArray::Values(ref this), &SVGStrokeDashArray::Values(ref other)) => {\n                super::lists::repeatable_list::squared_distance(this, other)\n            },\n            _ => Err(()),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/animated/transform.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Animated types for transform.\n// There are still some implementation on Matrix3D in animated_properties.mako.rs\n// because they still need mako to generate the code.\n\nuse super::animate_multiplicative_factor;\nuse super::{Animate, Procedure, ToAnimatedZero};\nuse crate::derives::*;\nuse crate::values::computed::transform::Rotate as ComputedRotate;\nuse crate::values::computed::transform::Scale as ComputedScale;\nuse crate::values::computed::transform::Transform as ComputedTransform;\nuse crate::values::computed::transform::TransformOperation as ComputedTransformOperation;\nuse crate::values::computed::transform::Translate as ComputedTranslate;\nuse crate::values::computed::transform::{DirectionVector, Matrix, Matrix3D};\nuse crate::values::computed::Angle;\nuse crate::values::computed::{Length, LengthPercentage};\nuse crate::values::computed::{Number, Percentage};\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::transform::{self, Transform, TransformOperation};\nuse crate::values::generics::transform::{Rotate, Scale, Translate};\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse std::cmp;\nuse std::ops::Add;\n\n// ------------------------------------\n// Animations for Matrix/Matrix3D.\n// ------------------------------------\n/// A 2d matrix for interpolation.\n#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[allow(missing_docs)]\n// FIXME: We use custom derive for ComputeSquaredDistance. However, If possible, we should convert\n// the InnerMatrix2D into types with physical meaning. This custom derive computes the squared\n// distance from each matrix item, and this makes the result different from that in Gecko if we\n// have skew factor in the Matrix3D.\npub struct InnerMatrix2D {\n    pub m11: CSSFloat,\n    pub m12: CSSFloat,\n    pub m21: CSSFloat,\n    pub m22: CSSFloat,\n}\n\nimpl Animate for InnerMatrix2D {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(InnerMatrix2D {\n            m11: animate_multiplicative_factor(self.m11, other.m11, procedure)?,\n            m12: self.m12.animate(&other.m12, procedure)?,\n            m21: self.m21.animate(&other.m21, procedure)?,\n            m22: animate_multiplicative_factor(self.m22, other.m22, procedure)?,\n        })\n    }\n}\n\n/// A 2d translation function.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]\npub struct Translate2D(f32, f32);\n\n/// A 2d scale function.\n#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct Scale2D(f32, f32);\n\nimpl Animate for Scale2D {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(Scale2D(\n            animate_multiplicative_factor(self.0, other.0, procedure)?,\n            animate_multiplicative_factor(self.1, other.1, procedure)?,\n        ))\n    }\n}\n\n/// A decomposed 2d matrix.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct MatrixDecomposed2D {\n    /// The translation function.\n    pub translate: Translate2D,\n    /// The scale function.\n    pub scale: Scale2D,\n    /// The rotation angle.\n    pub angle: f32,\n    /// The inner matrix.\n    pub matrix: InnerMatrix2D,\n}\n\nimpl Animate for MatrixDecomposed2D {\n    /// <https://drafts.csswg.org/css-transforms/#interpolation-of-decomposed-2d-matrix-values>\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        // If x-axis of one is flipped, and y-axis of the other,\n        // convert to an unflipped rotation.\n        let mut scale = self.scale;\n        let mut angle = self.angle;\n        let mut other_angle = other.angle;\n        if (scale.0 < 0.0 && other.scale.1 < 0.0) || (scale.1 < 0.0 && other.scale.0 < 0.0) {\n            scale.0 = -scale.0;\n            scale.1 = -scale.1;\n            angle += if angle < 0.0 { 180. } else { -180. };\n        }\n\n        // Don't rotate the long way around.\n        if angle == 0.0 {\n            angle = 360.\n        }\n        if other_angle == 0.0 {\n            other_angle = 360.\n        }\n\n        if (angle - other_angle).abs() > 180. {\n            if angle > other_angle {\n                angle -= 360.\n            } else {\n                other_angle -= 360.\n            }\n        }\n\n        // Interpolate all values.\n        let translate = self.translate.animate(&other.translate, procedure)?;\n        let scale = scale.animate(&other.scale, procedure)?;\n        let angle = angle.animate(&other_angle, procedure)?;\n        let matrix = self.matrix.animate(&other.matrix, procedure)?;\n\n        Ok(MatrixDecomposed2D {\n            translate,\n            scale,\n            angle,\n            matrix,\n        })\n    }\n}\n\nimpl ComputeSquaredDistance for MatrixDecomposed2D {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        // Use Radian to compute the distance.\n        const RAD_PER_DEG: f64 = std::f64::consts::PI / 180.0;\n        let angle1 = self.angle as f64 * RAD_PER_DEG;\n        let angle2 = other.angle as f64 * RAD_PER_DEG;\n        Ok(self.translate.compute_squared_distance(&other.translate)?\n            + self.scale.compute_squared_distance(&other.scale)?\n            + angle1.compute_squared_distance(&angle2)?\n            + self.matrix.compute_squared_distance(&other.matrix)?)\n    }\n}\n\nimpl From<Matrix3D> for MatrixDecomposed2D {\n    /// Decompose a 2D matrix.\n    /// <https://drafts.csswg.org/css-transforms/#decomposing-a-2d-matrix>\n    fn from(matrix: Matrix3D) -> MatrixDecomposed2D {\n        let mut row0x = matrix.m11;\n        let mut row0y = matrix.m12;\n        let mut row1x = matrix.m21;\n        let mut row1y = matrix.m22;\n\n        let translate = Translate2D(matrix.m41, matrix.m42);\n        let mut scale = Scale2D(\n            (row0x * row0x + row0y * row0y).sqrt(),\n            (row1x * row1x + row1y * row1y).sqrt(),\n        );\n\n        // If determinant is negative, one axis was flipped.\n        let determinant = row0x * row1y - row0y * row1x;\n        if determinant < 0. {\n            if row0x < row1y {\n                scale.0 = -scale.0;\n            } else {\n                scale.1 = -scale.1;\n            }\n        }\n\n        // Renormalize matrix to remove scale.\n        if scale.0 != 0.0 {\n            row0x *= 1. / scale.0;\n            row0y *= 1. / scale.0;\n        }\n        if scale.1 != 0.0 {\n            row1x *= 1. / scale.1;\n            row1y *= 1. / scale.1;\n        }\n\n        // Compute rotation and renormalize matrix.\n        let mut angle = row0y.atan2(row0x);\n        if angle != 0.0 {\n            let sn = -row0y;\n            let cs = row0x;\n            let m11 = row0x;\n            let m12 = row0y;\n            let m21 = row1x;\n            let m22 = row1y;\n            row0x = cs * m11 + sn * m21;\n            row0y = cs * m12 + sn * m22;\n            row1x = -sn * m11 + cs * m21;\n            row1y = -sn * m12 + cs * m22;\n        }\n\n        let m = InnerMatrix2D {\n            m11: row0x,\n            m12: row0y,\n            m21: row1x,\n            m22: row1y,\n        };\n\n        // Convert into degrees because our rotation functions expect it.\n        angle = angle.to_degrees();\n        MatrixDecomposed2D {\n            translate: translate,\n            scale: scale,\n            angle: angle,\n            matrix: m,\n        }\n    }\n}\n\nimpl From<MatrixDecomposed2D> for Matrix3D {\n    /// Recompose a 2D matrix.\n    /// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-2d-matrix>\n    fn from(decomposed: MatrixDecomposed2D) -> Matrix3D {\n        let mut computed_matrix = Matrix3D::identity();\n        computed_matrix.m11 = decomposed.matrix.m11;\n        computed_matrix.m12 = decomposed.matrix.m12;\n        computed_matrix.m21 = decomposed.matrix.m21;\n        computed_matrix.m22 = decomposed.matrix.m22;\n\n        // Translate matrix.\n        computed_matrix.m41 = decomposed.translate.0;\n        computed_matrix.m42 = decomposed.translate.1;\n\n        // Rotate matrix.\n        let angle = decomposed.angle.to_radians();\n        let cos_angle = angle.cos();\n        let sin_angle = angle.sin();\n\n        let mut rotate_matrix = Matrix3D::identity();\n        rotate_matrix.m11 = cos_angle;\n        rotate_matrix.m12 = sin_angle;\n        rotate_matrix.m21 = -sin_angle;\n        rotate_matrix.m22 = cos_angle;\n\n        // Multiplication of computed_matrix and rotate_matrix\n        computed_matrix = rotate_matrix.multiply(&computed_matrix);\n\n        // Scale matrix.\n        computed_matrix.m11 *= decomposed.scale.0;\n        computed_matrix.m12 *= decomposed.scale.0;\n        computed_matrix.m21 *= decomposed.scale.1;\n        computed_matrix.m22 *= decomposed.scale.1;\n        computed_matrix\n    }\n}\n\nimpl Animate for Matrix {\n    #[cfg(feature = \"servo\")]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let this = Matrix3D::from(*self);\n        let other = Matrix3D::from(*other);\n        let this = MatrixDecomposed2D::from(this);\n        let other = MatrixDecomposed2D::from(other);\n        Matrix3D::from(this.animate(&other, procedure)?).into_2d()\n    }\n\n    #[cfg(feature = \"gecko\")]\n    // Gecko doesn't exactly follow the spec here; we use a different procedure\n    // to match it\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let this = Matrix3D::from(*self);\n        let other = Matrix3D::from(*other);\n        let from = decompose_2d_matrix(&this)?;\n        let to = decompose_2d_matrix(&other)?;\n        Matrix3D::from(from.animate(&to, procedure)?).into_2d()\n    }\n}\n\n/// A 3d translation.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]\npub struct Translate3D(pub f32, pub f32, pub f32);\n\n/// A 3d scale function.\n#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct Scale3D(pub f32, pub f32, pub f32);\n\nimpl Scale3D {\n    /// Negate self.\n    fn negate(&mut self) {\n        self.0 *= -1.0;\n        self.1 *= -1.0;\n        self.2 *= -1.0;\n    }\n}\n\nimpl Animate for Scale3D {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(Scale3D(\n            animate_multiplicative_factor(self.0, other.0, procedure)?,\n            animate_multiplicative_factor(self.1, other.1, procedure)?,\n            animate_multiplicative_factor(self.2, other.2, procedure)?,\n        ))\n    }\n}\n\n/// A 3d skew function.\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[derive(Animate, Clone, Copy, Debug)]\npub struct Skew(f32, f32, f32);\n\nimpl ComputeSquaredDistance for Skew {\n    // We have to use atan() to convert the skew factors into skew angles, so implement\n    // ComputeSquaredDistance manually.\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(self.0.atan().compute_squared_distance(&other.0.atan())?\n            + self.1.atan().compute_squared_distance(&other.1.atan())?\n            + self.2.atan().compute_squared_distance(&other.2.atan())?)\n    }\n}\n\n/// A 3d perspective transformation.\n#[derive(Clone, ComputeSquaredDistance, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct Perspective(pub f32, pub f32, pub f32, pub f32);\n\nimpl Animate for Perspective {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(Perspective(\n            self.0.animate(&other.0, procedure)?,\n            self.1.animate(&other.1, procedure)?,\n            self.2.animate(&other.2, procedure)?,\n            animate_multiplicative_factor(self.3, other.3, procedure)?,\n        ))\n    }\n}\n\n/// A quaternion used to represent a rotation.\n#[derive(Clone, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct Quaternion(f64, f64, f64, f64);\n\nimpl Quaternion {\n    /// Return a quaternion from a unit direction vector and angle (unit: radian).\n    #[inline]\n    fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {\n        debug_assert!(\n            (vector.length() - 1.).abs() < 0.0001,\n            \"Only accept an unit direction vector to create a quaternion\"\n        );\n\n        // Quaternions between the range [360, 720] will treated as rotations at the other\n        // direction: [-360, 0]. And quaternions between the range [720*k, 720*(k+1)] will be\n        // treated as rotations [0, 720]. So it does not make sense to use quaternions to rotate\n        // the element more than ±360deg. Therefore, we have to make sure its range is (-360, 360).\n        let half_angle = angle\n            .abs()\n            .rem_euclid(std::f64::consts::TAU)\n            .copysign(angle)\n            / 2.;\n\n        // Reference:\n        // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation\n        //\n        // if the direction axis is (x, y, z) = xi + yj + zk,\n        // and the angle is |theta|, this formula can be done using\n        // an extension of Euler's formula:\n        //   q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))\n        //     = cos(theta/2) +\n        //       x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k\n        Quaternion(\n            vector.x as f64 * half_angle.sin(),\n            vector.y as f64 * half_angle.sin(),\n            vector.z as f64 * half_angle.sin(),\n            half_angle.cos(),\n        )\n    }\n\n    /// Calculate the dot product.\n    #[inline]\n    fn dot(&self, other: &Self) -> f64 {\n        self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3\n    }\n\n    /// Return the scaled quaternion by a factor.\n    #[inline]\n    fn scale(&self, factor: f64) -> Self {\n        Quaternion(\n            self.0 * factor,\n            self.1 * factor,\n            self.2 * factor,\n            self.3 * factor,\n        )\n    }\n}\n\nimpl Add for Quaternion {\n    type Output = Self;\n\n    fn add(self, other: Self) -> Self {\n        Self(\n            self.0 + other.0,\n            self.1 + other.1,\n            self.2 + other.2,\n            self.3 + other.3,\n        )\n    }\n}\n\nimpl Animate for Quaternion {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let (this_weight, other_weight) = procedure.weights();\n        debug_assert!(\n            // Doule EPSILON since both this_weight and other_weght have calculation errors\n            // which are approximately equal to EPSILON.\n            (this_weight + other_weight - 1.0f64).abs() <= f64::EPSILON * 2.0\n                || other_weight == 1.0f64\n                || other_weight == 0.0f64,\n            \"animate should only be used for interpolating or accumulating transforms\"\n        );\n\n        // We take a specialized code path for accumulation (where other_weight\n        // is 1).\n        if let Procedure::Accumulate { .. } = procedure {\n            debug_assert_eq!(other_weight, 1.0);\n            if this_weight == 0.0 {\n                return Ok(*other);\n            }\n\n            let clamped_w = self.3.min(1.0).max(-1.0);\n\n            // Determine the scale factor.\n            let mut theta = clamped_w.acos();\n            let mut scale = if theta == 0.0 { 0.0 } else { 1.0 / theta.sin() };\n            theta *= this_weight;\n            scale *= theta.sin();\n\n            // Scale the self matrix by this_weight.\n            let mut scaled_self = *self;\n            scaled_self.0 *= scale;\n            scaled_self.1 *= scale;\n            scaled_self.2 *= scale;\n            scaled_self.3 = theta.cos();\n\n            // Multiply scaled-self by other.\n            let a = &scaled_self;\n            let b = other;\n            return Ok(Quaternion(\n                a.3 * b.0 + a.0 * b.3 + a.1 * b.2 - a.2 * b.1,\n                a.3 * b.1 - a.0 * b.2 + a.1 * b.3 + a.2 * b.0,\n                a.3 * b.2 + a.0 * b.1 - a.1 * b.0 + a.2 * b.3,\n                a.3 * b.3 - a.0 * b.0 - a.1 * b.1 - a.2 * b.2,\n            ));\n        }\n\n        // https://drafts.csswg.org/css-transforms-2/#interpolation-of-decomposed-3d-matrix-values\n        //\n        // Dot product, clamped between -1 and 1.\n        let cos_half_theta =\n            (self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3)\n                .min(1.0)\n                .max(-1.0);\n\n        if cos_half_theta.abs() == 1.0 {\n            return Ok(*self);\n        }\n\n        let half_theta = cos_half_theta.acos();\n        let sin_half_theta = (1.0 - cos_half_theta * cos_half_theta).sqrt();\n\n        let right_weight = (other_weight * half_theta).sin() / sin_half_theta;\n        // The spec would like to use\n        // \"(other_weight * half_theta).cos() - cos_half_theta * right_weight\". However, this\n        // formula may produce some precision issues of floating-point number calculation, e.g.\n        // when the progress is 100% (i.e. |other_weight| is 1), the |left_weight| may not be\n        // perfectly equal to 0. It could be something like -2.22e-16, which is approximately equal\n        // to zero, in the test. And after we recompose the Matrix3D, these approximated zeros\n        // make us failed to treat this Matrix3D as a Matrix2D, when serializating it.\n        //\n        // Therefore, we use another formula to calculate |left_weight| here. Blink and WebKit also\n        // use this formula, which is defined in:\n        // https://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/index.htm\n        // https://github.com/w3c/csswg-drafts/issues/9338\n        let left_weight = (this_weight * half_theta).sin() / sin_half_theta;\n\n        Ok(self.scale(left_weight) + other.scale(right_weight))\n    }\n}\n\nimpl ComputeSquaredDistance for Quaternion {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors,\n        // so we can get their angle difference by:\n        // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.\n        let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;\n        Ok(SquaredDistance::from_sqrt(distance))\n    }\n}\n\n/// A decomposed 3d matrix.\n#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\npub struct MatrixDecomposed3D {\n    /// A translation function.\n    pub translate: Translate3D,\n    /// A scale function.\n    pub scale: Scale3D,\n    /// The skew component of the transformation.\n    pub skew: Skew,\n    /// The perspective component of the transformation.\n    pub perspective: Perspective,\n    /// The quaternion used to represent the rotation.\n    pub quaternion: Quaternion,\n}\n\nimpl From<MatrixDecomposed3D> for Matrix3D {\n    /// Recompose a 3D matrix.\n    /// <https://drafts.csswg.org/css-transforms/#recomposing-to-a-3d-matrix>\n    fn from(decomposed: MatrixDecomposed3D) -> Matrix3D {\n        let mut matrix = Matrix3D::identity();\n\n        // Apply perspective\n        matrix.set_perspective(&decomposed.perspective);\n\n        // Apply translation\n        matrix.apply_translate(&decomposed.translate);\n\n        // Apply rotation\n        {\n            let x = decomposed.quaternion.0;\n            let y = decomposed.quaternion.1;\n            let z = decomposed.quaternion.2;\n            let w = decomposed.quaternion.3;\n\n            // Construct a composite rotation matrix from the quaternion values\n            // rotationMatrix is a identity 4x4 matrix initially\n            let mut rotation_matrix = Matrix3D::identity();\n            rotation_matrix.m11 = 1.0 - 2.0 * (y * y + z * z) as f32;\n            rotation_matrix.m12 = 2.0 * (x * y + z * w) as f32;\n            rotation_matrix.m13 = 2.0 * (x * z - y * w) as f32;\n            rotation_matrix.m21 = 2.0 * (x * y - z * w) as f32;\n            rotation_matrix.m22 = 1.0 - 2.0 * (x * x + z * z) as f32;\n            rotation_matrix.m23 = 2.0 * (y * z + x * w) as f32;\n            rotation_matrix.m31 = 2.0 * (x * z + y * w) as f32;\n            rotation_matrix.m32 = 2.0 * (y * z - x * w) as f32;\n            rotation_matrix.m33 = 1.0 - 2.0 * (x * x + y * y) as f32;\n\n            matrix = rotation_matrix.multiply(&matrix);\n        }\n\n        // Apply skew\n        {\n            let mut temp = Matrix3D::identity();\n            if decomposed.skew.2 != 0.0 {\n                temp.m32 = decomposed.skew.2;\n                matrix = temp.multiply(&matrix);\n                temp.m32 = 0.0;\n            }\n\n            if decomposed.skew.1 != 0.0 {\n                temp.m31 = decomposed.skew.1;\n                matrix = temp.multiply(&matrix);\n                temp.m31 = 0.0;\n            }\n\n            if decomposed.skew.0 != 0.0 {\n                temp.m21 = decomposed.skew.0;\n                matrix = temp.multiply(&matrix);\n            }\n        }\n\n        // Apply scale\n        matrix.apply_scale(&decomposed.scale);\n\n        matrix\n    }\n}\n\n/// Decompose a 3D matrix.\n/// https://drafts.csswg.org/css-transforms-2/#decomposing-a-3d-matrix\n/// http://www.realtimerendering.com/resources/GraphicsGems/gemsii/unmatrix.c\nfn decompose_3d_matrix(mut matrix: Matrix3D) -> Result<MatrixDecomposed3D, ()> {\n    // Combine 2 point.\n    let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {\n        [\n            (ascl * a[0]) + (bscl * b[0]),\n            (ascl * a[1]) + (bscl * b[1]),\n            (ascl * a[2]) + (bscl * b[2]),\n        ]\n    };\n    // Dot product.\n    let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];\n    // Cross product.\n    let cross = |row1: [f32; 3], row2: [f32; 3]| {\n        [\n            row1[1] * row2[2] - row1[2] * row2[1],\n            row1[2] * row2[0] - row1[0] * row2[2],\n            row1[0] * row2[1] - row1[1] * row2[0],\n        ]\n    };\n\n    if matrix.m44 == 0.0 {\n        return Err(());\n    }\n\n    let scaling_factor = matrix.m44;\n\n    // Normalize the matrix.\n    matrix.scale_by_factor(1.0 / scaling_factor);\n\n    // perspective_matrix is used to solve for perspective, but it also provides\n    // an easy way to test for singularity of the upper 3x3 component.\n    let mut perspective_matrix = matrix;\n\n    perspective_matrix.m14 = 0.0;\n    perspective_matrix.m24 = 0.0;\n    perspective_matrix.m34 = 0.0;\n    perspective_matrix.m44 = 1.0;\n\n    if perspective_matrix.determinant() == 0.0 {\n        return Err(());\n    }\n\n    // First, isolate perspective.\n    let perspective = if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {\n        let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];\n\n        perspective_matrix = perspective_matrix.inverse().unwrap().transpose();\n        let perspective = perspective_matrix.pre_mul_point4(&right_hand_side);\n        // NOTE(emilio): Even though the reference algorithm clears the\n        // fourth column here (matrix.m14..matrix.m44), they're not used below\n        // so it's not really needed.\n        Perspective(\n            perspective[0],\n            perspective[1],\n            perspective[2],\n            perspective[3],\n        )\n    } else {\n        Perspective(0.0, 0.0, 0.0, 1.0)\n    };\n\n    // Next take care of translation (easy).\n    let translate = Translate3D(matrix.m41, matrix.m42, matrix.m43);\n\n    // Now get scale and shear. 'row' is a 3 element array of 3 component vectors\n    let mut row = matrix.get_matrix_3x3_part();\n\n    // Compute X scale factor and normalize first row.\n    let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();\n    let mut scale = Scale3D(row0len, 0.0, 0.0);\n    row[0] = [\n        row[0][0] / row0len,\n        row[0][1] / row0len,\n        row[0][2] / row0len,\n    ];\n\n    // Compute XY shear factor and make 2nd row orthogonal to 1st.\n    let mut skew = Skew(dot(row[0], row[1]), 0.0, 0.0);\n    row[1] = combine(row[1], row[0], 1.0, -skew.0);\n\n    // Now, compute Y scale and normalize 2nd row.\n    let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();\n    scale.1 = row1len;\n    row[1] = [\n        row[1][0] / row1len,\n        row[1][1] / row1len,\n        row[1][2] / row1len,\n    ];\n    skew.0 /= scale.1;\n\n    // Compute XZ and YZ shears, orthogonalize 3rd row\n    skew.1 = dot(row[0], row[2]);\n    row[2] = combine(row[2], row[0], 1.0, -skew.1);\n    skew.2 = dot(row[1], row[2]);\n    row[2] = combine(row[2], row[1], 1.0, -skew.2);\n\n    // Next, get Z scale and normalize 3rd row.\n    let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();\n    scale.2 = row2len;\n    row[2] = [\n        row[2][0] / row2len,\n        row[2][1] / row2len,\n        row[2][2] / row2len,\n    ];\n    skew.1 /= scale.2;\n    skew.2 /= scale.2;\n\n    // At this point, the matrix (in rows) is orthonormal.\n    // Check for a coordinate system flip.  If the determinant\n    // is -1, then negate the matrix and the scaling factors.\n    if dot(row[0], cross(row[1], row[2])) < 0.0 {\n        scale.negate();\n        for i in 0..3 {\n            row[i][0] *= -1.0;\n            row[i][1] *= -1.0;\n            row[i][2] *= -1.0;\n        }\n    }\n\n    // Now, get the rotations out.\n    let mut quaternion = Quaternion(\n        0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),\n        0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0) as f64).sqrt(),\n        0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),\n        0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0) as f64).sqrt(),\n    );\n\n    if row[2][1] > row[1][2] {\n        quaternion.0 = -quaternion.0\n    }\n    if row[0][2] > row[2][0] {\n        quaternion.1 = -quaternion.1\n    }\n    if row[1][0] > row[0][1] {\n        quaternion.2 = -quaternion.2\n    }\n\n    Ok(MatrixDecomposed3D {\n        translate,\n        scale,\n        skew,\n        perspective,\n        quaternion,\n    })\n}\n\n/**\n * The relevant section of the transitions specification:\n * https://drafts.csswg.org/web-animations-1/#animation-types\n * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-\n * defers all of the details to the 2-D and 3-D transforms specifications.\n * For the 2-D transforms specification (all that's relevant for us, right\n * now), the relevant section is:\n * https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms\n * This, in turn, refers to the unmatrix program in Graphics Gems,\n * available from http://graphicsgems.org/ , and in\n * particular as the file GraphicsGems/gemsii/unmatrix.c\n * in http://graphicsgems.org/AllGems.tar.gz\n *\n * The unmatrix reference is for general 3-D transform matrices (any of the\n * 16 components can have any value).\n *\n * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:\n *\n * [ A C E ]\n * [ B D F ]\n * [ 0 0 1 ]\n *\n * For that case, I believe the algorithm in unmatrix reduces to:\n *\n *  (1) If A * D - B * C == 0, the matrix is singular.  Fail.\n *\n *  (2) Set translation components (Tx and Ty) to the translation parts of\n *      the matrix (E and F) and then ignore them for the rest of the time.\n *      (For us, E and F each actually consist of three constants:  a\n *      length, a multiplier for the width, and a multiplier for the\n *      height.  This actually requires its own decomposition, but I'll\n *      keep that separate.)\n *\n *  (3) Let the X scale (Sx) be sqrt(A^2 + B^2).  Then divide both A and B\n *      by it.\n *\n *  (4) Let the XY shear (K) be A * C + B * D.  From C, subtract A times\n *      the XY shear.  From D, subtract B times the XY shear.\n *\n *  (5) Let the Y scale (Sy) be sqrt(C^2 + D^2).  Divide C, D, and the XY\n *      shear (K) by it.\n *\n *  (6) At this point, A * D - B * C is either 1 or -1.  If it is -1,\n *      negate the XY shear (K), the X scale (Sx), and A, B, C, and D.\n *      (Alternatively, we could negate the XY shear (K) and the Y scale\n *      (Sy).)\n *\n *  (7) Let the rotation be R = atan2(B, A).\n *\n * Then the resulting decomposed transformation is:\n *\n *   translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)\n *\n * An interesting result of this is that all of the simple transform\n * functions (i.e., all functions other than matrix()), in isolation,\n * decompose back to themselves except for:\n *   'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes\n *   to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the\n *   alternate sign possibilities that would get fixed in step 6):\n *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) =\n * sec(φ). Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) =\n * sin(φ). In step 4, the XY shear is sin(φ). Thus, after step 4, C =\n * -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ). Thus, in step 5, the Y scale is\n * sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ). Thus, after step 5, C = -sin(φ), D\n * = cos(φ), and the XY shear is tan(φ). Thus, in step 6, A * D - B * C =\n * cos²(φ) + sin²(φ) = 1. In step 7, the rotation is thus φ.\n *\n *   skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes\n *   to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring\n *   the alternate sign possibilities that would get fixed in step 6):\n *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) =\n * sec(φ). Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) =\n * sin(φ). In step 4, the XY shear is cos(φ)tan(θ) + sin(φ). Thus, after step 4,\n *     C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)\n *     D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)\n *     Thus, in step 5, the Y scale is sqrt(C² + D²) =\n *     sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -\n *          2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +\n *          (sin²(φ)cos²(φ) + cos⁴(φ))) =\n *     sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =\n *     cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so\n *     we avoid flipping in step 6).\n *     After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is\n *     (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =\n *     (dividing both numerator and denominator by cos(φ))\n *     (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).\n *     (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)\n *     Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.\n *     In step 7, the rotation is thus φ.\n *\n *     To check this result, we can multiply things back together:\n *\n *     [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ)    0   ]\n *     [ sin(φ)  cos(φ) ] [ 0      1     ] [   0    cos(φ) ]\n *\n *     [ cos(φ)      cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ)    0   ]\n *     [ sin(φ)      sin(φ)tan(θ + φ) + cos(φ) ] [   0    cos(φ) ]\n *\n *     but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),\n *     cos(φ)tan(θ + φ) - sin(φ)\n *      = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)\n *      = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)\n *      = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)\n *      = tan(θ) (cos(φ) + sin(φ)tan(φ))\n *      = tan(θ) sec(φ) (cos²(φ) + sin²(φ))\n *      = tan(θ) sec(φ)\n *     and\n *     sin(φ)tan(θ + φ) + cos(φ)\n *      = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)\n *      = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)\n *      = sec(φ) (sin²(φ) + cos²(φ))\n *      = sec(φ)\n *     so the above is:\n *     [ cos(φ)  tan(θ) sec(φ) ] [ sec(φ)    0   ]\n *     [ sin(φ)     sec(φ)     ] [   0    cos(φ) ]\n *\n *     [    1   tan(θ) ]\n *     [ tan(φ)    1   ]\n */\n\n/// Decompose a 2D matrix for Gecko. This implements the above decomposition algorithm.\n#[cfg(feature = \"gecko\")]\nfn decompose_2d_matrix(matrix: &Matrix3D) -> Result<MatrixDecomposed3D, ()> {\n    // The index is column-major, so the equivalent transform matrix is:\n    // | m11 m21  0 m41 |  =>  | m11 m21 | and translate(m41, m42)\n    // | m12 m22  0 m42 |      | m12 m22 |\n    // |   0   0  1   0 |\n    // |   0   0  0   1 |\n    let (mut m11, mut m12) = (matrix.m11, matrix.m12);\n    let (mut m21, mut m22) = (matrix.m21, matrix.m22);\n    // Check if this is a singular matrix.\n    if m11 * m22 == m12 * m21 {\n        return Err(());\n    }\n\n    let mut scale_x = (m11 * m11 + m12 * m12).sqrt();\n    m11 /= scale_x;\n    m12 /= scale_x;\n\n    let mut shear_xy = m11 * m21 + m12 * m22;\n    m21 -= m11 * shear_xy;\n    m22 -= m12 * shear_xy;\n\n    let scale_y = (m21 * m21 + m22 * m22).sqrt();\n    m21 /= scale_y;\n    m22 /= scale_y;\n    shear_xy /= scale_y;\n\n    let determinant = m11 * m22 - m12 * m21;\n    // Determinant should now be 1 or -1.\n    if 0.99 > determinant.abs() || determinant.abs() > 1.01 {\n        return Err(());\n    }\n\n    if determinant < 0. {\n        m11 = -m11;\n        m12 = -m12;\n        shear_xy = -shear_xy;\n        scale_x = -scale_x;\n    }\n\n    Ok(MatrixDecomposed3D {\n        translate: Translate3D(matrix.m41, matrix.m42, 0.),\n        scale: Scale3D(scale_x, scale_y, 1.),\n        skew: Skew(shear_xy, 0., 0.),\n        perspective: Perspective(0., 0., 0., 1.),\n        quaternion: Quaternion::from_direction_and_angle(\n            &DirectionVector::new(0., 0., 1.),\n            m12.atan2(m11) as f64,\n        ),\n    })\n}\n\nimpl Animate for Matrix3D {\n    #[cfg(feature = \"servo\")]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.is_3d() || other.is_3d() {\n            let decomposed_from = decompose_3d_matrix(*self);\n            let decomposed_to = decompose_3d_matrix(*other);\n            match (decomposed_from, decomposed_to) {\n                (Ok(this), Ok(other)) => Ok(Matrix3D::from(this.animate(&other, procedure)?)),\n                // Matrices can be undecomposable due to couple reasons, e.g.,\n                // non-invertible matrices. In this case, we should report Err\n                // here, and let the caller do the fallback procedure.\n                _ => Err(()),\n            }\n        } else {\n            let this = MatrixDecomposed2D::from(*self);\n            let other = MatrixDecomposed2D::from(*other);\n            Ok(Matrix3D::from(this.animate(&other, procedure)?))\n        }\n    }\n\n    #[cfg(feature = \"gecko\")]\n    // Gecko doesn't exactly follow the spec here; we use a different procedure\n    // to match it\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        let (from, to) = if self.is_3d() || other.is_3d() {\n            (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)\n        } else {\n            (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)\n        };\n        // Matrices can be undecomposable due to couple reasons, e.g.,\n        // non-invertible matrices. In this case, we should report Err here,\n        // and let the caller do the fallback procedure.\n        Ok(Matrix3D::from(from.animate(&to, procedure)?))\n    }\n}\n\nimpl ComputeSquaredDistance for Matrix3D {\n    #[inline]\n    #[cfg(feature = \"servo\")]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        if self.is_3d() || other.is_3d() {\n            let from = decompose_3d_matrix(*self)?;\n            let to = decompose_3d_matrix(*other)?;\n            from.compute_squared_distance(&to)\n        } else {\n            let from = MatrixDecomposed2D::from(*self);\n            let to = MatrixDecomposed2D::from(*other);\n            from.compute_squared_distance(&to)\n        }\n    }\n\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let (from, to) = if self.is_3d() || other.is_3d() {\n            (decompose_3d_matrix(*self)?, decompose_3d_matrix(*other)?)\n        } else {\n            (decompose_2d_matrix(self)?, decompose_2d_matrix(other)?)\n        };\n        from.compute_squared_distance(&to)\n    }\n}\n\n// ------------------------------------\n// Animation for Transform list.\n// ------------------------------------\nfn is_matched_operation(\n    first: &ComputedTransformOperation,\n    second: &ComputedTransformOperation,\n) -> bool {\n    match (first, second) {\n        (&TransformOperation::Matrix(..), &TransformOperation::Matrix(..))\n        | (&TransformOperation::Matrix3D(..), &TransformOperation::Matrix3D(..))\n        | (&TransformOperation::Skew(..), &TransformOperation::Skew(..))\n        | (&TransformOperation::SkewX(..), &TransformOperation::SkewX(..))\n        | (&TransformOperation::SkewY(..), &TransformOperation::SkewY(..))\n        | (&TransformOperation::Rotate(..), &TransformOperation::Rotate(..))\n        | (&TransformOperation::Rotate3D(..), &TransformOperation::Rotate3D(..))\n        | (&TransformOperation::RotateX(..), &TransformOperation::RotateX(..))\n        | (&TransformOperation::RotateY(..), &TransformOperation::RotateY(..))\n        | (&TransformOperation::RotateZ(..), &TransformOperation::RotateZ(..))\n        | (&TransformOperation::Perspective(..), &TransformOperation::Perspective(..)) => true,\n        // Match functions that have the same primitive transform function\n        (a, b) if a.is_translate() && b.is_translate() => true,\n        (a, b) if a.is_scale() && b.is_scale() => true,\n        (a, b) if a.is_rotate() && b.is_rotate() => true,\n        // InterpolateMatrix and AccumulateMatrix are for mismatched transforms\n        _ => false,\n    }\n}\n\n/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>\nimpl Animate for ComputedTransform {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use std::borrow::Cow;\n\n        // Addition for transforms simply means appending to the list of\n        // transform functions. This is different to how we handle the other\n        // animation procedures so we treat it separately here rather than\n        // handling it in TransformOperation.\n        if procedure == Procedure::Add {\n            let result = self.0.iter().chain(&*other.0).cloned().collect();\n            return Ok(Transform(result));\n        }\n\n        let this = Cow::Borrowed(&self.0);\n        let other = Cow::Borrowed(&other.0);\n\n        // Interpolate the common prefix\n        let mut result = this\n            .iter()\n            .zip(other.iter())\n            .take_while(|(this, other)| is_matched_operation(this, other))\n            .map(|(this, other)| this.animate(other, procedure))\n            .collect::<Result<Vec<_>, _>>()?;\n\n        // Deal with the remainders\n        let this_remainder = if this.len() > result.len() {\n            Some(&this[result.len()..])\n        } else {\n            None\n        };\n        let other_remainder = if other.len() > result.len() {\n            Some(&other[result.len()..])\n        } else {\n            None\n        };\n\n        match (this_remainder, other_remainder) {\n            // If there is a remainder from *both* lists we must have had mismatched functions.\n            // => Add the remainders to a suitable ___Matrix function.\n            (Some(this_remainder), Some(other_remainder)) => {\n                result.push(TransformOperation::animate_mismatched_transforms(\n                    this_remainder,\n                    other_remainder,\n                    procedure,\n                )?);\n            },\n            // If there is a remainder from just one list, then one list must be shorter but\n            // completely match the type of the corresponding functions in the longer list.\n            // => Interpolate the remainder with identity transforms.\n            (Some(remainder), None) | (None, Some(remainder)) => {\n                let fill_right = this_remainder.is_some();\n                result.append(\n                    &mut remainder\n                        .iter()\n                        .map(|transform| {\n                            let identity = transform.to_animated_zero().unwrap();\n\n                            match transform {\n                                TransformOperation::AccumulateMatrix { .. }\n                                | TransformOperation::InterpolateMatrix { .. } => {\n                                    let (from, to) = if fill_right {\n                                        (transform, &identity)\n                                    } else {\n                                        (&identity, transform)\n                                    };\n\n                                    TransformOperation::animate_mismatched_transforms(\n                                        &[from.clone()],\n                                        &[to.clone()],\n                                        procedure,\n                                    )\n                                },\n                                _ => {\n                                    let (lhs, rhs) = if fill_right {\n                                        (transform, &identity)\n                                    } else {\n                                        (&identity, transform)\n                                    };\n                                    lhs.animate(rhs, procedure)\n                                },\n                            }\n                        })\n                        .collect::<Result<Vec<_>, _>>()?,\n                );\n            },\n            (None, None) => {},\n        }\n\n        Ok(Transform(result.into()))\n    }\n}\n\nimpl ComputeSquaredDistance for ComputedTransform {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let squared_dist = super::lists::with_zero::squared_distance(&self.0, &other.0);\n\n        // Roll back to matrix interpolation if there is any Err(()) in the\n        // transform lists, such as mismatched transform functions.\n        //\n        // FIXME: Using a zero size here seems a bit sketchy but matches the\n        // previous behavior.\n        if squared_dist.is_err() {\n            let rect = euclid::Rect::zero();\n            let matrix1: Matrix3D = self.to_transform_3d_matrix(Some(&rect))?.0.into();\n            let matrix2: Matrix3D = other.to_transform_3d_matrix(Some(&rect))?.0.into();\n            return matrix1.compute_squared_distance(&matrix2);\n        }\n\n        squared_dist\n    }\n}\n\n/// <http://dev.w3.org/csswg/css-transforms/#interpolation-of-transforms>\nimpl Animate for ComputedTransformOperation {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match (self, other) {\n            (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {\n                Ok(TransformOperation::Matrix3D(\n                    this.animate(other, procedure)?,\n                ))\n            },\n            (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {\n                Ok(TransformOperation::Matrix(this.animate(other, procedure)?))\n            },\n            (\n                &TransformOperation::Skew(ref fx, ref fy),\n                &TransformOperation::Skew(ref tx, ref ty),\n            ) => Ok(TransformOperation::Skew(\n                fx.animate(tx, procedure)?,\n                fy.animate(ty, procedure)?,\n            )),\n            (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {\n                Ok(TransformOperation::SkewX(f.animate(t, procedure)?))\n            },\n            (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {\n                Ok(TransformOperation::SkewY(f.animate(t, procedure)?))\n            },\n            (\n                &TransformOperation::Translate3D(ref fx, ref fy, ref fz),\n                &TransformOperation::Translate3D(ref tx, ref ty, ref tz),\n            ) => Ok(TransformOperation::Translate3D(\n                fx.animate(tx, procedure)?,\n                fy.animate(ty, procedure)?,\n                fz.animate(tz, procedure)?,\n            )),\n            (\n                &TransformOperation::Translate(ref fx, ref fy),\n                &TransformOperation::Translate(ref tx, ref ty),\n            ) => Ok(TransformOperation::Translate(\n                fx.animate(tx, procedure)?,\n                fy.animate(ty, procedure)?,\n            )),\n            (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {\n                Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))\n            },\n            (&TransformOperation::TranslateY(ref f), &TransformOperation::TranslateY(ref t)) => {\n                Ok(TransformOperation::TranslateY(f.animate(t, procedure)?))\n            },\n            (&TransformOperation::TranslateZ(ref f), &TransformOperation::TranslateZ(ref t)) => {\n                Ok(TransformOperation::TranslateZ(f.animate(t, procedure)?))\n            },\n            (\n                &TransformOperation::Scale3D(ref fx, ref fy, ref fz),\n                &TransformOperation::Scale3D(ref tx, ref ty, ref tz),\n            ) => Ok(TransformOperation::Scale3D(\n                animate_multiplicative_factor(*fx, *tx, procedure)?,\n                animate_multiplicative_factor(*fy, *ty, procedure)?,\n                animate_multiplicative_factor(*fz, *tz, procedure)?,\n            )),\n            (&TransformOperation::ScaleX(ref f), &TransformOperation::ScaleX(ref t)) => Ok(\n                TransformOperation::ScaleX(animate_multiplicative_factor(*f, *t, procedure)?),\n            ),\n            (&TransformOperation::ScaleY(ref f), &TransformOperation::ScaleY(ref t)) => Ok(\n                TransformOperation::ScaleY(animate_multiplicative_factor(*f, *t, procedure)?),\n            ),\n            (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(\n                TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),\n            ),\n            (\n                &TransformOperation::Scale(ref fx, ref fy),\n                &TransformOperation::Scale(ref tx, ref ty),\n            ) => Ok(TransformOperation::Scale(\n                animate_multiplicative_factor(*fx, *tx, procedure)?,\n                animate_multiplicative_factor(*fy, *ty, procedure)?,\n            )),\n            (\n                &TransformOperation::Rotate3D(fx, fy, fz, fa),\n                &TransformOperation::Rotate3D(tx, ty, tz, ta),\n            ) => {\n                let animated = Rotate::Rotate3D(fx, fy, fz, fa)\n                    .animate(&Rotate::Rotate3D(tx, ty, tz, ta), procedure)?;\n                let (fx, fy, fz, fa) = ComputedRotate::resolve(&animated);\n                Ok(TransformOperation::Rotate3D(fx, fy, fz, fa))\n            },\n            (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta)) => {\n                Ok(TransformOperation::RotateX(fa.animate(&ta, procedure)?))\n            },\n            (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta)) => {\n                Ok(TransformOperation::RotateY(fa.animate(&ta, procedure)?))\n            },\n            (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta)) => {\n                Ok(TransformOperation::RotateZ(fa.animate(&ta, procedure)?))\n            },\n            (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {\n                Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))\n            },\n            (&TransformOperation::Rotate(fa), &TransformOperation::RotateZ(ta)) => {\n                Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))\n            },\n            (&TransformOperation::RotateZ(fa), &TransformOperation::Rotate(ta)) => {\n                Ok(TransformOperation::Rotate(fa.animate(&ta, procedure)?))\n            },\n            (\n                &TransformOperation::Perspective(ref fd),\n                &TransformOperation::Perspective(ref td),\n            ) => {\n                use crate::values::computed::CSSPixelLength;\n                use crate::values::generics::transform::create_perspective_matrix;\n\n                // From https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions:\n                //\n                //    The transform functions matrix(), matrix3d() and\n                //    perspective() get converted into 4x4 matrices first and\n                //    interpolated as defined in section Interpolation of\n                //    Matrices afterwards.\n                //\n                let from = create_perspective_matrix(fd.infinity_or(|l| l.px()));\n                let to = create_perspective_matrix(td.infinity_or(|l| l.px()));\n\n                let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?;\n\n                let decomposed = decompose_3d_matrix(interpolated)?;\n                let perspective_z = decomposed.perspective.2;\n                // Clamp results outside of the -1 to 0 range so that we get perspective\n                // function values between 1 and infinity.\n                let used_value = if perspective_z >= 0. {\n                    transform::PerspectiveFunction::None\n                } else {\n                    transform::PerspectiveFunction::Length(CSSPixelLength::new(\n                        if perspective_z <= -1. {\n                            1.\n                        } else {\n                            -1. / perspective_z\n                        },\n                    ))\n                };\n                Ok(TransformOperation::Perspective(used_value))\n            },\n            _ if self.is_translate() && other.is_translate() => self\n                .to_translate_3d()\n                .animate(&other.to_translate_3d(), procedure),\n            _ if self.is_scale() && other.is_scale() => {\n                self.to_scale_3d().animate(&other.to_scale_3d(), procedure)\n            },\n            _ if self.is_rotate() && other.is_rotate() => self\n                .to_rotate_3d()\n                .animate(&other.to_rotate_3d(), procedure),\n            _ => Err(()),\n        }\n    }\n}\n\nimpl ComputedTransformOperation {\n    /// If there are no size dependencies, we try to animate in-place, to avoid\n    /// creating deeply nested Interpolate* operations.\n    fn try_animate_mismatched_transforms_in_place(\n        left: &[Self],\n        right: &[Self],\n        procedure: Procedure,\n    ) -> Result<Self, ()> {\n        let (left, _left_3d) = Transform::components_to_transform_3d_matrix(left, None)?;\n        let (right, _right_3d) = Transform::components_to_transform_3d_matrix(right, None)?;\n        Ok(Self::Matrix3D(\n            Matrix3D::from(left).animate(&Matrix3D::from(right), procedure)?,\n        ))\n    }\n\n    fn animate_mismatched_transforms(\n        left: &[Self],\n        right: &[Self],\n        procedure: Procedure,\n    ) -> Result<Self, ()> {\n        if let Ok(op) = Self::try_animate_mismatched_transforms_in_place(left, right, procedure) {\n            return Ok(op);\n        }\n        let from_list = Transform(left.to_vec().into());\n        let to_list = Transform(right.to_vec().into());\n        Ok(match procedure {\n            Procedure::Add => {\n                debug_assert!(false, \"Addition should've been handled earlier\");\n                return Err(());\n            },\n            Procedure::Interpolate { progress } => Self::InterpolateMatrix {\n                from_list,\n                to_list,\n                progress: Percentage(progress as f32),\n            },\n            Procedure::Accumulate { count } => Self::AccumulateMatrix {\n                from_list,\n                to_list,\n                count: cmp::min(count, i32::max_value() as u64) as i32,\n            },\n        })\n    }\n}\n\n// This might not be the most useful definition of distance. It might be better, for example,\n// to trace the distance travelled by a point as its transform is interpolated between the two\n// lists. That, however, proves to be quite complicated so we take a simple approach for now.\n// See https://bugzilla.mozilla.org/show_bug.cgi?id=1318591#c0.\nimpl ComputeSquaredDistance for ComputedTransformOperation {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        match (self, other) {\n            (&TransformOperation::Matrix3D(ref this), &TransformOperation::Matrix3D(ref other)) => {\n                this.compute_squared_distance(other)\n            },\n            (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {\n                let this: Matrix3D = (*this).into();\n                let other: Matrix3D = (*other).into();\n                this.compute_squared_distance(&other)\n            },\n            (\n                &TransformOperation::Skew(ref fx, ref fy),\n                &TransformOperation::Skew(ref tx, ref ty),\n            ) => Ok(fx.compute_squared_distance(&tx)? + fy.compute_squared_distance(&ty)?),\n            (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t))\n            | (&TransformOperation::SkewY(ref f), &TransformOperation::SkewY(ref t)) => {\n                f.compute_squared_distance(&t)\n            },\n            (\n                &TransformOperation::Translate3D(ref fx, ref fy, ref fz),\n                &TransformOperation::Translate3D(ref tx, ref ty, ref tz),\n            ) => {\n                // For translate, We don't want to require doing layout in order\n                // to calculate the result, so drop the percentage part.\n                //\n                // However, dropping percentage makes us impossible to compute\n                // the distance for the percentage-percentage case, but Gecko\n                // uses the same formula, so it's fine for now.\n                let basis = Length::new(0.);\n                let fx = fx.resolve(basis).px();\n                let fy = fy.resolve(basis).px();\n                let tx = tx.resolve(basis).px();\n                let ty = ty.resolve(basis).px();\n\n                Ok(fx.compute_squared_distance(&tx)?\n                    + fy.compute_squared_distance(&ty)?\n                    + fz.compute_squared_distance(&tz)?)\n            },\n            (\n                &TransformOperation::Scale3D(ref fx, ref fy, ref fz),\n                &TransformOperation::Scale3D(ref tx, ref ty, ref tz),\n            ) => Ok(fx.compute_squared_distance(&tx)?\n                + fy.compute_squared_distance(&ty)?\n                + fz.compute_squared_distance(&tz)?),\n            (\n                &TransformOperation::Rotate3D(fx, fy, fz, fa),\n                &TransformOperation::Rotate3D(tx, ty, tz, ta),\n            ) => Rotate::Rotate3D(fx, fy, fz, fa)\n                .compute_squared_distance(&Rotate::Rotate3D(tx, ty, tz, ta)),\n            (&TransformOperation::RotateX(fa), &TransformOperation::RotateX(ta))\n            | (&TransformOperation::RotateY(fa), &TransformOperation::RotateY(ta))\n            | (&TransformOperation::RotateZ(fa), &TransformOperation::RotateZ(ta))\n            | (&TransformOperation::Rotate(fa), &TransformOperation::Rotate(ta)) => {\n                fa.compute_squared_distance(&ta)\n            },\n            (\n                &TransformOperation::Perspective(ref fd),\n                &TransformOperation::Perspective(ref td),\n            ) => fd\n                .infinity_or(|l| l.px())\n                .compute_squared_distance(&td.infinity_or(|l| l.px())),\n            (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m))\n            | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => {\n                // FIXME(emilio): Is this right? Why interpolating this with\n                // Perspective but not with anything else?\n                let mut p_matrix = Matrix3D::identity();\n                let p = p.infinity_or(|p| p.px());\n                if p >= 0. {\n                    p_matrix.m34 = -1. / p.max(1.);\n                }\n                p_matrix.compute_squared_distance(&m)\n            },\n            // Gecko cross-interpolates amongst all translate and all scale\n            // functions (See ToPrimitive in layout/style/StyleAnimationValue.cpp)\n            // without falling back to InterpolateMatrix\n            _ if self.is_translate() && other.is_translate() => self\n                .to_translate_3d()\n                .compute_squared_distance(&other.to_translate_3d()),\n            _ if self.is_scale() && other.is_scale() => self\n                .to_scale_3d()\n                .compute_squared_distance(&other.to_scale_3d()),\n            _ if self.is_rotate() && other.is_rotate() => self\n                .to_rotate_3d()\n                .compute_squared_distance(&other.to_rotate_3d()),\n            _ => Err(()),\n        }\n    }\n}\n\n// ------------------------------------\n// Individual transforms.\n// ------------------------------------\n/// <https://drafts.csswg.org/css-transforms-2/#propdef-rotate>\nimpl ComputedRotate {\n    fn resolve(&self) -> (Number, Number, Number, Angle) {\n        // According to the spec:\n        // https://drafts.csswg.org/css-transforms-2/#individual-transforms\n        //\n        // If the axis is unspecified, it defaults to \"0 0 1\"\n        match *self {\n            Rotate::None => (0., 0., 1., Angle::zero()),\n            Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),\n            Rotate::Rotate(angle) => (0., 0., 1., angle),\n        }\n    }\n}\n\nimpl Animate for ComputedRotate {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use euclid::approxeq::ApproxEq;\n        match (self, other) {\n            (&Rotate::None, &Rotate::None) => Ok(Rotate::None),\n            (&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {\n                // We always normalize direction vector for rotate3d() first, so we should also\n                // apply the same rule for rotate property. In other words, we promote none into\n                // a 3d rotate, and normalize both direction vector first, and then do\n                // interpolation.\n                let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);\n                Ok(Rotate::Rotate3D(\n                    fx,\n                    fy,\n                    fz,\n                    fa.animate(&Angle::zero(), procedure)?,\n                ))\n            },\n            (&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {\n                // Normalize direction vector first.\n                let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);\n                Ok(Rotate::Rotate3D(\n                    tx,\n                    ty,\n                    tz,\n                    Angle::zero().animate(&ta, procedure)?,\n                ))\n            },\n            (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {\n                // https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions\n\n                let (from, to) = (self.resolve(), other.resolve());\n                // For interpolations with the primitive rotate3d(), the direction vectors of the\n                // transform functions get normalized first.\n                let (fx, fy, fz, fa) =\n                    transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);\n                let (tx, ty, tz, ta) =\n                    transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);\n\n                // The rotation angle gets interpolated numerically and the rotation vector of the\n                // non-zero angle is used or (0, 0, 1) if both angles are zero.\n                //\n                // Note: the normalization may get two different vectors because of the\n                // floating-point precision, so we have to use approx_eq to compare two\n                // vectors.\n                let fv = DirectionVector::new(fx, fy, fz);\n                let tv = DirectionVector::new(tx, ty, tz);\n                if fa.is_zero() || ta.is_zero() || fv.approx_eq(&tv) {\n                    let (x, y, z) = if fa.is_zero() && ta.is_zero() {\n                        (0., 0., 1.)\n                    } else if fa.is_zero() {\n                        (tx, ty, tz)\n                    } else {\n                        // ta.is_zero() or both vectors are equal.\n                        (fx, fy, fz)\n                    };\n                    return Ok(Rotate::Rotate3D(x, y, z, fa.animate(&ta, procedure)?));\n                }\n\n                // Slerp algorithm doesn't work well for Procedure::Add, which makes both\n                // |this_weight| and |other_weight| be 1.0, and this may make the cosine value of\n                // the angle be out of the range (i.e. the 4th component of the quaternion vector).\n                // (See Quaternion::animate() for more details about the Slerp formula.)\n                // Therefore, if the cosine value is out of range, we get an NaN after applying\n                // acos() on it, and so the result is invalid.\n                // Note: This is specialized for `rotate` property. The addition of `transform`\n                // property has been handled in `ComputedTransform::animate()` by merging two list\n                // directly.\n                let rq = if procedure == Procedure::Add {\n                    // In Transform::animate(), it converts two rotations into transform matrices,\n                    // and do matrix multiplication. This match the spec definition for the\n                    // addition.\n                    // https://drafts.csswg.org/css-transforms-2/#combining-transform-lists\n                    let f = ComputedTransformOperation::Rotate3D(fx, fy, fz, fa);\n                    let t = ComputedTransformOperation::Rotate3D(tx, ty, tz, ta);\n                    let v =\n                        Transform(vec![f].into()).animate(&Transform(vec![t].into()), procedure)?;\n                    let (m, _) = v.to_transform_3d_matrix(None)?;\n                    // Decompose the matrix and retrive the quaternion vector.\n                    decompose_3d_matrix(Matrix3D::from(m))?.quaternion\n                } else {\n                    // If the normalized vectors are not equal and both rotation angles are\n                    // non-zero the transform functions get converted into 4x4 matrices first and\n                    // interpolated as defined in section Interpolation of Matrices afterwards.\n                    // However, per the spec issue [1], we prefer to converting the rotate3D into\n                    // quaternion vectors directly, and then apply Slerp algorithm.\n                    //\n                    // Both ways should be identical, and converting rotate3D into quaternion\n                    // vectors directly can avoid redundant math operations, e.g. the generation of\n                    // the equivalent matrix3D and the unnecessary decomposition parts of\n                    // translation, scale, skew, and persepctive in the matrix3D.\n                    //\n                    // [1] https://github.com/w3c/csswg-drafts/issues/9278\n                    let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());\n                    let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());\n                    Quaternion::animate(&fq, &tq, procedure)?\n                };\n\n                let (x, y, z, angle) = transform::get_normalized_vector_and_angle(\n                    rq.0 as f32,\n                    rq.1 as f32,\n                    rq.2 as f32,\n                    // Due to floating point precision issues, the quaternion may contain values\n                    // slightly larger out of the [-1.0, 1.0] range - Clamp to avoid NaN.\n                    rq.3.clamp(-1.0, 1.0).acos() as f32 * 2.0,\n                );\n\n                Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))\n            },\n            (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {\n                // If this is a 2D rotation, we just animate the <angle>\n                let (from, to) = (self.resolve().3, other.resolve().3);\n                Ok(Rotate::Rotate(from.animate(&to, procedure)?))\n            },\n        }\n    }\n}\n\nimpl ComputeSquaredDistance for ComputedRotate {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        use euclid::approxeq::ApproxEq;\n        match (self, other) {\n            (&Rotate::None, &Rotate::None) => Ok(SquaredDistance::from_sqrt(0.)),\n            (&Rotate::Rotate3D(_, _, _, a), &Rotate::None)\n            | (&Rotate::None, &Rotate::Rotate3D(_, _, _, a)) => {\n                a.compute_squared_distance(&Angle::zero())\n            },\n            (&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {\n                let (from, to) = (self.resolve(), other.resolve());\n                let (mut fx, mut fy, mut fz, angle1) =\n                    transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);\n                let (mut tx, mut ty, mut tz, angle2) =\n                    transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);\n\n                if angle1.is_zero() && angle2.is_zero() {\n                    (fx, fy, fz) = (0., 0., 1.);\n                    (tx, ty, tz) = (0., 0., 1.);\n                } else if angle1.is_zero() {\n                    (fx, fy, fz) = (tx, ty, tz);\n                } else if angle2.is_zero() {\n                    (tx, ty, tz) = (fx, fy, fz);\n                }\n\n                let v1 = DirectionVector::new(fx, fy, fz);\n                let v2 = DirectionVector::new(tx, ty, tz);\n                if v1.approx_eq(&v2) {\n                    angle1.compute_squared_distance(&angle2)\n                } else {\n                    let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());\n                    let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());\n                    q1.compute_squared_distance(&q2)\n                }\n            },\n            (&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => self\n                .resolve()\n                .3\n                .compute_squared_distance(&other.resolve().3),\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-transforms-2/#propdef-translate>\nimpl ComputedTranslate {\n    fn resolve(&self) -> (LengthPercentage, LengthPercentage, Length) {\n        // According to the spec:\n        // https://drafts.csswg.org/css-transforms-2/#individual-transforms\n        //\n        // Unspecified translations default to 0px\n        match *self {\n            Translate::None => (\n                LengthPercentage::zero(),\n                LengthPercentage::zero(),\n                Length::zero(),\n            ),\n            Translate::Translate(ref tx, ref ty, ref tz) => (tx.clone(), ty.clone(), tz.clone()),\n        }\n    }\n}\n\nimpl Animate for ComputedTranslate {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match (self, other) {\n            (&Translate::None, &Translate::None) => Ok(Translate::None),\n            (&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {\n                let (from, to) = (self.resolve(), other.resolve());\n                Ok(Translate::Translate(\n                    from.0.animate(&to.0, procedure)?,\n                    from.1.animate(&to.1, procedure)?,\n                    from.2.animate(&to.2, procedure)?,\n                ))\n            },\n        }\n    }\n}\n\nimpl ComputeSquaredDistance for ComputedTranslate {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let (from, to) = (self.resolve(), other.resolve());\n        Ok(from.0.compute_squared_distance(&to.0)?\n            + from.1.compute_squared_distance(&to.1)?\n            + from.2.compute_squared_distance(&to.2)?)\n    }\n}\n\n/// <https://drafts.csswg.org/css-transforms-2/#propdef-scale>\nimpl ComputedScale {\n    fn resolve(&self) -> (Number, Number, Number) {\n        // According to the spec:\n        // https://drafts.csswg.org/css-transforms-2/#individual-transforms\n        //\n        // Unspecified scales default to 1\n        match *self {\n            Scale::None => (1.0, 1.0, 1.0),\n            Scale::Scale(sx, sy, sz) => (sx, sy, sz),\n        }\n    }\n}\n\nimpl Animate for ComputedScale {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        match (self, other) {\n            (&Scale::None, &Scale::None) => Ok(Scale::None),\n            (&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {\n                let (from, to) = (self.resolve(), other.resolve());\n                // For transform lists, we add by appending to the list of\n                // transform functions. However, ComputedScale cannot be\n                // simply concatenated, so we have to calculate the additive\n                // result here.\n                if procedure == Procedure::Add {\n                    // scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)\n                    return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));\n                }\n                Ok(Scale::Scale(\n                    animate_multiplicative_factor(from.0, to.0, procedure)?,\n                    animate_multiplicative_factor(from.1, to.1, procedure)?,\n                    animate_multiplicative_factor(from.2, to.2, procedure)?,\n                ))\n            },\n        }\n    }\n}\n\nimpl ComputeSquaredDistance for ComputedScale {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        let (from, to) = (self.resolve(), other.resolve());\n        Ok(from.0.compute_squared_distance(&to.0)?\n            + from.1.compute_squared_distance(&to.1)?\n            + from.2.compute_squared_distance(&to.2)?)\n    }\n}\n"
  },
  {
    "path": "style/values/computed/align.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Values for CSS Box Alignment properties\n//!\n//! https://drafts.csswg.org/css-align/\n\nuse crate::derives::*;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::specified;\n\npub use super::specified::{ContentDistribution, ItemPlacement, SelfAlignment};\n\n/// The computed value for the `justify-items` property.\n///\n/// Need to carry around both the specified and computed value to handle the\n/// special legacy keyword without destroying style sharing.\n///\n/// In particular, `justify-items` is a reset property, so we ought to be able\n/// to share its computed representation across elements as long as they match\n/// the same rules. Except that it's not true if the specified value for\n/// `justify-items` is `legacy` and the computed value of the parent has the\n/// `legacy` modifier.\n///\n/// So instead of computing `legacy` \"normally\" looking at get_parent_position(),\n/// marking it as uncacheable, we carry the specified value around and handle\n/// the special case in `StyleAdjuster` instead, only when the result of the\n/// computation would vary.\n///\n/// Note that we also need to special-case this property in matching.rs, in\n/// order to properly handle changes to the legacy keyword... This all kinda\n/// sucks :(.\n///\n/// See the discussion in https://bugzil.la/1384542.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToTyped)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct ComputedJustifyItems {\n    /// The specified value for the property. Can contain the bare `legacy`\n    /// keyword.\n    #[css(skip)]\n    pub specified: specified::JustifyItems,\n    /// The computed value for the property. Cannot contain the bare `legacy`\n    /// keyword, but note that it could contain it in combination with other\n    /// keywords like `left`, `right` or `center`.\n    pub computed: specified::JustifyItems,\n}\n\npub use self::ComputedJustifyItems as JustifyItems;\n\nimpl JustifyItems {\n    /// Returns the `legacy` value.\n    pub fn legacy() -> Self {\n        Self {\n            specified: specified::JustifyItems::legacy(),\n            computed: specified::JustifyItems::normal(),\n        }\n    }\n}\n\nimpl ToComputedValue for specified::JustifyItems {\n    type ComputedValue = JustifyItems;\n\n    /// <https://drafts.csswg.org/css-align/#valdef-justify-items-legacy>\n    fn to_computed_value(&self, _context: &Context) -> JustifyItems {\n        use crate::values::specified::align;\n        let specified = *self;\n        let computed = if (self.0).0 != align::AlignFlags::LEGACY {\n            *self\n        } else {\n            // If the inherited value of `justify-items` includes the\n            // `legacy` keyword, `legacy` computes to the inherited value, but\n            // we assume it computes to `normal`, and handle that special-case\n            // in StyleAdjuster.\n            Self::normal()\n        };\n\n        JustifyItems {\n            specified,\n            computed,\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &JustifyItems) -> Self {\n        computed.specified\n    }\n}\n"
  },
  {
    "path": "style/values/computed/angle.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed angles.\n\nuse crate::derives::*;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse std::f64::consts::PI;\nuse std::fmt::{self, Write};\nuse std::ops::Neg;\nuse std::{f32, f64};\nuse style_traits::{CssWriter, ToCss};\n\n/// A computed angle in degrees.\n#[derive(\n    Add,\n    Animate,\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n)]\n#[repr(C)]\npub struct Angle(CSSFloat);\n\nimpl ToCss for Angle {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.degrees().to_css(dest)?;\n        dest.write_str(\"deg\")\n    }\n}\n\nconst RAD_PER_DEG: f64 = PI / 180.0;\n\nimpl Angle {\n    /// Creates a computed `Angle` value from a radian amount.\n    pub fn from_radians(radians: CSSFloat) -> Self {\n        Angle(radians / RAD_PER_DEG as f32)\n    }\n\n    /// Creates a computed `Angle` value from a degrees amount.\n    #[inline]\n    pub fn from_degrees(degrees: CSSFloat) -> Self {\n        Angle(degrees)\n    }\n\n    /// Returns the amount of radians this angle represents.\n    #[inline]\n    pub fn radians(&self) -> CSSFloat {\n        self.radians64().min(f32::MAX as f64).max(f32::MIN as f64) as f32\n    }\n\n    /// Returns the amount of radians this angle represents as a `f64`.\n    ///\n    /// Gecko stores angles as singles, but does this computation using doubles.\n    ///\n    /// This is significant enough to mess up rounding to the nearest\n    /// quarter-turn for 225 degrees, for example.\n    #[inline]\n    pub fn radians64(&self) -> f64 {\n        self.0 as f64 * RAD_PER_DEG\n    }\n\n    /// Return the value in degrees.\n    #[inline]\n    pub fn degrees(&self) -> CSSFloat {\n        self.0\n    }\n}\n\nimpl Zero for Angle {\n    #[inline]\n    fn zero() -> Self {\n        Angle(0.0)\n    }\n\n    #[inline]\n    fn is_zero(&self) -> bool {\n        self.0 == 0.\n    }\n}\n\nimpl ComputeSquaredDistance for Angle {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        // Use the formula for calculating the distance between angles defined in SVG:\n        // https://www.w3.org/TR/SVG/animate.html#complexDistances\n        self.radians64()\n            .compute_squared_distance(&other.radians64())\n    }\n}\n\nimpl Neg for Angle {\n    type Output = Angle;\n\n    #[inline]\n    fn neg(self) -> Angle {\n        Angle(-self.0)\n    }\n}\n"
  },
  {
    "path": "style/values/computed/animation.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values for properties related to animations and transitions\n\nuse crate::derives::*;\nuse crate::values::computed::{Context, LengthPercentage, Time, ToComputedValue};\nuse crate::values::generics::animation as generics;\nuse crate::values::specified::animation as specified;\nuse crate::values::CSSFloat;\nuse std::fmt::{self, Write};\nuse style_traits::{CssString, CssWriter, KeywordValue, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\npub use crate::values::specified::animation::{\n    AnimationComposition, AnimationDirection, AnimationFillMode, AnimationName, AnimationPlayState,\n    ScrollAxis, TimelineName, TransitionBehavior, TransitionProperty, ViewTransitionClass,\n    ViewTransitionName,\n};\n\n/// A computed value for the `animation-duration` property.\npub type AnimationDuration = generics::GenericAnimationDuration<Time>;\n\nimpl AnimationDuration {\n    /// Returns the amount of seconds this time represents.\n    #[inline]\n    pub fn seconds(&self) -> CSSFloat {\n        match *self {\n            Self::Auto => 0.0,\n            Self::Time(ref t) => t.seconds(),\n        }\n    }\n}\n\n/// A computed value for the `animation-iteration-count` property.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]\n#[repr(C)]\npub struct AnimationIterationCount(pub f32);\n\nimpl ToComputedValue for specified::AnimationIterationCount {\n    type ComputedValue = AnimationIterationCount;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        AnimationIterationCount(match *self {\n            specified::AnimationIterationCount::Number(n) => n.to_computed_value(context).0,\n            specified::AnimationIterationCount::Infinite => f32::INFINITY,\n        })\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        use crate::values::specified::NonNegativeNumber;\n        if computed.0.is_infinite() {\n            specified::AnimationIterationCount::Infinite\n        } else {\n            specified::AnimationIterationCount::Number(NonNegativeNumber::new(computed.0))\n        }\n    }\n}\n\nimpl AnimationIterationCount {\n    /// Returns the value `1.0`.\n    #[inline]\n    pub fn one() -> Self {\n        Self(1.0)\n    }\n}\n\nimpl ToCss for AnimationIterationCount {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.0.is_infinite() {\n            dest.write_str(\"infinite\")\n        } else {\n            self.0.to_css(dest)\n        }\n    }\n}\n\nimpl ToTyped for AnimationIterationCount {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        if self.0.is_infinite() {\n            dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\n                \"infinite\",\n            ))));\n            Ok(())\n        } else {\n            self.0.to_typed(dest)\n        }\n    }\n}\n\n/// A computed value for the `animation-timeline` property.\npub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;\n\n/// A computed value for the `view-timeline-inset` property.\npub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;\n\n/// A computed value for the `animation-range-start` property.\npub type AnimationRangeStart = generics::GenericAnimationRangeStart<LengthPercentage>;\nimpl AnimationRangeStart {\n    /// The `normal` value.\n    pub fn normal() -> Self {\n        Self(generics::GenericAnimationRangeValue::normal(\n            LengthPercentage::zero_percent(),\n        ))\n    }\n}\n\n/// A computed value for the `animation-range-end` property.\npub type AnimationRangeEnd = generics::GenericAnimationRangeEnd<LengthPercentage>;\nimpl AnimationRangeEnd {\n    /// The `normal` value.\n    pub fn normal() -> Self {\n        Self(generics::GenericAnimationRangeValue::normal(\n            LengthPercentage::hundred_percent(),\n        ))\n    }\n}\n"
  },
  {
    "path": "style/values/computed/background.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values related to backgrounds.\n\nuse crate::values::computed::length::NonNegativeLengthPercentage;\nuse crate::values::generics::background::BackgroundSize as GenericBackgroundSize;\n\npub use crate::values::specified::background::BackgroundRepeat;\n\n/// A computed value for the `background-size` property.\npub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;\n"
  },
  {
    "path": "style/values/computed/basic_shape.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the computed value of\n//! [`basic-shape`][basic-shape]s\n//!\n//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape\n\nuse crate::values::animated::{Animate, Procedure};\nuse crate::values::computed::angle::Angle;\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Image, LengthPercentage, Position};\nuse crate::values::generics::basic_shape as generic;\nuse crate::values::specified::svg_path::{CoordPair, PathCommand, SVGPathPosition};\nuse crate::values::CSSFloat;\n\n/// A computed alias for FillRule.\npub use crate::values::generics::basic_shape::FillRule;\n\n/// A computed `clip-path` value.\npub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;\n\n/// A computed `shape-outside` value.\npub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;\n\n/// A computed basic shape.\npub type BasicShape = generic::GenericBasicShape<Angle, Position, LengthPercentage, InsetRect>;\n\n/// The computed value of `inset()`.\npub type InsetRect = generic::GenericInsetRect<LengthPercentage>;\n\n/// A computed circle.\npub type Circle = generic::Circle<Position, LengthPercentage>;\n\n/// A computed ellipse.\npub type Ellipse = generic::Ellipse<Position, LengthPercentage>;\n\n/// The computed value of `ShapeRadius`.\npub type ShapeRadius = generic::GenericShapeRadius<LengthPercentage>;\n\n/// The computed value of `shape()`.\npub type Shape = generic::Shape<Angle, Position, LengthPercentage>;\n\n/// The computed value of `ShapeCommand`.\npub type ShapeCommand = generic::GenericShapeCommand<Angle, Position, LengthPercentage>;\n\n/// The computed value of `PathOrShapeFunction`.\npub type PathOrShapeFunction =\n    generic::GenericPathOrShapeFunction<Angle, Position, LengthPercentage>;\n\n/// The computed value of `CoordinatePair`.\npub type CoordinatePair = generic::CoordinatePair<LengthPercentage>;\n\n/// The computed value of 'ControlPoint'.\npub type ControlPoint = generic::ControlPoint<Position, LengthPercentage>;\n\n/// The computed value of 'RelativeControlPoint'.\npub type RelativeControlPoint = generic::RelativeControlPoint<LengthPercentage>;\n\n/// The computed value of 'CommandEndPoint'.\npub type CommandEndPoint = generic::CommandEndPoint<Position, LengthPercentage>;\n\n/// The computed value of hline and vline's endpoint.\npub type AxisEndPoint = generic::AxisEndPoint<LengthPercentage>;\n\n/// Animate from `Shape` to `Path`, and vice versa.\nmacro_rules! animate_shape {\n    (\n        $from:ident,\n        $to:ident,\n        $procedure:ident,\n        $from_as_shape:tt,\n        $to_as_shape:tt\n    ) => {{\n        // Check fill-rule.\n        if $from.fill != $to.fill {\n            return Err(());\n        }\n\n        // Check the list of commands. (This is a specialized lists::by_computed_value::animate().)\n        let from_cmds = $from.commands();\n        let to_cmds = $to.commands();\n        if from_cmds.len() != to_cmds.len() {\n            return Err(());\n        }\n        let commands = from_cmds\n            .iter()\n            .zip(to_cmds.iter())\n            .map(|(from_cmd, to_cmd)| {\n                $from_as_shape(from_cmd).animate(&$to_as_shape(to_cmd), $procedure)\n            })\n            .collect::<Result<Vec<ShapeCommand>, ()>>()?;\n\n        Ok(Shape {\n            fill: $from.fill,\n            commands: commands.into(),\n        })\n    }};\n}\n\nimpl Animate for PathOrShapeFunction {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        // Per spec, commands are \"the same\" if they use the same command keyword, and use the same\n        // <by-to> keyword. For curve and smooth, they also must have the same number of control\n        // points. Therefore, we don't have to do normalization here. (Note that we do\n        // normalization if we animate from path() to path(). See svg_path.rs for more details.)\n        //\n        // https://drafts.csswg.org/css-shapes-2/#interpolating-shape\n        match (self, other) {\n            (Self::Path(ref from), Self::Path(ref to)) => {\n                from.animate(to, procedure).map(Self::Path)\n            },\n            (Self::Shape(ref from), Self::Shape(ref to)) => {\n                from.animate(to, procedure).map(Self::Shape)\n            },\n            (Self::Shape(ref from), Self::Path(ref to)) => {\n                // Animate from shape() to path(). We convert each PathCommand into ShapeCommand,\n                // and return shape().\n                animate_shape!(\n                    from,\n                    to,\n                    procedure,\n                    (|shape_cmd| shape_cmd),\n                    (|path_cmd| ShapeCommand::from(path_cmd))\n                )\n                .map(Self::Shape)\n            },\n            (Self::Path(ref from), Self::Shape(ref to)) => {\n                // Animate from path() to shape(). We convert each PathCommand into ShapeCommand,\n                // and return shape().\n                animate_shape!(\n                    from,\n                    to,\n                    procedure,\n                    (|path_cmd| ShapeCommand::from(path_cmd)),\n                    (|shape_cmd| shape_cmd)\n                )\n                .map(Self::Shape)\n            },\n        }\n    }\n}\n\nimpl From<&PathCommand> for ShapeCommand {\n    #[inline]\n    fn from(path: &PathCommand) -> Self {\n        match path {\n            &PathCommand::Close => Self::Close,\n            &PathCommand::Move { ref point } => Self::Move {\n                point: point.into(),\n            },\n            &PathCommand::Line { ref point } => Self::Move {\n                point: point.into(),\n            },\n            &PathCommand::HLine { ref x } => Self::HLine { x: x.into() },\n            &PathCommand::VLine { ref y } => Self::VLine { y: y.into() },\n            &PathCommand::CubicCurve {\n                ref point,\n                ref control1,\n                ref control2,\n            } => Self::CubicCurve {\n                point: point.into(),\n                control1: control1.into(),\n                control2: control2.into(),\n            },\n            &PathCommand::QuadCurve {\n                ref point,\n                ref control1,\n            } => Self::QuadCurve {\n                point: point.into(),\n                control1: control1.into(),\n            },\n            &PathCommand::SmoothCubic {\n                ref point,\n                ref control2,\n            } => Self::SmoothCubic {\n                point: point.into(),\n                control2: control2.into(),\n            },\n            &PathCommand::SmoothQuad { ref point } => Self::SmoothQuad {\n                point: point.into(),\n            },\n            &PathCommand::Arc {\n                ref point,\n                ref radii,\n                arc_sweep,\n                arc_size,\n                rotate,\n            } => Self::Arc {\n                point: point.into(),\n                radii: radii.into(),\n                arc_sweep,\n                arc_size,\n                rotate: Angle::from_degrees(rotate),\n            },\n        }\n    }\n}\n\nimpl From<&CoordPair> for CoordinatePair {\n    #[inline]\n    fn from(p: &CoordPair) -> Self {\n        use crate::values::computed::CSSPixelLength;\n        Self::new(\n            LengthPercentage::new_length(CSSPixelLength::new(p.x)),\n            LengthPercentage::new_length(CSSPixelLength::new(p.y)),\n        )\n    }\n}\n\nimpl From<&SVGPathPosition> for Position {\n    #[inline]\n    fn from(p: &SVGPathPosition) -> Self {\n        use crate::values::computed::CSSPixelLength;\n        Self::new(\n            LengthPercentage::new_length(CSSPixelLength::new(p.horizontal)),\n            LengthPercentage::new_length(CSSPixelLength::new(p.vertical)),\n        )\n    }\n}\n\nimpl From<&generic::CommandEndPoint<SVGPathPosition, CSSFloat>> for CommandEndPoint {\n    #[inline]\n    fn from(p: &generic::CommandEndPoint<SVGPathPosition, CSSFloat>) -> Self {\n        match p {\n            generic::CommandEndPoint::ToPosition(pos) => Self::ToPosition(pos.into()),\n            generic::CommandEndPoint::ByCoordinate(coord) => Self::ByCoordinate(coord.into()),\n        }\n    }\n}\n\nimpl From<&generic::AxisEndPoint<CSSFloat>> for AxisEndPoint {\n    #[inline]\n    fn from(p: &generic::AxisEndPoint<CSSFloat>) -> Self {\n        use crate::values::computed::CSSPixelLength;\n        use generic::AxisPosition;\n        match p {\n            generic::AxisEndPoint::ToPosition(AxisPosition::LengthPercent(lp)) => Self::ToPosition(\n                AxisPosition::LengthPercent(LengthPercentage::new_length(CSSPixelLength::new(*lp))),\n            ),\n            generic::AxisEndPoint::ToPosition(AxisPosition::Keyword(_)) => {\n                unreachable!(\"Invalid state: SVG path commands cannot contain a keyword.\")\n            },\n            generic::AxisEndPoint::ByCoordinate(pos) => {\n                Self::ByCoordinate(LengthPercentage::new_length(CSSPixelLength::new(*pos)))\n            },\n        }\n    }\n}\n\nimpl From<&generic::ControlPoint<SVGPathPosition, CSSFloat>> for ControlPoint {\n    #[inline]\n    fn from(p: &generic::ControlPoint<SVGPathPosition, CSSFloat>) -> Self {\n        match p {\n            generic::ControlPoint::Absolute(pos) => Self::Absolute(pos.into()),\n            generic::ControlPoint::Relative(point) => Self::Relative(RelativeControlPoint {\n                coord: CoordinatePair::from(&point.coord),\n                reference: point.reference,\n            }),\n        }\n    }\n}\n\nimpl From<&generic::ArcRadii<CSSFloat>> for generic::ArcRadii<LengthPercentage> {\n    #[inline]\n    fn from(p: &generic::ArcRadii<CSSFloat>) -> Self {\n        use crate::values::computed::CSSPixelLength;\n        Self {\n            rx: LengthPercentage::new_length(CSSPixelLength::new(p.rx)),\n            ry: p\n                .ry\n                .map(|v| LengthPercentage::new_length(CSSPixelLength::new(v))),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/computed/border.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values related to borders.\n\nuse crate::derives::*;\nuse crate::properties::{LogicalGroupId, LonghandId};\nuse crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};\nuse crate::values::computed::length::{\n    CSSPixelLength, NonNegativeLength, NonNegativeLengthPercentage,\n};\nuse crate::values::computed::{NonNegativeNumber, NonNegativeNumberOrPercentage};\nuse crate::values::generics::border::{\n    GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice,\n    GenericBorderRadius, GenericBorderSpacing,\n};\nuse crate::values::generics::rect::Rect;\nuse crate::values::generics::size::Size2D;\nuse crate::values::generics::NonNegative;\nuse crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};\nuse crate::Zero;\nuse app_units::Au;\n\npub use crate::values::specified::border::BorderImageRepeat;\n\n/// A computed value for -webkit-text-stroke-width.\npub type LineWidth = Au;\n\n/// A computed value for border-width (and the like).\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToTyped, From)]\n#[repr(transparent)]\npub struct BorderSideWidth(pub Au);\n\nimpl BorderSideWidth {\n    /// The `medium` value.\n    pub fn medium() -> Self {\n        Self(Au::from_px(3))\n    }\n}\n\nimpl ToAnimatedValue for BorderSideWidth {\n    type AnimatedValue = CSSPixelLength;\n\n    #[inline]\n    fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {\n        self.0.to_animated_value(context)\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Self(Au::from_animated_value(animated))\n    }\n}\n\nimpl ToResolvedValue for BorderSideWidth {\n    type ResolvedValue = CSSPixelLength;\n\n    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {\n        let resolved_length = CSSPixelLength::from(self.0).to_resolved_value(context);\n        if !context\n            .current_longhand\n            .is_some_and(|l| l.logical_group() == Some(LogicalGroupId::BorderWidth))\n        {\n            return resolved_length;\n        }\n        // Only for border widths, a style of none/hidden causes the resolved value to be zero.\n        let style = match context.current_longhand.unwrap() {\n            LonghandId::BorderTopWidth => context.style.clone_border_top_style(),\n            LonghandId::BorderRightWidth => context.style.clone_border_right_style(),\n            LonghandId::BorderBottomWidth => context.style.clone_border_bottom_style(),\n            LonghandId::BorderLeftWidth => context.style.clone_border_left_style(),\n            _ => {\n                debug_assert!(false, \"Expected a physical longhand\");\n                return resolved_length;\n            },\n        };\n        if style.none_or_hidden() {\n            return CSSPixelLength::new(0.0);\n        }\n        resolved_length\n    }\n\n    #[inline]\n    fn from_resolved_value(value: Self::ResolvedValue) -> Self {\n        Self(Au::from_f32_px(value.px()))\n    }\n}\n\n/// A computed value for outline-offset\npub type BorderSideOffset = Au;\n\n/// A computed value for the `border-image-width` property.\npub type BorderImageWidth = Rect<BorderImageSideWidth>;\n\n/// A computed value for a single side of a `border-image-width` property.\npub type BorderImageSideWidth =\n    GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;\n\n/// A computed value for the `border-image-slice` property.\npub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;\n\n/// A computed value for the `border-radius` property.\npub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;\n\n/// A computed value for the `border-*-radius` longhand properties.\npub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;\n\n/// A computed value for the `border-spacing` longhand property.\npub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;\n\nimpl BorderImageSideWidth {\n    /// Returns `1`.\n    #[inline]\n    pub fn one() -> Self {\n        GenericBorderImageSideWidth::Number(NonNegative(1.))\n    }\n}\n\nimpl BorderImageSlice {\n    /// Returns the `100%` value.\n    #[inline]\n    pub fn hundred_percent() -> Self {\n        GenericBorderImageSlice {\n            offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),\n            fill: false,\n        }\n    }\n}\n\nimpl BorderSpacing {\n    /// Returns `0 0`.\n    pub fn zero() -> Self {\n        GenericBorderSpacing(Size2D::new(\n            NonNegativeLength::zero(),\n            NonNegativeLength::zero(),\n        ))\n    }\n\n    /// Returns the horizontal spacing.\n    pub fn horizontal(&self) -> Au {\n        Au::from(*self.0.width())\n    }\n\n    /// Returns the vertical spacing.\n    pub fn vertical(&self) -> Au {\n        Au::from(*self.0.height())\n    }\n}\n"
  },
  {
    "path": "style/values/computed/box.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for box properties.\n\nuse crate::derives::*;\nuse crate::values::animated::{Animate, Procedure, ToAnimatedValue};\nuse crate::values::computed::length::{LengthPercentage, NonNegativeLength};\nuse crate::values::computed::{Context, Integer, Number, ToComputedValue};\nuse crate::values::generics::box_::{\n    GenericBaselineShift, GenericContainIntrinsicSize, GenericLineClamp, GenericOverflowClipMargin,\n    GenericPerspective,\n};\nuse crate::values::specified::box_ as specified;\nuse std::fmt;\nuse style_traits::{CssWriter, ToCss};\n\npub use crate::values::specified::box_::{\n    AlignmentBaseline, Appearance, BaselineSource, BreakBetween, BreakWithin, Clear, Contain,\n    ContainerName, ContainerType, ContentVisibility, Display, DominantBaseline, Float, Overflow,\n    OverflowAnchor, OverscrollBehavior, PositionProperty, ScrollSnapAlign, ScrollSnapAxis,\n    ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,\n    WritingModeProperty,\n};\n\n/// A computed value for the `baseline-shift` property.\npub type BaselineShift = GenericBaselineShift<LengthPercentage>;\n\n/// A computed value for the `overflow-clip-margin` property.\npub type OverflowClipMargin = GenericOverflowClipMargin<NonNegativeLength>;\n\n/// A computed value for the `contain-intrinsic-size` property.\npub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;\n\nimpl ContainIntrinsicSize {\n    /// Converts contain-intrinsic-size to auto style.\n    pub fn add_auto_if_needed(&self) -> Option<Self> {\n        Some(match *self {\n            Self::None => Self::AutoNone,\n            Self::Length(ref l) => Self::AutoLength(*l),\n            Self::AutoNone | Self::AutoLength(..) => return None,\n        })\n    }\n}\n\n/// A computed value for the `line-clamp` property.\npub type LineClamp = GenericLineClamp<Integer>;\n\nimpl Animate for LineClamp {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.is_none() != other.is_none() {\n            return Err(());\n        }\n        if self.is_none() {\n            return Ok(Self::none());\n        }\n        Ok(Self(self.0.animate(&other.0, procedure)?.max(1)))\n    }\n}\n\n/// A computed value for the `perspective` property.\npub type Perspective = GenericPerspective<NonNegativeLength>;\n\n/// A computed value for the `resize` property.\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToResolvedValue, ToTyped,\n)]\n#[repr(u8)]\npub enum Resize {\n    None,\n    Both,\n    Horizontal,\n    Vertical,\n}\n\nimpl ToComputedValue for specified::Resize {\n    type ComputedValue = Resize;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Resize {\n        let is_vertical = context.style().writing_mode.is_vertical();\n        match self {\n            specified::Resize::Inline => {\n                context\n                    .rule_cache_conditions\n                    .borrow_mut()\n                    .set_writing_mode_dependency(context.builder.writing_mode);\n                if is_vertical {\n                    Resize::Vertical\n                } else {\n                    Resize::Horizontal\n                }\n            },\n            specified::Resize::Block => {\n                context\n                    .rule_cache_conditions\n                    .borrow_mut()\n                    .set_writing_mode_dependency(context.builder.writing_mode);\n                if is_vertical {\n                    Resize::Horizontal\n                } else {\n                    Resize::Vertical\n                }\n            },\n            specified::Resize::None => Resize::None,\n            specified::Resize::Both => Resize::Both,\n            specified::Resize::Horizontal => Resize::Horizontal,\n            specified::Resize::Vertical => Resize::Vertical,\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Resize) -> specified::Resize {\n        match computed {\n            Resize::None => specified::Resize::None,\n            Resize::Both => specified::Resize::Both,\n            Resize::Horizontal => specified::Resize::Horizontal,\n            Resize::Vertical => specified::Resize::Vertical,\n        }\n    }\n}\n\n/// The computed `zoom` property value.\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct Zoom(f32);\n\nimpl ToComputedValue for specified::Zoom {\n    type ComputedValue = Zoom;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        let n = match *self {\n            Self::Normal => return Zoom::ONE,\n            Self::Document => return Zoom::DOCUMENT,\n            Self::Value(ref n) => n.0.to_number().get(),\n        };\n        if n == 0.0 {\n            // For legacy reasons, zoom: 0 (and 0%) computes to 1. ¯\\_(ツ)_/¯\n            return Zoom::ONE;\n        }\n        Zoom(n)\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::new_number(computed.value())\n    }\n}\n\nimpl ToCss for Zoom {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use std::fmt::Write;\n        if *self == Self::DOCUMENT {\n            return dest.write_str(\"document\");\n        }\n        self.value().to_css(dest)\n    }\n}\n\nimpl ToAnimatedValue for Zoom {\n    type AnimatedValue = Number;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.value()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Zoom(animated.max(0.0))\n    }\n}\n\nimpl Zoom {\n    /// The value 1. This is by far the most common value.\n    pub const ONE: Zoom = Zoom(1.0);\n\n    /// The `document` value. This can appear in the computed zoom property value, but not in the\n    /// `effective_zoom` field.\n    pub const DOCUMENT: Zoom = Zoom(0.0);\n\n    /// Returns whether we're the number 1.\n    #[inline]\n    pub fn is_one(self) -> bool {\n        self == Self::ONE\n    }\n\n    /// Returns whether we're the `document` keyword.\n    #[inline]\n    pub fn is_document(self) -> bool {\n        self == Self::DOCUMENT\n    }\n\n    /// Returns the inverse of our value.\n    #[inline]\n    pub fn inverted(&self) -> Option<Self> {\n        if self.0 == 0.0 {\n            return None;\n        }\n        Some(Self(1. / self.0))\n    }\n\n    /// Returns the value as a float.\n    #[inline]\n    pub fn value(&self) -> f32 {\n        self.0\n    }\n\n    /// Computes the effective zoom for a given new zoom value in rhs.\n    pub fn compute_effective(self, specified: Self) -> Self {\n        if specified == Self::DOCUMENT {\n            return Self::ONE;\n        }\n        if self == Self::ONE {\n            return specified;\n        }\n        if specified == Self::ONE {\n            return self;\n        }\n        Zoom(self.0 * specified.0)\n    }\n\n    /// Returns the zoomed value.\n    #[inline]\n    pub fn zoom(self, value: f32) -> f32 {\n        if self == Self::ONE {\n            return value;\n        }\n        value * self.value()\n    }\n\n    /// Returns the un-zoomed value.\n    #[inline]\n    pub fn unzoom(self, value: f32) -> f32 {\n        // Avoid division by zero if our effective zoom computation ends up being zero.\n        if self == Self::ONE || self.0 == 0.0 {\n            return value;\n        }\n        value / self.value()\n    }\n}\n"
  },
  {
    "path": "style/values/computed/color.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed color values.\n\nuse crate::color::AbsoluteColor;\nuse crate::values::animated::ToAnimatedZero;\nuse crate::values::computed::percentage::Percentage;\nuse crate::values::generics::color::{\n    GenericCaretColor, GenericColor, GenericColorMix, GenericColorOrAuto,\n};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\npub use crate::values::specified::color::{ColorScheme, ForcedColorAdjust, PrintColorAdjust};\n\n/// The computed value of the `color` property.\npub type ColorPropertyValue = AbsoluteColor;\n\n/// A computed value for `<color>`.\npub type Color = GenericColor<Percentage>;\n\n/// A computed color-mix().\npub type ColorMix = GenericColorMix<Color, Percentage>;\n\nimpl ToCss for Color {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match *self {\n            Self::Absolute(ref c) => c.to_css(dest),\n            Self::ColorFunction(ref color_function) => color_function.to_css(dest),\n            Self::CurrentColor => dest.write_str(\"currentcolor\"),\n            Self::ColorMix(ref m) => m.to_css(dest),\n            Self::ContrastColor(ref c) => {\n                dest.write_str(\"contrast-color(\")?;\n                c.to_css(dest)?;\n                dest.write_char(')')\n            },\n        }\n    }\n}\n\nimpl Color {\n    /// A fully transparent color.\n    pub const TRANSPARENT_BLACK: Self = Self::Absolute(AbsoluteColor::TRANSPARENT_BLACK);\n\n    /// An opaque black color.\n    pub const BLACK: Self = Self::Absolute(AbsoluteColor::BLACK);\n\n    /// An opaque white color.\n    pub const WHITE: Self = Self::Absolute(AbsoluteColor::WHITE);\n\n    /// Create a new computed [`Color`] from a given color-mix, simplifying it to an absolute color\n    /// if possible.\n    pub fn from_color_mix(color_mix: ColorMix) -> Self {\n        if let Some(absolute) = color_mix.mix_to_absolute() {\n            Self::Absolute(absolute)\n        } else {\n            Self::ColorMix(Box::new(color_mix))\n        }\n    }\n\n    /// Combine this complex color with the given foreground color into an\n    /// absolute color.\n    pub fn resolve_to_absolute(&self, current_color: &AbsoluteColor) -> AbsoluteColor {\n        use crate::values::specified::percentage::ToPercentage;\n\n        match *self {\n            Self::Absolute(c) => c,\n            Self::ColorFunction(ref color_function) => {\n                color_function.resolve_to_absolute(current_color)\n            },\n            Self::CurrentColor => *current_color,\n            Self::ColorMix(ref mix) => {\n                use crate::color::mix;\n\n                mix::mix_many(\n                    mix.interpolation,\n                    mix.items.iter().map(|item| {\n                        mix::ColorMixItem::new(\n                            item.color.resolve_to_absolute(current_color),\n                            item.percentage.to_percentage(),\n                        )\n                    }),\n                    mix.flags,\n                )\n            },\n            Self::ContrastColor(ref c) => {\n                let bg_color = c.resolve_to_absolute(current_color);\n                if Self::contrast_ratio(&bg_color, &AbsoluteColor::BLACK)\n                    > Self::contrast_ratio(&bg_color, &AbsoluteColor::WHITE)\n                {\n                    AbsoluteColor::BLACK\n                } else {\n                    AbsoluteColor::WHITE\n                }\n            },\n        }\n    }\n\n    fn contrast_ratio(a: &AbsoluteColor, b: &AbsoluteColor) -> f32 {\n        // TODO: This just implements the WCAG 2.1 algorithm,\n        // https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio\n        // Consider using a more sophisticated contrast algorithm, e.g. see\n        // https://apcacontrast.com\n        let compute = |c| -> f32 {\n            if c <= 0.04045 {\n                c / 12.92\n            } else {\n                f32::powf((c + 0.055) / 1.055, 2.4)\n            }\n        };\n        let luminance = |r, g, b| -> f32 { 0.2126 * r + 0.7152 * g + 0.0722 * b };\n        let a = a.into_srgb_legacy();\n        let b = b.into_srgb_legacy();\n        let a = a.raw_components();\n        let b = b.raw_components();\n        let la = luminance(compute(a[0]), compute(a[1]), compute(a[2])) + 0.05;\n        let lb = luminance(compute(b[0]), compute(b[1]), compute(b[2])) + 0.05;\n        if la > lb {\n            la / lb\n        } else {\n            lb / la\n        }\n    }\n}\n\nimpl ToAnimatedZero for AbsoluteColor {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(Self::TRANSPARENT_BLACK)\n    }\n}\n\n/// auto | <color>\npub type ColorOrAuto = GenericColorOrAuto<Color>;\n\n/// caret-color\npub type CaretColor = GenericCaretColor<Color>;\n"
  },
  {
    "path": "style/values/computed/column.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for the column properties.\n\nuse crate::values::computed::PositiveInteger;\nuse crate::values::generics::column::GenericColumnCount;\n\n/// A computed type for `column-count` values.\npub type ColumnCount = GenericColumnCount<PositiveInteger>;\n"
  },
  {
    "path": "style/values/computed/counters.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values for counter properties\n\nuse crate::values::computed::image::Image;\nuse crate::values::generics::counters as generics;\nuse crate::values::generics::counters::CounterIncrement as GenericCounterIncrement;\nuse crate::values::generics::counters::CounterReset as GenericCounterReset;\nuse crate::values::generics::counters::CounterSet as GenericCounterSet;\n\n/// A computed value for the `counter-increment` property.\npub type CounterIncrement = GenericCounterIncrement<i32>;\n\n/// A computed value for the `counter-reset` property.\npub type CounterReset = GenericCounterReset<i32>;\n\n/// A computed value for the `counter-set` property.\npub type CounterSet = GenericCounterSet<i32>;\n\n/// A computed value for the `content` property.\npub type Content = generics::GenericContent<Image>;\n\n/// A computed content item.\npub type ContentItem = generics::GenericContentItem<Image>;\n"
  },
  {
    "path": "style/values/computed/easing.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS Easing functions.\n\nuse euclid::approxeq::ApproxEq;\n\nuse crate::bezier::Bezier;\nuse crate::piecewise_linear::PiecewiseLinearFunction;\nuse crate::values::computed::{Integer, Number};\nuse crate::values::generics::easing::{self, BeforeFlag, StepPosition, TimingKeyword};\n\n/// A computed timing function.\npub type ComputedTimingFunction = easing::TimingFunction<Integer, Number, PiecewiseLinearFunction>;\n\n/// An alias of the computed timing function.\npub type TimingFunction = ComputedTimingFunction;\n\nimpl ComputedTimingFunction {\n    fn calculate_step_output(\n        steps: i32,\n        pos: StepPosition,\n        progress: f64,\n        before_flag: BeforeFlag,\n    ) -> f64 {\n        // User specified values can cause overflow (bug 1706157). Increments/decrements\n        // should be gravefully handled.\n        let mut current_step = (progress * (steps as f64)).floor() as i32;\n\n        // Increment current step if it is jump-start or start.\n        if pos == StepPosition::Start\n            || pos == StepPosition::JumpStart\n            || pos == StepPosition::JumpBoth\n        {\n            current_step = current_step.checked_add(1).unwrap_or(current_step);\n        }\n\n        // If the \"before flag\" is set and we are at a transition point,\n        // drop back a step\n        if before_flag == BeforeFlag::Set\n            && (progress * steps as f64).rem_euclid(1.0).approx_eq(&0.0)\n        {\n            current_step = current_step.checked_sub(1).unwrap_or(current_step);\n        }\n\n        // We should not produce a result outside [0, 1] unless we have an\n        // input outside that range. This takes care of steps that would otherwise\n        // occur at boundaries.\n        if progress >= 0.0 && current_step < 0 {\n            current_step = 0;\n        }\n\n        // |jumps| should always be in [1, i32::MAX].\n        let jumps = if pos == StepPosition::JumpBoth {\n            steps.checked_add(1).unwrap_or(steps)\n        } else if pos == StepPosition::JumpNone {\n            steps.checked_sub(1).unwrap_or(steps)\n        } else {\n            steps\n        };\n\n        if progress <= 1.0 && current_step > jumps {\n            current_step = jumps;\n        }\n\n        (current_step as f64) / (jumps as f64)\n    }\n\n    /// The output of the timing function given the progress ratio of this animation.\n    pub fn calculate_output(&self, progress: f64, before_flag: BeforeFlag, epsilon: f64) -> f64 {\n        let progress = match self {\n            TimingFunction::CubicBezier { x1, y1, x2, y2 } => {\n                Bezier::calculate_bezier_output(progress, epsilon, *x1, *y1, *x2, *y2)\n            },\n            TimingFunction::Steps(steps, pos) => {\n                Self::calculate_step_output(*steps, *pos, progress, before_flag)\n            },\n            TimingFunction::LinearFunction(function) => function.at(progress as f32).into(),\n            TimingFunction::Keyword(keyword) => match keyword {\n                TimingKeyword::Linear => progress,\n                TimingKeyword::Ease => {\n                    Bezier::calculate_bezier_output(progress, epsilon, 0.25, 0.1, 0.25, 1.)\n                },\n                TimingKeyword::EaseIn => {\n                    Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 1., 1.)\n                },\n                TimingKeyword::EaseOut => {\n                    Bezier::calculate_bezier_output(progress, epsilon, 0., 0., 0.58, 1.)\n                },\n                TimingKeyword::EaseInOut => {\n                    Bezier::calculate_bezier_output(progress, epsilon, 0.42, 0., 0.58, 1.)\n                },\n            },\n        };\n\n        // The output progress value of an easing function is a real number in the range:\n        // [-inf, inf].\n        // https://drafts.csswg.org/css-easing-1/#output-progress-value\n        //\n        // However, we expect to use the finite progress for interpolation and web-animations\n        // https://drafts.csswg.org/css-values-4/#interpolation\n        // https://drafts.csswg.org/web-animations-1/#dom-computedeffecttiming-progress\n        //\n        // So we clamp the infinite progress, per the spec issue:\n        // https://github.com/w3c/csswg-drafts/issues/8344\n        progress.min(f64::MAX).max(f64::MIN)\n    }\n}\n"
  },
  {
    "path": "style/values/computed/effects.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values related to effects.\n\nuse crate::values::computed::color::Color;\nuse crate::values::computed::length::{Length, NonNegativeLength};\n#[cfg(feature = \"gecko\")]\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Angle, Number};\nuse crate::values::generics::effects::BoxShadow as GenericBoxShadow;\nuse crate::values::generics::effects::Filter as GenericFilter;\nuse crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;\n#[cfg(not(feature = \"gecko\"))]\nuse crate::values::Impossible;\n\n/// A computed value for a single shadow of the `box-shadow` property.\npub type BoxShadow = GenericBoxShadow<Color, Length, NonNegativeLength, Length>;\n\n/// A computed value for a single `filter`.\n#[cfg(feature = \"gecko\")]\npub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow, ComputedUrl>;\n\n/// A computed value for a single `filter`.\n#[cfg(feature = \"servo\")]\npub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow, Impossible>;\n\n/// A computed value for the `drop-shadow()` filter.\npub type SimpleShadow = GenericSimpleShadow<Color, Length, NonNegativeLength>;\n"
  },
  {
    "path": "style/values/computed/flex.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values related to flexbox.\n\nuse crate::values::computed::Size;\nuse crate::values::generics::flex::FlexBasis as GenericFlexBasis;\n\n/// A computed value for the `flex-basis` property.\npub type FlexBasis = GenericFlexBasis<Size>;\n\nimpl FlexBasis {\n    /// `auto`\n    #[inline]\n    pub fn auto() -> Self {\n        GenericFlexBasis::Size(Size::auto())\n    }\n}\n"
  },
  {
    "path": "style/values/computed/font.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values for font properties\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::animated::ToAnimatedValue;\nuse crate::values::computed::{\n    Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,\n    ToComputedValue, Zoom,\n};\nuse crate::values::generics::font::{\n    FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,\n};\nuse crate::values::generics::{font as generics, NonNegative};\nuse crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};\nuse crate::values::specified::font::{\n    self as specified, KeywordInfo, MAX_FONT_WEIGHT, MIN_FONT_WEIGHT,\n};\nuse crate::values::specified::length::{FontBaseSize, LineHeightBase, NoCalcLength};\nuse crate::values::CSSInteger;\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, serialize_identifier, CssStringWriter, Parser};\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps};\nuse num_traits::abs;\nuse num_traits::cast::AsPrimitive;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\npub use crate::values::computed::Length as MozScriptMinSize;\npub use crate::values::specified::font::MozScriptSizeMultiplier;\npub use crate::values::specified::font::{FontPalette, FontSynthesis, FontSynthesisStyle};\npub use crate::values::specified::font::{\n    FontVariantAlternates, FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric,\n    QueryFontMetricsFlags, XLang, XTextScale,\n};\npub use crate::values::specified::Integer as SpecifiedInteger;\npub use crate::values::specified::Number as SpecifiedNumber;\n\n/// Generic template for font property type classes that use a fixed-point\n/// internal representation with `FRACTION_BITS` for the fractional part.\n///\n/// Values are constructed from and exposed as floating-point, but stored\n/// internally as fixed point, so there will be a quantization effect on\n/// fractional values, depending on the number of fractional bits used.\n///\n/// Using (16-bit) fixed-point types rather than floats for these style\n/// attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle; it\n/// will also tend to reduce the number of distinct font instances that get\n/// created, particularly when styles are animated or set to arbitrary values\n/// (e.g. by sliders in the UI), which should reduce pressure on graphics\n/// resources and improve cache hit rates.\n///\n/// cbindgen:derive-lt\n/// cbindgen:derive-lte\n/// cbindgen:derive-gt\n/// cbindgen:derive-gte\n#[repr(C)]\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    ToResolvedValue,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\npub struct FixedPoint<T, const FRACTION_BITS: u16> {\n    /// The actual representation.\n    pub value: T,\n}\n\nimpl<T, const FRACTION_BITS: u16> FixedPoint<T, FRACTION_BITS>\nwhere\n    T: AsPrimitive<f32>,\n    f32: AsPrimitive<T>,\n    u16: AsPrimitive<T>,\n{\n    const SCALE: u16 = 1 << FRACTION_BITS;\n    const INVERSE_SCALE: f32 = 1.0 / Self::SCALE as f32;\n\n    /// Returns a fixed-point bit from a floating-point context.\n    pub fn from_float(v: f32) -> Self {\n        Self {\n            value: (v * Self::SCALE as f32).round().as_(),\n        }\n    }\n\n    /// Returns the floating-point representation.\n    pub fn to_float(&self) -> f32 {\n        self.value.as_() * Self::INVERSE_SCALE\n    }\n}\n\n// We implement this and mul below only for u16 types, because u32 types might need more care about\n// overflow. But it's not hard to implement in either case.\nimpl<const FRACTION_BITS: u16> std::ops::Div for FixedPoint<u16, FRACTION_BITS> {\n    type Output = Self;\n    fn div(self, rhs: Self) -> Self {\n        Self {\n            value: (((self.value as u32) << (FRACTION_BITS as u32)) / (rhs.value as u32)) as u16,\n        }\n    }\n}\nimpl<const FRACTION_BITS: u16> std::ops::Mul for FixedPoint<u16, FRACTION_BITS> {\n    type Output = Self;\n    fn mul(self, rhs: Self) -> Self {\n        Self {\n            value: (((self.value as u32) * (rhs.value as u32)) >> (FRACTION_BITS as u32)) as u16,\n        }\n    }\n}\n\n/// font-weight: range 1..1000, fractional values permitted; keywords\n/// 'normal', 'bold' aliased to 400, 700 respectively.\n///\n/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)\npub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;\n\n/// This is an alias which is useful mostly as a cbindgen / C++ inference\n/// workaround.\npub type FontWeightFixedPoint = FixedPoint<u16, FONT_WEIGHT_FRACTION_BITS>;\n\n/// A value for the font-weight property per:\n///\n/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight\n///\n/// cbindgen:derive-lt\n/// cbindgen:derive-lte\n/// cbindgen:derive-gt\n/// cbindgen:derive-gte\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    ToResolvedValue,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct FontWeight(FontWeightFixedPoint);\nimpl ToAnimatedValue for FontWeight {\n    type AnimatedValue = Number;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.value()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        FontWeight::from_float(animated)\n    }\n}\n\nimpl ToCss for FontWeight {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.value().to_css(dest)\n    }\n}\n\nimpl ToTyped for FontWeight {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        self.value().to_typed(dest)\n    }\n}\n\nimpl FontWeight {\n    /// The `normal` keyword.\n    pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint {\n        value: 400 << FONT_WEIGHT_FRACTION_BITS,\n    });\n\n    /// The `bold` value.\n    pub const BOLD: FontWeight = FontWeight(FontWeightFixedPoint {\n        value: 700 << FONT_WEIGHT_FRACTION_BITS,\n    });\n\n    /// The threshold from which we consider a font bold.\n    pub const BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {\n        value: 600 << FONT_WEIGHT_FRACTION_BITS,\n    });\n\n    /// The threshold above which CSS font matching prefers bolder faces\n    /// over lighter ones.\n    pub const PREFER_BOLD_THRESHOLD: FontWeight = FontWeight(FontWeightFixedPoint {\n        value: 500 << FONT_WEIGHT_FRACTION_BITS,\n    });\n\n    /// Returns the `normal` keyword value.\n    pub fn normal() -> Self {\n        Self::NORMAL\n    }\n\n    /// Whether this weight is bold\n    pub fn is_bold(&self) -> bool {\n        *self >= Self::BOLD_THRESHOLD\n    }\n\n    /// Returns the value as a float.\n    pub fn value(&self) -> f32 {\n        self.0.to_float()\n    }\n\n    /// Construct a valid weight from a float value.\n    pub fn from_float(v: f32) -> Self {\n        Self(FixedPoint::from_float(\n            v.max(MIN_FONT_WEIGHT).min(MAX_FONT_WEIGHT),\n        ))\n    }\n\n    /// Return the bolder weight.\n    ///\n    /// See the table in:\n    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values\n    pub fn bolder(self) -> Self {\n        let value = self.value();\n        if value < 350. {\n            return Self::NORMAL;\n        }\n        if value < 550. {\n            return Self::BOLD;\n        }\n        Self::from_float(value.max(900.))\n    }\n\n    /// Return the lighter weight.\n    ///\n    /// See the table in:\n    /// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values\n    pub fn lighter(self) -> Self {\n        let value = self.value();\n        if value < 550. {\n            return Self::from_float(value.min(100.));\n        }\n        if value < 750. {\n            return Self::NORMAL;\n        }\n        Self::BOLD\n    }\n}\n\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedZero,\n    ToCss,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Serialize, Deserialize))]\n/// The computed value of font-size\npub struct FontSize {\n    /// The computed size, that we use to compute ems etc. This accounts for\n    /// e.g., text-zoom.\n    pub computed_size: NonNegativeLength,\n    /// The actual used size. This is the computed font size, potentially\n    /// constrained by other factors like minimum font-size settings and so on.\n    #[css(skip)]\n    pub used_size: NonNegativeLength,\n    /// If derived from a keyword, the keyword and additional transformations applied to it\n    #[css(skip)]\n    pub keyword_info: KeywordInfo,\n}\n\nimpl FontSize {\n    /// The actual computed font size.\n    #[inline]\n    pub fn computed_size(&self) -> Length {\n        self.computed_size.0\n    }\n\n    /// The actual used font size.\n    #[inline]\n    pub fn used_size(&self) -> Length {\n        self.used_size.0\n    }\n\n    /// Apply zoom to the font-size. This is usually done by ToComputedValue.\n    #[inline]\n    pub fn zoom(&self, zoom: Zoom) -> Self {\n        Self {\n            computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),\n            used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),\n            keyword_info: self.keyword_info,\n        }\n    }\n\n    #[inline]\n    /// Get default value of font size.\n    pub fn medium() -> Self {\n        Self {\n            computed_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),\n            used_size: NonNegative(Length::new(specified::FONT_MEDIUM_PX)),\n            keyword_info: KeywordInfo::medium(),\n        }\n    }\n}\n\nimpl ToAnimatedValue for FontSize {\n    type AnimatedValue = Length;\n\n    #[inline]\n    fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.computed_size.0.to_animated_value(context)\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        FontSize {\n            computed_size: NonNegative(animated.clamp_to_non_negative()),\n            used_size: NonNegative(animated.clamp_to_non_negative()),\n            keyword_info: KeywordInfo::none(),\n        }\n    }\n}\n\nimpl ToResolvedValue for FontSize {\n    type ResolvedValue = NonNegativeLength;\n\n    #[inline]\n    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {\n        self.computed_size.to_resolved_value(context)\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        let computed_size = NonNegativeLength::from_resolved_value(resolved);\n        Self {\n            computed_size,\n            used_size: computed_size,\n            keyword_info: KeywordInfo::none(),\n        }\n    }\n}\n\n#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue, ToTyped)]\n#[cfg_attr(feature = \"servo\", derive(Hash, Serialize, Deserialize))]\n/// Specifies a prioritized list of font family names or generic family names.\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct FontFamily {\n    /// The actual list of family names.\n    pub families: FontFamilyList,\n    /// Whether this font-family came from a specified system-font.\n    pub is_system_font: bool,\n    /// Whether this is the initial font-family that might react to language\n    /// changes.\n    pub is_initial: bool,\n}\n\nmacro_rules! static_font_family {\n    ($ident:ident, $family:expr) => {\n        static $ident: std::sync::LazyLock<FontFamily> = std::sync::LazyLock::new(|| FontFamily {\n            families: FontFamilyList {\n                list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)),\n            },\n            is_system_font: false,\n            is_initial: false,\n        });\n    };\n}\n\nimpl FontFamily {\n    #[inline]\n    /// Get default font family as `serif` which is a generic font-family\n    pub fn serif() -> Self {\n        Self::generic(GenericFontFamily::Serif).clone()\n    }\n\n    /// Returns the font family for `-moz-bullet-font`.\n    #[cfg(feature = \"gecko\")]\n    pub(crate) fn moz_bullet() -> &'static Self {\n        static_font_family!(\n            MOZ_BULLET,\n            SingleFontFamily::FamilyName(FamilyName {\n                name: atom!(\"-moz-bullet-font\"),\n                syntax: FontFamilyNameSyntax::Identifiers,\n            })\n        );\n\n        &*MOZ_BULLET\n    }\n\n    /// Returns a font family for a single system font.\n    #[cfg(feature = \"gecko\")]\n    pub fn for_system_font(name: &str) -> Self {\n        Self {\n            families: FontFamilyList {\n                list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(\n                    FamilyName {\n                        name: Atom::from(name),\n                        syntax: FontFamilyNameSyntax::Identifiers,\n                    },\n                ))),\n            },\n            is_system_font: true,\n            is_initial: false,\n        }\n    }\n\n    /// Returns a generic font family.\n    pub fn generic(generic: GenericFontFamily) -> &'static Self {\n        macro_rules! generic_font_family {\n            ($ident:ident, $family:ident) => {\n                static_font_family!(\n                    $ident,\n                    SingleFontFamily::Generic(GenericFontFamily::$family)\n                )\n            };\n        }\n\n        generic_font_family!(SERIF, Serif);\n        generic_font_family!(SANS_SERIF, SansSerif);\n        generic_font_family!(MONOSPACE, Monospace);\n        generic_font_family!(CURSIVE, Cursive);\n        generic_font_family!(FANTASY, Fantasy);\n        #[cfg(feature = \"gecko\")]\n        generic_font_family!(MATH, Math);\n        #[cfg(feature = \"gecko\")]\n        generic_font_family!(MOZ_EMOJI, MozEmoji);\n        generic_font_family!(SYSTEM_UI, SystemUi);\n\n        let family = match generic {\n            GenericFontFamily::None => {\n                debug_assert!(false, \"Bogus caller!\");\n                &*SERIF\n            },\n            GenericFontFamily::Serif => &*SERIF,\n            GenericFontFamily::SansSerif => &*SANS_SERIF,\n            GenericFontFamily::Monospace => &*MONOSPACE,\n            GenericFontFamily::Cursive => &*CURSIVE,\n            GenericFontFamily::Fantasy => &*FANTASY,\n            #[cfg(feature = \"gecko\")]\n            GenericFontFamily::Math => &*MATH,\n            #[cfg(feature = \"gecko\")]\n            GenericFontFamily::MozEmoji => &*MOZ_EMOJI,\n            GenericFontFamily::SystemUi => &*SYSTEM_UI,\n        };\n        debug_assert_eq!(\n            *family.families.iter().next().unwrap(),\n            SingleFontFamily::Generic(generic)\n        );\n        family\n    }\n}\n\nimpl MallocSizeOf for FontFamily {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        use malloc_size_of::MallocUnconditionalSizeOf;\n        // SharedFontList objects are generally measured from the pointer stored\n        // in the specified value. So only count this if the SharedFontList is\n        // unshared.\n        let shared_font_list = &self.families.list;\n        if shared_font_list.is_unique() {\n            shared_font_list.unconditional_size_of(ops)\n        } else {\n            0\n        }\n    }\n}\n\nimpl ToCss for FontFamily {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        let mut iter = self.families.iter();\n        match iter.next() {\n            Some(f) => f.to_css(dest)?,\n            None => return Ok(()),\n        }\n        for family in iter {\n            dest.write_str(\", \")?;\n            family.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// The name of a font family of choice.\n#[derive(\n    Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct FamilyName {\n    /// Name of the font family.\n    pub name: Atom,\n    /// Syntax of the font family.\n    pub syntax: FontFamilyNameSyntax,\n}\n\n#[cfg(feature = \"gecko\")]\nimpl FamilyName {\n    fn is_known_icon_font_family(&self) -> bool {\n        use crate::gecko_bindings::bindings;\n        unsafe { bindings::Gecko_IsKnownIconFontFamily(self.name.as_ptr()) }\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl FamilyName {\n    fn is_known_icon_font_family(&self) -> bool {\n        false\n    }\n}\n\nimpl ToCss for FamilyName {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        match self.syntax {\n            FontFamilyNameSyntax::Quoted => {\n                dest.write_char('\"')?;\n                write!(CssStringWriter::new(dest), \"{}\", self.name)?;\n                dest.write_char('\"')\n            },\n            FontFamilyNameSyntax::Identifiers => {\n                let mut first = true;\n                for ident in self.name.to_string().split(' ') {\n                    if first {\n                        first = false;\n                    } else {\n                        dest.write_char(' ')?;\n                    }\n                    debug_assert!(\n                        !ident.is_empty(),\n                        \"Family name with leading, \\\n                         trailing, or consecutive white spaces should \\\n                         have been marked quoted by the parser\"\n                    );\n                    serialize_identifier(ident, dest)?;\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\n#[derive(\n    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n/// Font family names must either be given quoted as strings,\n/// or unquoted as a sequence of one or more identifiers.\n#[repr(u8)]\npub enum FontFamilyNameSyntax {\n    /// The family name was specified in a quoted form, e.g. \"Font Name\"\n    /// or 'Font Name'.\n    Quoted,\n\n    /// The family name was specified in an unquoted form as a sequence of\n    /// identifiers.\n    Identifiers,\n}\n\n/// A set of faces that vary in weight, width or slope.\n/// cbindgen:derive-mut-casts=true\n#[derive(\n    Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize, Hash))]\n#[repr(u8)]\npub enum SingleFontFamily {\n    /// The name of a font family of choice.\n    FamilyName(FamilyName),\n    /// Generic family name.\n    Generic(GenericFontFamily),\n}\n\nfn system_ui_enabled(_: &ParserContext) -> bool {\n    static_prefs::pref!(\"layout.css.system-ui.enabled\")\n}\n\n#[cfg(feature = \"gecko\")]\nfn math_enabled(context: &ParserContext) -> bool {\n    context.chrome_rules_enabled() || static_prefs::pref!(\"mathml.font_family_math.enabled\")\n}\n\n/// A generic font-family name.\n///\n/// The order here is important, if you change it make sure that\n/// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s\n/// sSingleGenerics are updated as well.\n///\n/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC\n/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 /\n/// bug 1726515.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(u32)]\n#[allow(missing_docs)]\npub enum GenericFontFamily {\n    /// No generic family specified, only for internal usage.\n    ///\n    /// NOTE(emilio): Gecko code relies on this variant being zero.\n    #[css(skip)]\n    None = 0,\n    Serif,\n    SansSerif,\n    #[parse(aliases = \"-moz-fixed\")]\n    Monospace,\n    Cursive,\n    Fantasy,\n    #[cfg(feature = \"gecko\")]\n    #[parse(condition = \"math_enabled\")]\n    Math,\n    #[parse(condition = \"system_ui_enabled\")]\n    SystemUi,\n    /// An internal value for emoji font selection.\n    #[css(skip)]\n    #[cfg(feature = \"gecko\")]\n    MozEmoji,\n}\n\nimpl GenericFontFamily {\n    /// When we disallow websites to override fonts, we ignore some generic\n    /// families that the website might specify, since they're not configured by\n    /// the user. See bug 789788 and bug 1730098.\n    pub(crate) fn valid_for_user_font_prioritization(self) -> bool {\n        match self {\n            Self::None | Self::Cursive | Self::Fantasy | Self::SystemUi => false,\n            #[cfg(feature = \"gecko\")]\n            Self::Math | Self::MozEmoji => false,\n            Self::Serif | Self::SansSerif | Self::Monospace => true,\n        }\n    }\n}\n\nimpl Parse for SingleFontFamily {\n    /// Parse a font-family value.\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) {\n            return Ok(SingleFontFamily::FamilyName(FamilyName {\n                name: Atom::from(&*value),\n                syntax: FontFamilyNameSyntax::Quoted,\n            }));\n        }\n\n        if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) {\n            return Ok(SingleFontFamily::Generic(generic));\n        }\n\n        let first_ident = input.expect_ident_cloned()?;\n        let reserved = match_ignore_ascii_case! { &first_ident,\n            // https://drafts.csswg.org/css-fonts/#propdef-font-family\n            // \"Font family names that happen to be the same as a keyword value\n            //  (`inherit`, `serif`, `sans-serif`, `monospace`, `fantasy`, and `cursive`)\n            //  must be quoted to prevent confusion with the keywords with the same names.\n            //  The keywords ‘initial’ and ‘default’ are reserved for future use\n            //  and must also be quoted when used as font names.\n            //  UAs must not consider these keywords as matching the <family-name> type.\"\n            \"inherit\" | \"initial\" | \"unset\" | \"revert\" | \"default\" => true,\n            _ => false,\n        };\n\n        let mut value = first_ident.as_ref().to_owned();\n        let mut serialize_quoted = value.contains(' ');\n\n        // These keywords are not allowed by themselves.\n        // The only way this value can be valid with with another keyword.\n        if reserved {\n            let ident = input.expect_ident()?;\n            serialize_quoted = serialize_quoted || ident.contains(' ');\n            value.push(' ');\n            value.push_str(&ident);\n        }\n        while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {\n            serialize_quoted = serialize_quoted || ident.contains(' ');\n            value.push(' ');\n            value.push_str(&ident);\n        }\n        let syntax = if serialize_quoted {\n            // For font family names which contains special white spaces, e.g.\n            // `font-family: \\ a\\ \\ b\\ \\ c\\ ;`, it is tricky to serialize them\n            // as identifiers correctly. Just mark them quoted so we don't need\n            // to worry about them in serialization code.\n            FontFamilyNameSyntax::Quoted\n        } else {\n            FontFamilyNameSyntax::Identifiers\n        };\n        Ok(SingleFontFamily::FamilyName(FamilyName {\n            name: Atom::from(value),\n            syntax,\n        }))\n    }\n}\n\n/// A list of font families.\n#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize, Hash))]\n#[repr(C)]\npub struct FontFamilyList {\n    /// The actual list of font families specified.\n    pub list: crate::ArcSlice<SingleFontFamily>,\n}\n\nimpl FontFamilyList {\n    /// Return iterator of SingleFontFamily\n    pub fn iter(&self) -> impl Iterator<Item = &SingleFontFamily> {\n        self.list.iter()\n    }\n\n    /// If there's a generic font family on the list which is suitable for user\n    /// font prioritization, then move it ahead of the other families in the list,\n    /// except for any families known to be ligature-based icon fonts, where using a\n    /// generic instead of the site's specified font may cause substantial breakage.\n    /// If no suitable generic is found in the list, insert the default generic ahead\n    /// of all the listed families except for known ligature-based icon fonts.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    pub(crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) {\n        let mut index_of_first_generic = None;\n        let mut target_index = None;\n\n        for (i, f) in self.iter().enumerate() {\n            match &*f {\n                SingleFontFamily::Generic(f) => {\n                    if index_of_first_generic.is_none() && f.valid_for_user_font_prioritization() {\n                        // If we haven't found a target position, there's nothing to do;\n                        // this entry is already ahead of everything except any whitelisted\n                        // icon fonts.\n                        if target_index.is_none() {\n                            return;\n                        }\n                        index_of_first_generic = Some(i);\n                        break;\n                    }\n                    // A non-prioritized generic (e.g. cursive, fantasy) becomes the target\n                    // position for prioritization, just like arbitrary named families.\n                    if target_index.is_none() {\n                        target_index = Some(i);\n                    }\n                },\n                SingleFontFamily::FamilyName(fam) => {\n                    // Target position for the first generic is in front of the first\n                    // non-whitelisted icon font family we find.\n                    if target_index.is_none() && !fam.is_known_icon_font_family() {\n                        target_index = Some(i);\n                    }\n                },\n            }\n        }\n\n        let mut new_list = self.list.iter().cloned().collect::<Vec<_>>();\n        let first_generic = match index_of_first_generic {\n            Some(i) => new_list.remove(i),\n            None => SingleFontFamily::Generic(generic),\n        };\n\n        if let Some(i) = target_index {\n            new_list.insert(i, first_generic);\n        } else {\n            new_list.push(first_generic);\n        }\n        self.list = crate::ArcSlice::from_iter(new_list.into_iter());\n    }\n\n    /// Returns whether we need to prioritize user fonts.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    pub(crate) fn needs_user_font_prioritization(&self) -> bool {\n        self.iter().next().map_or(true, |f| match f {\n            SingleFontFamily::Generic(f) => !f.valid_for_user_font_prioritization(),\n            _ => true,\n        })\n    }\n\n    /// Return the generic ID if it is a single generic font\n    pub fn single_generic(&self) -> Option<GenericFontFamily> {\n        let mut iter = self.iter();\n        if let Some(SingleFontFamily::Generic(f)) = iter.next() {\n            if iter.next().is_none() {\n                return Some(*f);\n            }\n        }\n        None\n    }\n}\n\n/// Preserve the readability of text when font fallback occurs.\npub type FontSizeAdjust = generics::GenericFontSizeAdjust<NonNegativeNumber>;\n\nimpl FontSizeAdjust {\n    #[inline]\n    /// Default value of font-size-adjust\n    pub fn none() -> Self {\n        FontSizeAdjust::None\n    }\n}\n\nimpl ToComputedValue for specified::FontSizeAdjust {\n    type ComputedValue = FontSizeAdjust;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        use crate::font_metrics::FontMetricsOrientation;\n\n        let font_metrics = |vertical, flags| {\n            let orient = if vertical {\n                FontMetricsOrientation::MatchContextPreferVertical\n            } else {\n                FontMetricsOrientation::Horizontal\n            };\n            let metrics = context.query_font_metrics(FontBaseSize::CurrentStyle, orient, flags);\n            let font_size = context.style().get_font().clone_font_size().used_size.0;\n            (metrics, font_size)\n        };\n\n        // Macro to resolve a from-font value using the given metric field. If not present,\n        // returns the fallback value, or if that is negative, resolves using ascent instead\n        // of the missing field (this is the fallback for cap-height).\n        macro_rules! resolve {\n            ($basis:ident, $value:expr, $vertical:expr, $field:ident, $fallback:expr, $flags:expr) => {{\n                match $value {\n                    specified::FontSizeAdjustFactor::Number(f) => {\n                        FontSizeAdjust::$basis(f.to_computed_value(context))\n                    },\n                    specified::FontSizeAdjustFactor::FromFont => {\n                        let (metrics, font_size) = font_metrics($vertical, $flags);\n                        let ratio = if let Some(metric) = metrics.$field {\n                            metric / font_size\n                        } else if $fallback >= 0.0 {\n                            $fallback\n                        } else {\n                            metrics.ascent / font_size\n                        };\n                        if ratio.is_nan() {\n                            FontSizeAdjust::$basis(NonNegative(abs($fallback)))\n                        } else {\n                            FontSizeAdjust::$basis(NonNegative(ratio))\n                        }\n                    },\n                }\n            }};\n        }\n\n        match *self {\n            Self::None => FontSizeAdjust::None,\n            Self::ExHeight(val) => {\n                resolve!(\n                    ExHeight,\n                    val,\n                    false,\n                    x_height,\n                    0.5,\n                    QueryFontMetricsFlags::empty()\n                )\n            },\n            Self::CapHeight(val) => {\n                resolve!(\n                    CapHeight,\n                    val,\n                    false,\n                    cap_height,\n                    -1.0, /* fall back to ascent */\n                    QueryFontMetricsFlags::empty()\n                )\n            },\n            Self::ChWidth(val) => {\n                resolve!(\n                    ChWidth,\n                    val,\n                    false,\n                    zero_advance_measure,\n                    0.5,\n                    QueryFontMetricsFlags::NEEDS_CH\n                )\n            },\n            Self::IcWidth(val) => {\n                resolve!(\n                    IcWidth,\n                    val,\n                    false,\n                    ic_width,\n                    1.0,\n                    QueryFontMetricsFlags::NEEDS_IC\n                )\n            },\n            Self::IcHeight(val) => {\n                resolve!(\n                    IcHeight,\n                    val,\n                    true,\n                    ic_width,\n                    1.0,\n                    QueryFontMetricsFlags::NEEDS_IC\n                )\n            },\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        macro_rules! case {\n            ($basis:ident, $val:expr) => {\n                Self::$basis(specified::FontSizeAdjustFactor::Number(\n                    ToComputedValue::from_computed_value($val),\n                ))\n            };\n        }\n        match *computed {\n            FontSizeAdjust::None => Self::None,\n            FontSizeAdjust::ExHeight(ref val) => case!(ExHeight, val),\n            FontSizeAdjust::CapHeight(ref val) => case!(CapHeight, val),\n            FontSizeAdjust::ChWidth(ref val) => case!(ChWidth, val),\n            FontSizeAdjust::IcWidth(ref val) => case!(IcWidth, val),\n            FontSizeAdjust::IcHeight(ref val) => case!(IcHeight, val),\n        }\n    }\n}\n\n/// Use FontSettings as computed type of FontFeatureSettings.\npub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;\n\n/// The computed value for font-variation-settings.\npub type FontVariationSettings = FontSettings<VariationValue<Number>>;\n\n// The computed value of font-{feature,variation}-settings discards values\n// with duplicate tags, keeping only the last occurrence of each tag.\nfn dedup_font_settings<T>(settings_list: &mut Vec<T>)\nwhere\n    T: TaggedFontValue,\n{\n    if settings_list.len() > 1 {\n        settings_list.sort_by_key(|k| k.tag().0);\n        // dedup() keeps the first of any duplicates, but we want the last,\n        // so we implement it manually here.\n        let mut prev_tag = settings_list.last().unwrap().tag();\n        for i in (0..settings_list.len() - 1).rev() {\n            let cur_tag = settings_list[i].tag();\n            if cur_tag == prev_tag {\n                settings_list.remove(i);\n            }\n            prev_tag = cur_tag;\n        }\n    }\n}\n\nimpl<T> ToComputedValue for FontSettings<T>\nwhere\n    T: ToComputedValue,\n    <T as ToComputedValue>::ComputedValue: TaggedFontValue,\n{\n    type ComputedValue = FontSettings<T::ComputedValue>;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        let mut v = self\n            .0\n            .iter()\n            .map(|item| item.to_computed_value(context))\n            .collect::<Vec<_>>();\n        dedup_font_settings(&mut v);\n        FontSettings(v.into_boxed_slice())\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self(computed.0.iter().map(T::from_computed_value).collect())\n    }\n}\n\n/// font-language-override can only have a single 1-4 ASCII character\n/// OpenType \"language system\" tag, so we should be able to compute\n/// it and store it as a 32-bit integer\n/// (see http://www.microsoft.com/typography/otspec/languagetags.htm).\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[typed(todo_derive_fields)]\n#[value_info(other_values = \"normal\")]\npub struct FontLanguageOverride(pub u32);\n\nimpl FontLanguageOverride {\n    #[inline]\n    /// Get computed default value of `font-language-override` with 0\n    pub fn normal() -> FontLanguageOverride {\n        FontLanguageOverride(0)\n    }\n\n    /// Returns this value as a `&str`, backed by `storage`.\n    #[inline]\n    pub(crate) fn to_str(self, storage: &mut [u8; 4]) -> &str {\n        *storage = u32::to_be_bytes(self.0);\n        // Safe because we ensure it's ASCII during parsing\n        let slice = if cfg!(debug_assertions) {\n            std::str::from_utf8(&storage[..]).unwrap()\n        } else {\n            unsafe { std::str::from_utf8_unchecked(&storage[..]) }\n        };\n        slice.trim_end()\n    }\n\n    /// Unsafe because `Self::to_str` requires the value to represent a UTF-8\n    /// string.\n    #[inline]\n    pub unsafe fn from_u32(value: u32) -> Self {\n        Self(value)\n    }\n}\n\nimpl ToCss for FontLanguageOverride {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if self.0 == 0 {\n            return dest.write_str(\"normal\");\n        }\n        self.to_str(&mut [0; 4]).to_css(dest)\n    }\n}\n\nimpl ToComputedValue for specified::MozScriptMinSize {\n    type ComputedValue = MozScriptMinSize;\n\n    fn to_computed_value(&self, cx: &Context) -> MozScriptMinSize {\n        // this value is used in the computation of font-size, so\n        // we use the parent size\n        let base_size = FontBaseSize::InheritedStyle;\n        let line_height_base = LineHeightBase::InheritedStyle;\n        match self.0 {\n            NoCalcLength::FontRelative(value) => {\n                value.to_computed_value(cx, base_size, line_height_base)\n            },\n            NoCalcLength::ServoCharacterWidth(value) => {\n                value.to_computed_value(base_size.resolve(cx).computed_size())\n            },\n            ref l => l.to_computed_value(cx),\n        }\n    }\n\n    fn from_computed_value(other: &MozScriptMinSize) -> Self {\n        specified::MozScriptMinSize(ToComputedValue::from_computed_value(other))\n    }\n}\n\n/// The computed value of the math-depth property.\npub type MathDepth = i8;\n\n#[cfg(feature = \"gecko\")]\nimpl ToComputedValue for specified::MathDepth {\n    type ComputedValue = MathDepth;\n\n    fn to_computed_value(&self, cx: &Context) -> i8 {\n        use crate::properties::longhands::math_style::SpecifiedValue as MathStyleValue;\n        use std::{cmp, i8};\n\n        let int = match *self {\n            specified::MathDepth::AutoAdd => {\n                let parent = cx.builder.get_parent_font().clone_math_depth() as i32;\n                let style = cx.builder.get_parent_font().clone_math_style();\n                if style == MathStyleValue::Compact {\n                    parent.saturating_add(1)\n                } else {\n                    parent\n                }\n            },\n            specified::MathDepth::Add(rel) => {\n                let parent = cx.builder.get_parent_font().clone_math_depth();\n                (parent as i32).saturating_add(rel.to_computed_value(cx))\n            },\n            specified::MathDepth::Absolute(abs) => abs.to_computed_value(cx),\n        };\n        cmp::min(int, i8::MAX as i32) as i8\n    }\n\n    fn from_computed_value(other: &i8) -> Self {\n        let computed_value = *other as i32;\n        specified::MathDepth::Absolute(SpecifiedInteger::from_computed_value(&computed_value))\n    }\n}\n\nimpl ToAnimatedValue for MathDepth {\n    type AnimatedValue = CSSInteger;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.into()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        use std::{cmp, i8};\n        cmp::min(animated, i8::MAX as i32) as i8\n    }\n}\n\n/// - Use a signed 8.8 fixed-point value (representable range -128.0..128)\n///\n/// Values of <angle> below -90 or above 90 are not permitted, so we use an out\n/// of range value to represent `italic`.\npub const FONT_STYLE_FRACTION_BITS: u16 = 8;\n\n/// This is an alias which is useful mostly as a cbindgen / C++ inference\n/// workaround.\npub type FontStyleFixedPoint = FixedPoint<i16, FONT_STYLE_FRACTION_BITS>;\n\n/// The computed value of `font-style`.\n///\n/// - Define angle of zero degrees as `normal`\n/// - Define out-of-range value 100 degrees as `italic`\n/// - Other values represent `oblique <angle>`\n///\n/// cbindgen:derive-lt\n/// cbindgen:derive-lte\n/// cbindgen:derive-gt\n/// cbindgen:derive-gte\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct FontStyle(FontStyleFixedPoint);\n\nimpl FontStyle {\n    /// The `normal` keyword, equal to `oblique` with angle zero.\n    pub const NORMAL: FontStyle = FontStyle(FontStyleFixedPoint {\n        value: 0 << FONT_STYLE_FRACTION_BITS,\n    });\n\n    /// The italic keyword.\n    pub const ITALIC: FontStyle = FontStyle(FontStyleFixedPoint {\n        value: 100 << FONT_STYLE_FRACTION_BITS,\n    });\n\n    /// The default angle for `font-style: oblique`.\n    /// See also https://github.com/w3c/csswg-drafts/issues/2295\n    pub const DEFAULT_OBLIQUE_DEGREES: i16 = 14;\n\n    /// The `oblique` keyword with the default degrees.\n    pub const OBLIQUE: FontStyle = FontStyle(FontStyleFixedPoint {\n        value: Self::DEFAULT_OBLIQUE_DEGREES << FONT_STYLE_FRACTION_BITS,\n    });\n\n    /// The `normal` value.\n    #[inline]\n    pub fn normal() -> Self {\n        Self::NORMAL\n    }\n\n    /// Returns the oblique angle for this style.\n    pub fn oblique(degrees: f32) -> Self {\n        Self(FixedPoint::from_float(\n            degrees\n                .max(specified::FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)\n                .min(specified::FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES),\n        ))\n    }\n\n    /// Returns the oblique angle for this style.\n    pub fn oblique_degrees(&self) -> f32 {\n        debug_assert_ne!(*self, Self::ITALIC);\n        self.0.to_float()\n    }\n}\n\nimpl ToCss for FontStyle {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if *self == Self::NORMAL {\n            return dest.write_str(\"normal\");\n        }\n        if *self == Self::ITALIC {\n            return dest.write_str(\"italic\");\n        }\n        dest.write_str(\"oblique\")?;\n        if *self != Self::OBLIQUE {\n            // It's not the default oblique amount, so append the angle in degrees.\n            dest.write_char(' ')?;\n            Angle::from_degrees(self.oblique_degrees()).to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl ToAnimatedValue for FontStyle {\n    type AnimatedValue = generics::FontStyle<Angle>;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        if self == Self::ITALIC {\n            return generics::FontStyle::Italic;\n        }\n        generics::FontStyle::Oblique(Angle::from_degrees(self.oblique_degrees()))\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        match animated {\n            generics::FontStyle::Italic => Self::ITALIC,\n            generics::FontStyle::Oblique(ref angle) => Self::oblique(angle.degrees()),\n        }\n    }\n}\n\n/// font-stretch is a percentage relative to normal.\n///\n/// We use an unsigned 10.6 fixed-point value (range 0.0 - 1023.984375)\n///\n/// We arbitrarily limit here to 1000%. (If that becomes a problem, we could\n/// reduce the number of fractional bits and increase the limit.)\npub const FONT_STRETCH_FRACTION_BITS: u16 = 6;\n\n/// This is an alias which is useful mostly as a cbindgen / C++ inference\n/// workaround.\npub type FontStretchFixedPoint = FixedPoint<u16, FONT_STRETCH_FRACTION_BITS>;\n\n/// A value for the font-stretch property per:\n///\n/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch\n///\n/// cbindgen:derive-lt\n/// cbindgen:derive-lte\n/// cbindgen:derive-gt\n/// cbindgen:derive-gte\n#[derive(\n    Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Hash, Serialize))]\n#[repr(C)]\npub struct FontStretch(pub FontStretchFixedPoint);\n\nimpl FontStretch {\n    /// The fraction bits, as an easy-to-access-constant.\n    pub const FRACTION_BITS: u16 = FONT_STRETCH_FRACTION_BITS;\n    /// 0.5 in our floating point representation.\n    pub const HALF: u16 = 1 << (Self::FRACTION_BITS - 1);\n\n    /// The `ultra-condensed` keyword.\n    pub const ULTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 50 << Self::FRACTION_BITS,\n    });\n    /// The `extra-condensed` keyword.\n    pub const EXTRA_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: (62 << Self::FRACTION_BITS) + Self::HALF,\n    });\n    /// The `condensed` keyword.\n    pub const CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 75 << Self::FRACTION_BITS,\n    });\n    /// The `semi-condensed` keyword.\n    pub const SEMI_CONDENSED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: (87 << Self::FRACTION_BITS) + Self::HALF,\n    });\n    /// The `normal` keyword.\n    pub const NORMAL: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 100 << Self::FRACTION_BITS,\n    });\n    /// The `semi-expanded` keyword.\n    pub const SEMI_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: (112 << Self::FRACTION_BITS) + Self::HALF,\n    });\n    /// The `expanded` keyword.\n    pub const EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 125 << Self::FRACTION_BITS,\n    });\n    /// The `extra-expanded` keyword.\n    pub const EXTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 150 << Self::FRACTION_BITS,\n    });\n    /// The `ultra-expanded` keyword.\n    pub const ULTRA_EXPANDED: FontStretch = FontStretch(FontStretchFixedPoint {\n        value: 200 << Self::FRACTION_BITS,\n    });\n\n    /// 100%\n    pub fn hundred() -> Self {\n        Self::NORMAL\n    }\n\n    /// Converts to a computed percentage.\n    #[inline]\n    pub fn to_percentage(&self) -> Percentage {\n        Percentage(self.0.to_float() / 100.0)\n    }\n\n    /// Converts from a computed percentage value.\n    pub fn from_percentage(p: f32) -> Self {\n        Self(FixedPoint::from_float((p * 100.).max(0.0).min(1000.0)))\n    }\n\n    /// Returns a relevant stretch value from a keyword.\n    /// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop\n    pub fn from_keyword(kw: specified::FontStretchKeyword) -> Self {\n        use specified::FontStretchKeyword::*;\n        match kw {\n            UltraCondensed => Self::ULTRA_CONDENSED,\n            ExtraCondensed => Self::EXTRA_CONDENSED,\n            Condensed => Self::CONDENSED,\n            SemiCondensed => Self::SEMI_CONDENSED,\n            Normal => Self::NORMAL,\n            SemiExpanded => Self::SEMI_EXPANDED,\n            Expanded => Self::EXPANDED,\n            ExtraExpanded => Self::EXTRA_EXPANDED,\n            UltraExpanded => Self::ULTRA_EXPANDED,\n        }\n    }\n\n    /// Returns the stretch keyword if we map to one of the relevant values.\n    pub fn as_keyword(&self) -> Option<specified::FontStretchKeyword> {\n        use specified::FontStretchKeyword::*;\n        // TODO: Can we use match here?\n        if *self == Self::ULTRA_CONDENSED {\n            return Some(UltraCondensed);\n        }\n        if *self == Self::EXTRA_CONDENSED {\n            return Some(ExtraCondensed);\n        }\n        if *self == Self::CONDENSED {\n            return Some(Condensed);\n        }\n        if *self == Self::SEMI_CONDENSED {\n            return Some(SemiCondensed);\n        }\n        if *self == Self::NORMAL {\n            return Some(Normal);\n        }\n        if *self == Self::SEMI_EXPANDED {\n            return Some(SemiExpanded);\n        }\n        if *self == Self::EXPANDED {\n            return Some(Expanded);\n        }\n        if *self == Self::EXTRA_EXPANDED {\n            return Some(ExtraExpanded);\n        }\n        if *self == Self::ULTRA_EXPANDED {\n            return Some(UltraExpanded);\n        }\n        None\n    }\n}\n\nimpl ToCss for FontStretch {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.to_percentage().to_css(dest)\n    }\n}\n\nimpl ToTyped for FontStretch {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        match self.as_keyword() {\n            Some(keyword) => keyword.to_typed(dest),\n            None => self.to_percentage().to_typed(dest),\n        }\n    }\n}\n\nimpl ToAnimatedValue for FontStretch {\n    type AnimatedValue = Percentage;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.to_percentage()\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Self::from_percentage(animated.0)\n    }\n}\n\n/// A computed value for the `line-height` property.\npub type LineHeight = generics::GenericLineHeight<NonNegativeNumber, NonNegativeLength>;\n\nimpl ToResolvedValue for LineHeight {\n    type ResolvedValue = Self;\n\n    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {\n        #[cfg(feature = \"gecko\")]\n        {\n            // Resolve <number> to an absolute <length> based on font size.\n            if matches!(self, Self::Normal | Self::MozBlockHeight) {\n                return self;\n            }\n            let wm = context.style.writing_mode;\n            Self::Length(\n                context\n                    .device\n                    .calc_line_height(\n                        context.style.get_font(),\n                        wm,\n                        Some(context.element_info.element),\n                    )\n                    .to_resolved_value(context),\n            )\n        }\n        #[cfg(feature = \"servo\")]\n        {\n            if let LineHeight::Number(num) = &self {\n                let size = context.style.get_font().clone_font_size().computed_size();\n                LineHeight::Length(NonNegativeLength::new(size.px() * num.0))\n            } else {\n                self\n            }\n        }\n    }\n\n    #[inline]\n    fn from_resolved_value(value: Self::ResolvedValue) -> Self {\n        value\n    }\n}\n"
  },
  {
    "path": "style/values/computed/image.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the computed value of\n//! [`image`][image]s\n//!\n//! [image]: https://drafts.csswg.org/css-images/#image-values\n\nuse crate::derives::*;\nuse crate::values::computed::percentage::Percentage;\nuse crate::values::computed::position::Position;\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Angle, Color, Context};\nuse crate::values::computed::{\n    AngleOrPercentage, Length, LengthPercentage, NonNegativeLength, NonNegativeLengthPercentage,\n    Resolution, ToComputedValue,\n};\nuse crate::values::generics::image::{self as generic, GradientCompatMode};\nuse crate::values::specified::image as specified;\nuse crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};\nuse std::f32::consts::PI;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\npub use specified::ImageRendering;\n\n/// Computed values for an image according to CSS-IMAGES.\n/// <https://drafts.csswg.org/css-images/#image-values>\npub type Image = generic::GenericImage<Gradient, ComputedUrl, Color, Percentage, Resolution>;\n\n// Images should remain small, see https://github.com/servo/servo/pull/18430\n#[cfg(feature = \"gecko\")]\nsize_of_test!(Image, 16);\n#[cfg(feature = \"servo\")]\nsize_of_test!(Image, 24);\n\n/// Computed values for a CSS gradient.\n/// <https://drafts.csswg.org/css-images/#gradients>\npub type Gradient = generic::GenericGradient<\n    LineDirection,\n    Length,\n    LengthPercentage,\n    Position,\n    Angle,\n    AngleOrPercentage,\n    Color,\n>;\n\n/// Computed values for CSS cross-fade\n/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>\npub type CrossFade = generic::CrossFade<Image, Color, Percentage>;\n\n/// A computed radial gradient ending shape.\npub type EndingShape = generic::GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage>;\n\n/// A computed gradient line direction.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToResolvedValue)]\n#[repr(C, u8)]\npub enum LineDirection {\n    /// An angle.\n    Angle(Angle),\n    /// A horizontal direction.\n    Horizontal(HorizontalPositionKeyword),\n    /// A vertical direction.\n    Vertical(VerticalPositionKeyword),\n    /// A corner.\n    Corner(HorizontalPositionKeyword, VerticalPositionKeyword),\n}\n\n/// The computed value for an `image-set()` image.\npub type ImageSet = generic::GenericImageSet<Image, Resolution>;\n\nimpl ToComputedValue for specified::ImageSet {\n    type ComputedValue = ImageSet;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        let items = self.items.to_computed_value(context);\n        let dpr = context.device().device_pixel_ratio().get();\n\n        let mut supported_image = false;\n        let mut selected_index = std::usize::MAX;\n        let mut selected_resolution = 0.0;\n\n        for (i, item) in items.iter().enumerate() {\n            if item.has_mime_type && !context.device().is_supported_mime_type(&item.mime_type) {\n                // If the MIME type is not supported, we discard the ImageSetItem.\n                continue;\n            }\n\n            let candidate_resolution = item.resolution.dppx();\n            debug_assert!(\n                candidate_resolution >= 0.0,\n                \"Resolutions should be non-negative\"\n            );\n            if candidate_resolution == 0.0 {\n                // If the resolution is 0, we also treat it as an invalid image.\n                continue;\n            }\n\n            // https://drafts.csswg.org/css-images-4/#image-set-notation:\n            //\n            //     Make a UA-specific choice of which to load, based on whatever criteria deemed\n            //     relevant (such as the resolution of the display, connection speed, etc).\n            //\n            // For now, select the lowest resolution greater than display density, otherwise the\n            // greatest resolution available.\n            let better_candidate = || {\n                if selected_resolution < dpr && candidate_resolution > selected_resolution {\n                    return true;\n                }\n                if candidate_resolution < selected_resolution && candidate_resolution >= dpr {\n                    return true;\n                }\n                false\n            };\n\n            // The first item with a supported MIME type is obviously the current best candidate\n            if !supported_image || better_candidate() {\n                supported_image = true;\n                selected_index = i;\n                selected_resolution = candidate_resolution;\n            }\n        }\n\n        ImageSet {\n            selected_index,\n            items,\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self {\n            selected_index: std::usize::MAX,\n            items: ToComputedValue::from_computed_value(&computed.items),\n        }\n    }\n}\n\nimpl generic::LineDirection for LineDirection {\n    fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {\n        match *self {\n            LineDirection::Angle(angle) => angle.radians() == PI,\n            LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {\n                compat_mode == GradientCompatMode::Modern\n            },\n            LineDirection::Vertical(VerticalPositionKeyword::Top) => {\n                compat_mode != GradientCompatMode::Modern\n            },\n            _ => false,\n        }\n    }\n\n    fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            LineDirection::Angle(ref angle) => angle.to_css(dest),\n            LineDirection::Horizontal(x) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                x.to_css(dest)\n            },\n            LineDirection::Vertical(y) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                y.to_css(dest)\n            },\n            LineDirection::Corner(x, y) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                x.to_css(dest)?;\n                dest.write_char(' ')?;\n                y.to_css(dest)\n            },\n        }\n    }\n}\n\nimpl ToComputedValue for specified::LineDirection {\n    type ComputedValue = LineDirection;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            specified::LineDirection::Angle(ref angle) => {\n                LineDirection::Angle(angle.to_computed_value(context))\n            },\n            specified::LineDirection::Horizontal(x) => LineDirection::Horizontal(x),\n            specified::LineDirection::Vertical(y) => LineDirection::Vertical(y),\n            specified::LineDirection::Corner(x, y) => LineDirection::Corner(x, y),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match *computed {\n            LineDirection::Angle(ref angle) => {\n                specified::LineDirection::Angle(ToComputedValue::from_computed_value(angle))\n            },\n            LineDirection::Horizontal(x) => specified::LineDirection::Horizontal(x),\n            LineDirection::Vertical(y) => specified::LineDirection::Vertical(y),\n            LineDirection::Corner(x, y) => specified::LineDirection::Corner(x, y),\n        }\n    }\n}\n\nimpl ToComputedValue for specified::Image {\n    type ComputedValue = Image;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match self {\n            Self::None => Image::None,\n            Self::Url(u) => Image::Url(u.to_computed_value(context)),\n            Self::Gradient(g) => Image::Gradient(g.to_computed_value(context)),\n            #[cfg(feature = \"gecko\")]\n            Self::Element(e) => Image::Element(e.to_computed_value(context)),\n            #[cfg(feature = \"gecko\")]\n            Self::MozSymbolicIcon(e) => Image::MozSymbolicIcon(e.to_computed_value(context)),\n            #[cfg(feature = \"servo\")]\n            Self::PaintWorklet(w) => Image::PaintWorklet(w.to_computed_value(context)),\n            Self::CrossFade(f) => Image::CrossFade(f.to_computed_value(context)),\n            Self::ImageSet(s) => Image::ImageSet(s.to_computed_value(context)),\n            Self::LightDark(ld) => ld.compute(context),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match computed {\n            Image::None => Self::None,\n            Image::Url(u) => Self::Url(ToComputedValue::from_computed_value(u)),\n            Image::Gradient(g) => Self::Gradient(ToComputedValue::from_computed_value(g)),\n            #[cfg(feature = \"gecko\")]\n            Image::Element(e) => Self::Element(ToComputedValue::from_computed_value(e)),\n            #[cfg(feature = \"gecko\")]\n            Image::MozSymbolicIcon(e) => {\n                Self::MozSymbolicIcon(ToComputedValue::from_computed_value(e))\n            },\n            #[cfg(feature = \"servo\")]\n            Image::PaintWorklet(w) => Self::PaintWorklet(ToComputedValue::from_computed_value(w)),\n            Image::CrossFade(f) => Self::CrossFade(ToComputedValue::from_computed_value(f)),\n            Image::ImageSet(s) => Self::ImageSet(ToComputedValue::from_computed_value(s)),\n            Image::LightDark(_) => unreachable!(\"Shouldn't have computed image-set values\"),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/computed/length.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! `<length>` computed values, and related ones.\n\nuse super::{Context, Number, ToComputedValue};\nuse crate::derives::*;\nuse crate::logical_geometry::PhysicalSide;\nuse crate::values::animated::{Context as AnimatedContext, ToAnimatedValue};\nuse crate::values::computed::position::TryTacticAdjustment;\nuse crate::values::computed::{NonNegativeNumber, Percentage, Zoom};\nuse crate::values::generics::length::{\n    GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,\n};\n#[cfg(feature = \"gecko\")]\nuse crate::values::generics::position::TreeScoped;\nuse crate::values::generics::NonNegative;\nuse crate::values::generics::{length as generics, ClampToNonNegative};\nuse crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};\nuse crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};\n#[cfg(feature = \"gecko\")]\nuse crate::values::DashedIdent;\nuse crate::values::{specified, CSSFloat};\nuse crate::Zero;\nuse app_units::Au;\nuse std::fmt::{self, Write};\nuse std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign};\nuse style_traits::{\n    CSSPixel, CssString, CssWriter, NumericValue, ToCss, ToTyped, TypedValue, UnitValue,\n};\nuse thin_vec::ThinVec;\n\npub use super::image::Image;\npub use super::length_percentage::{LengthPercentage, NonNegativeLengthPercentage};\npub use crate::values::specified::url::UrlOrNone;\npub use crate::values::specified::{Angle, BorderStyle, Time};\n\nimpl ToComputedValue for specified::NoCalcLength {\n    type ComputedValue = Length;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.to_computed_value_with_base_size(\n            context,\n            FontBaseSize::CurrentStyle,\n            LineHeightBase::CurrentStyle,\n        )\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::Absolute(AbsoluteLength::Px(computed.px()))\n    }\n}\n\nimpl specified::NoCalcLength {\n    /// Computes a length with a given font-relative base size.\n    pub fn to_computed_value_with_base_size(\n        &self,\n        context: &Context,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> Length {\n        match *self {\n            Self::Absolute(length) => length.to_computed_value(context),\n            Self::FontRelative(length) => {\n                length.to_computed_value(context, base_size, line_height_base)\n            },\n            Self::ViewportPercentage(length) => length.to_computed_value(context),\n            Self::ContainerRelative(length) => length.to_computed_value(context),\n            Self::ServoCharacterWidth(length) => length\n                .to_computed_value(context.style().get_font().clone_font_size().computed_size()),\n        }\n    }\n}\n\nimpl ToComputedValue for specified::Length {\n    type ComputedValue = Length;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            Self::NoCalc(l) => l.to_computed_value(context),\n            Self::Calc(ref calc) => {\n                let result = calc.to_computed_value(context);\n                debug_assert!(\n                    result.to_length().is_some(),\n                    \"{:?} didn't resolve to a length: {:?}\",\n                    calc,\n                    result,\n                );\n                result.to_length().unwrap_or_else(Length::zero)\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::NoCalc(specified::NoCalcLength::from_computed_value(computed))\n    }\n}\n\n/// Some boilerplate to share between negative and non-negative\n/// length-percentage or auto.\nmacro_rules! computed_length_percentage_or_auto {\n    ($inner:ty) => {\n        /// Returns the used value.\n        #[inline]\n        pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {\n            match *self {\n                Self::Auto => None,\n                Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)),\n            }\n        }\n    };\n}\n\n/// A computed type for `<length-percentage> | auto`.\npub type LengthPercentageOrAuto = generics::GenericLengthPercentageOrAuto<LengthPercentage>;\n\nimpl LengthPercentageOrAuto {\n    /// Clamps the value to a non-negative value.\n    pub fn clamp_to_non_negative(self) -> Self {\n        use crate::values::generics::length::LengthPercentageOrAuto::*;\n        match self {\n            LengthPercentage(l) => LengthPercentage(l.clamp_to_non_negative()),\n            Auto => Auto,\n        }\n    }\n\n    /// Convert to have a borrow inside the enum\n    pub fn as_ref(&self) -> generics::GenericLengthPercentageOrAuto<&LengthPercentage> {\n        use crate::values::generics::length::LengthPercentageOrAuto::*;\n        match *self {\n            LengthPercentage(ref lp) => LengthPercentage(lp),\n            Auto => Auto,\n        }\n    }\n\n    computed_length_percentage_or_auto!(LengthPercentage);\n}\n\nimpl generics::GenericLengthPercentageOrAuto<&LengthPercentage> {\n    /// Resolves the percentage.\n    #[inline]\n    pub fn percentage_relative_to(&self, basis: Length) -> LengthOrAuto {\n        use crate::values::generics::length::LengthPercentageOrAuto::*;\n        match self {\n            LengthPercentage(length_percentage) => {\n                LengthPercentage(length_percentage.percentage_relative_to(basis))\n            },\n            Auto => Auto,\n        }\n    }\n\n    /// Maybe resolves the percentage.\n    #[inline]\n    pub fn maybe_percentage_relative_to(&self, basis: Option<Length>) -> LengthOrAuto {\n        use crate::values::generics::length::LengthPercentageOrAuto::*;\n        match self {\n            LengthPercentage(length_percentage) => length_percentage\n                .maybe_percentage_relative_to(basis)\n                .map_or(Auto, LengthPercentage),\n            Auto => Auto,\n        }\n    }\n}\n\n/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.\npub type NonNegativeLengthPercentageOrAuto =\n    generics::GenericLengthPercentageOrAuto<NonNegativeLengthPercentage>;\n\nimpl NonNegativeLengthPercentageOrAuto {\n    computed_length_percentage_or_auto!(NonNegativeLengthPercentage);\n}\n\n/// The computed `<length>` value.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    Serialize,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct CSSPixelLength(CSSFloat);\n\nimpl ToResolvedValue for CSSPixelLength {\n    type ResolvedValue = Self;\n\n    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {\n        Self(context.style.effective_zoom.unzoom(self.0))\n    }\n\n    #[inline]\n    fn from_resolved_value(value: Self::ResolvedValue) -> Self {\n        value\n    }\n}\n\nimpl ToAnimatedValue for CSSPixelLength {\n    type AnimatedValue = Self;\n\n    fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {\n        Self(context.style.effective_zoom.unzoom(self.0))\n    }\n\n    #[inline]\n    fn from_animated_value(value: Self::AnimatedValue) -> Self {\n        value\n    }\n}\n\nimpl fmt::Debug for CSSPixelLength {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.0.fmt(f)?;\n        f.write_str(\" px\")\n    }\n}\n\nimpl CSSPixelLength {\n    /// Return a new CSSPixelLength.\n    #[inline]\n    pub fn new(px: CSSFloat) -> Self {\n        CSSPixelLength(px)\n    }\n\n    /// Returns a normalized (NaN turned to zero) version of this length.\n    #[inline]\n    pub fn normalized(self) -> Self {\n        Self::new(crate::values::normalize(self.0))\n    }\n\n    /// Returns a finite (normalized and clamped to float min and max) version of this length.\n    #[inline]\n    pub fn finite(self) -> Self {\n        Self::new(crate::values::normalize(self.0).min(f32::MAX).max(f32::MIN))\n    }\n\n    /// Scale the length by a given amount.\n    #[inline]\n    pub fn scale_by(self, scale: CSSFloat) -> Self {\n        CSSPixelLength(self.0 * scale)\n    }\n\n    /// Return the containing pixel value.\n    #[inline]\n    pub fn px(self) -> CSSFloat {\n        self.0\n    }\n\n    /// Zooms a particular length.\n    #[inline]\n    pub fn zoom(self, zoom: Zoom) -> Self {\n        Self::new(zoom.zoom(self.px()))\n    }\n\n    /// Return the length with app_unit i32 type.\n    #[inline]\n    pub fn to_i32_au(self) -> i32 {\n        Au::from(self).0\n    }\n\n    /// Return the absolute value of this length.\n    #[inline]\n    pub fn abs(self) -> Self {\n        CSSPixelLength::new(self.0.abs())\n    }\n\n    /// Return the clamped value of this length.\n    #[inline]\n    pub fn clamp_to_non_negative(self) -> Self {\n        CSSPixelLength::new(self.0.max(0.))\n    }\n\n    /// Returns the minimum between `self` and `other`.\n    #[inline]\n    pub fn min(self, other: Self) -> Self {\n        CSSPixelLength::new(self.0.min(other.0))\n    }\n\n    /// Returns the maximum between `self` and `other`.\n    #[inline]\n    pub fn max(self, other: Self) -> Self {\n        CSSPixelLength::new(self.0.max(other.0))\n    }\n\n    /// Sets `self` to the maximum between `self` and `other`.\n    #[inline]\n    pub fn max_assign(&mut self, other: Self) {\n        *self = self.max(other);\n    }\n\n    /// Clamp the value to a lower bound and an optional upper bound.\n    ///\n    /// Can be used for example with `min-width` and `max-width`.\n    #[inline]\n    pub fn clamp_between_extremums(self, min_size: Self, max_size: Option<Self>) -> Self {\n        self.clamp_below_max(max_size).max(min_size)\n    }\n\n    /// Clamp the value to an optional upper bound.\n    ///\n    /// Can be used for example with `max-width`.\n    #[inline]\n    pub fn clamp_below_max(self, max_size: Option<Self>) -> Self {\n        match max_size {\n            None => self,\n            Some(max_size) => self.min(max_size),\n        }\n    }\n}\n\nimpl num_traits::Zero for CSSPixelLength {\n    fn zero() -> Self {\n        CSSPixelLength::new(0.)\n    }\n\n    fn is_zero(&self) -> bool {\n        self.px() == 0.\n    }\n}\n\nimpl ToCss for CSSPixelLength {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.0.to_css(dest)?;\n        dest.write_str(\"px\")\n    }\n}\n\nimpl ToTyped for CSSPixelLength {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {\n            value: self.0 as f32,\n            unit: CssString::from(\"px\"),\n        })));\n        Ok(())\n    }\n}\n\nimpl std::iter::Sum for CSSPixelLength {\n    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {\n        iter.fold(Length::zero(), Add::add)\n    }\n}\n\nimpl Add for CSSPixelLength {\n    type Output = Self;\n\n    #[inline]\n    fn add(self, other: Self) -> Self {\n        Self::new(self.px() + other.px())\n    }\n}\n\nimpl AddAssign for CSSPixelLength {\n    #[inline]\n    fn add_assign(&mut self, other: Self) {\n        self.0 += other.0;\n    }\n}\n\nimpl Div for CSSPixelLength {\n    type Output = CSSFloat;\n\n    #[inline]\n    fn div(self, other: Self) -> CSSFloat {\n        self.px() / other.px()\n    }\n}\n\nimpl Div<CSSFloat> for CSSPixelLength {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, other: CSSFloat) -> Self {\n        Self::new(self.px() / other)\n    }\n}\n\nimpl MulAssign<CSSFloat> for CSSPixelLength {\n    #[inline]\n    fn mul_assign(&mut self, other: CSSFloat) {\n        self.0 *= other;\n    }\n}\n\nimpl Mul<CSSFloat> for CSSPixelLength {\n    type Output = Self;\n\n    #[inline]\n    fn mul(self, other: CSSFloat) -> Self {\n        Self::new(self.px() * other)\n    }\n}\n\nimpl Neg for CSSPixelLength {\n    type Output = Self;\n\n    #[inline]\n    fn neg(self) -> Self {\n        CSSPixelLength::new(-self.0)\n    }\n}\n\nimpl Sub for CSSPixelLength {\n    type Output = Self;\n\n    #[inline]\n    fn sub(self, other: Self) -> Self {\n        Self::new(self.px() - other.px())\n    }\n}\n\nimpl SubAssign for CSSPixelLength {\n    #[inline]\n    fn sub_assign(&mut self, other: Self) {\n        self.0 -= other.0;\n    }\n}\n\nimpl From<CSSPixelLength> for Au {\n    #[inline]\n    fn from(len: CSSPixelLength) -> Self {\n        Au::from_f32_px(len.0)\n    }\n}\n\nimpl From<Au> for CSSPixelLength {\n    #[inline]\n    fn from(len: Au) -> Self {\n        CSSPixelLength::new(len.to_f32_px())\n    }\n}\n\nimpl From<CSSPixelLength> for euclid::Length<CSSFloat, CSSPixel> {\n    #[inline]\n    fn from(length: CSSPixelLength) -> Self {\n        Self::new(length.0)\n    }\n}\n\n/// An alias of computed `<length>` value.\npub type Length = CSSPixelLength;\n\n/// Either a computed `<length>` or the `auto` keyword.\npub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;\n\n/// Either a non-negative `<length>` or the `auto` keyword.\npub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;\n\n/// Either a computed `<length>` or a `<number>` value.\npub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;\n\n/// A wrapper of Length, whose value must be >= 0.\npub type NonNegativeLength = NonNegative<Length>;\n\nimpl ClampToNonNegative for Length {\n    fn clamp_to_non_negative(self) -> Self {\n        Self::new(self.px().max(0.))\n    }\n}\n\nimpl NonNegativeLength {\n    /// Create a NonNegativeLength.\n    #[inline]\n    pub fn new(px: CSSFloat) -> Self {\n        NonNegative(Length::new(px.max(0.)))\n    }\n\n    /// Return the pixel value of |NonNegativeLength|.\n    #[inline]\n    pub fn px(&self) -> CSSFloat {\n        self.0.px()\n    }\n\n    #[inline]\n    /// Ensures it is non negative\n    pub fn clamp(self) -> Self {\n        if (self.0).0 < 0. {\n            Self::zero()\n        } else {\n            self\n        }\n    }\n}\n\nimpl From<Length> for NonNegativeLength {\n    #[inline]\n    fn from(len: Length) -> Self {\n        NonNegative(len)\n    }\n}\n\nimpl From<Au> for NonNegativeLength {\n    #[inline]\n    fn from(au: Au) -> Self {\n        NonNegative(au.into())\n    }\n}\n\nimpl From<NonNegativeLength> for Au {\n    #[inline]\n    fn from(non_negative_len: NonNegativeLength) -> Self {\n        Au::from(non_negative_len.0)\n    }\n}\n\n/// Either a computed NonNegativeLengthPercentage or the `normal` keyword.\npub type NonNegativeLengthPercentageOrNormal =\n    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;\n\n/// Either a non-negative `<length>` or a `<number>`.\npub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;\n\n/// A computed value for `min-width`, `min-height`, `width` or `height` property.\npub type Size = GenericSize<NonNegativeLengthPercentage>;\n\n/// A computed value for `max-width` or `max-height` property.\npub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;\n\n#[cfg(feature = \"gecko\")]\nuse crate::{\n    gecko_bindings::structs::AnchorPosResolutionParams, logical_geometry::PhysicalAxis,\n    values::generics::length::AnchorSizeKeyword,\n};\n\n/// Resolve the anchor function with the given resolver. Returns `Err()` if no anchor is found.\n/// `prop_axis`, axis of the property (e.g. `margin-left` -> Horizontal axis), is used if the\n/// anchor size keyword is not specified.\n#[cfg(feature = \"gecko\")]\npub fn resolve_anchor_size(\n    anchor_name: &TreeScoped<DashedIdent>,\n    prop_axis: PhysicalAxis,\n    anchor_size_keyword: AnchorSizeKeyword,\n    params: &AnchorPosResolutionParams,\n) -> Result<Length, ()> {\n    use crate::gecko_bindings::structs::Gecko_GetAnchorPosSize;\n\n    let mut offset = Length::zero();\n    let valid = unsafe {\n        Gecko_GetAnchorPosSize(\n            params,\n            anchor_name.value.0.as_ptr(),\n            &anchor_name.scope,\n            prop_axis as u8,\n            anchor_size_keyword as u8,\n            &mut offset,\n        )\n    };\n\n    if !valid {\n        return Err(());\n    }\n\n    Ok(offset)\n}\n\n/// A computed type for `margin` properties.\npub type Margin = generics::GenericMargin<LengthPercentage>;\n\nimpl TryTacticAdjustment for MaxSize {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        debug_assert!(\n            old_side.orthogonal_to(new_side),\n            \"Sizes should only change axes\"\n        );\n        match self {\n            Self::FitContentFunction(lp)\n            | Self::LengthPercentage(lp)\n            | Self::AnchorContainingCalcFunction(lp) => {\n                lp.try_tactic_adjustment(old_side, new_side);\n            },\n            Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),\n            Self::None\n            | Self::MaxContent\n            | Self::MinContent\n            | Self::FitContent\n            | Self::WebkitFillAvailable\n            | Self::Stretch => {},\n            #[cfg(feature = \"gecko\")]\n            Self::MozAvailable => {},\n        }\n    }\n}\n\nimpl TryTacticAdjustment for Size {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        debug_assert!(\n            old_side.orthogonal_to(new_side),\n            \"Sizes should only change axes\"\n        );\n        match self {\n            Self::FitContentFunction(lp)\n            | Self::LengthPercentage(lp)\n            | Self::AnchorContainingCalcFunction(lp) => {\n                lp.try_tactic_adjustment(old_side, new_side);\n            },\n            Self::AnchorSizeFunction(s) => s.try_tactic_adjustment(old_side, new_side),\n            Self::Auto\n            | Self::MaxContent\n            | Self::MinContent\n            | Self::FitContent\n            | Self::WebkitFillAvailable\n            | Self::Stretch => {},\n            #[cfg(feature = \"gecko\")]\n            Self::MozAvailable => {},\n        }\n    }\n}\n\nimpl TryTacticAdjustment for Percentage {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        if old_side.parallel_to(new_side) {\n            self.0 = 1.0 - self.0;\n        }\n    }\n}\n\nimpl TryTacticAdjustment for Margin {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        match self {\n            Self::Auto => {},\n            Self::LengthPercentage(lp) | Self::AnchorContainingCalcFunction(lp) => {\n                lp.try_tactic_adjustment(old_side, new_side)\n            },\n            Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/computed/length_percentage.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! `<length-percentage>` computed values, and related ones.\n//!\n//! The over-all design is a tagged pointer, with the lower bits of the pointer\n//! being non-zero if it is a non-calc value.\n//!\n//! It is expected to take 64 bits both in x86 and x86-64. This is implemented\n//! as a `union`, with 4 different variants:\n//!\n//!  * The length and percentage variants have a { tag, f32 } (effectively)\n//!    layout. The tag has to overlap with the lower 2 bits of the calc variant.\n//!\n//!  * The `calc()` variant is a { tag, pointer } in x86 (so same as the\n//!    others), or just a { pointer } in x86-64 (so that the two bits of the tag\n//!    can be obtained from the lower bits of the pointer).\n//!\n//!  * There's a `tag` variant just to make clear when only the tag is intended\n//!    to be read. Note that the tag needs to be masked always by `TAG_MASK`, to\n//!    deal with the pointer variant in x86-64.\n//!\n//! The assertions in the constructor methods ensure that the tag getter matches\n//! our expectations.\n\nuse super::{position::AnchorSide, Context, Length, Percentage, ToComputedValue};\nuse crate::derives::*;\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::{AnchorPosOffsetResolutionParams, GeckoFontMetrics};\nuse crate::logical_geometry::{PhysicalAxis, PhysicalSide};\nuse crate::values::animated::{\n    Animate, Context as AnimatedContext, Procedure, ToAnimatedValue, ToAnimatedZero,\n};\nuse crate::values::computed::position::TryTacticAdjustment;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::calc::{CalcUnits, GenericAnchorFunctionFallback, PositivePercentageBasis};\n#[cfg(feature = \"gecko\")]\nuse crate::values::generics::length::AnchorResolutionResult;\nuse crate::values::generics::position::GenericAnchorSide;\nuse crate::values::generics::{calc, ClampToNonNegative, NonNegative};\nuse crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};\nuse crate::values::specified::length::{EqualsPercentage, FontBaseSize, LineHeightBase};\nuse crate::values::{specified, CSSFloat};\nuse crate::{Zero, ZeroNoPercent};\nuse app_units::Au;\nuse debug_unreachable::debug_unreachable;\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps};\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{self, Write};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{CssWriter, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\n#[doc(hidden)]\n#[derive(Clone, Copy)]\n#[repr(C)]\npub struct LengthVariant {\n    tag: u8,\n    length: Length,\n}\n\n#[doc(hidden)]\n#[derive(Clone, Copy)]\n#[repr(C)]\npub struct PercentageVariant {\n    tag: u8,\n    percentage: Percentage,\n}\n\n// NOTE(emilio): cbindgen only understands the #[cfg] on the top level\n// definition.\n#[doc(hidden)]\n#[derive(Clone, Copy)]\n#[repr(C)]\n#[cfg(target_pointer_width = \"32\")]\npub struct CalcVariant {\n    tag: u8,\n    // Ideally CalcLengthPercentage, but that would cause circular references\n    // for leaves referencing LengthPercentage.\n    ptr: *mut (),\n}\n\n#[doc(hidden)]\n#[derive(Clone, Copy)]\n#[repr(C)]\n#[cfg(target_pointer_width = \"64\")]\npub struct CalcVariant {\n    ptr: usize, // In little-endian byte order\n}\n\n// `CalcLengthPercentage` is `Send + Sync` as asserted below.\nunsafe impl Send for CalcVariant {}\nunsafe impl Sync for CalcVariant {}\n\n#[doc(hidden)]\n#[derive(Clone, Copy)]\n#[repr(C)]\npub struct TagVariant {\n    tag: u8,\n}\n\n/// A `<length-percentage>` value. This can be either a `<length>`, a\n/// `<percentage>`, or a combination of both via `calc()`.\n///\n/// cbindgen:private-default-tagged-enum-constructor=false\n/// cbindgen:derive-mut-casts=true\n///\n/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage\n///\n/// The tag is stored in the lower two bits.\n///\n/// We need to use a struct instead of the union directly because unions with\n/// Drop implementations are unstable, looks like.\n///\n/// Also we need the union and the variants to be `pub` (even though the member\n/// is private) so that cbindgen generates it. They're not part of the public\n/// API otherwise.\n#[repr(transparent)]\npub struct LengthPercentage(LengthPercentageUnion);\n\n#[doc(hidden)]\n#[repr(C)]\npub union LengthPercentageUnion {\n    length: LengthVariant,\n    percentage: PercentageVariant,\n    calc: CalcVariant,\n    tag: TagVariant,\n}\n\nimpl LengthPercentageUnion {\n    #[doc(hidden)] // Need to be public so that cbindgen generates it.\n    pub const TAG_CALC: u8 = 0;\n    #[doc(hidden)]\n    pub const TAG_LENGTH: u8 = 1;\n    #[doc(hidden)]\n    pub const TAG_PERCENTAGE: u8 = 2;\n    #[doc(hidden)]\n    pub const TAG_MASK: u8 = 0b11;\n}\n\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[repr(u8)]\nenum Tag {\n    Calc = LengthPercentageUnion::TAG_CALC,\n    Length = LengthPercentageUnion::TAG_LENGTH,\n    Percentage = LengthPercentageUnion::TAG_PERCENTAGE,\n}\n\n// All the members should be 64 bits, even in 32-bit builds.\n#[allow(unused)]\nunsafe fn static_assert() {\n    fn assert_send_and_sync<T: Send + Sync>() {}\n    std::mem::transmute::<u64, LengthVariant>(0u64);\n    std::mem::transmute::<u64, PercentageVariant>(0u64);\n    std::mem::transmute::<u64, CalcVariant>(0u64);\n    std::mem::transmute::<u64, LengthPercentage>(0u64);\n    assert_send_and_sync::<LengthVariant>();\n    assert_send_and_sync::<PercentageVariant>();\n    assert_send_and_sync::<CalcLengthPercentage>();\n}\n\nimpl Drop for LengthPercentage {\n    fn drop(&mut self) {\n        if self.tag() == Tag::Calc {\n            let _ = unsafe { Box::from_raw(self.calc_ptr()) };\n        }\n    }\n}\n\nimpl MallocSizeOf for LengthPercentage {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        match self.unpack() {\n            Unpacked::Length(..) | Unpacked::Percentage(..) => 0,\n            Unpacked::Calc(c) => unsafe { ops.malloc_size_of(c) },\n        }\n    }\n}\n\nimpl ToAnimatedValue for LengthPercentage {\n    type AnimatedValue = Self;\n\n    fn to_animated_value(self, context: &AnimatedContext) -> Self::AnimatedValue {\n        if context.style.effective_zoom.is_one() {\n            return self;\n        }\n        self.map_lengths(|l| l.to_animated_value(context))\n    }\n\n    #[inline]\n    fn from_animated_value(value: Self::AnimatedValue) -> Self {\n        value\n    }\n}\n\nimpl ToResolvedValue for LengthPercentage {\n    type ResolvedValue = Self;\n\n    fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {\n        if context.style.effective_zoom.is_one() {\n            return self;\n        }\n        self.map_lengths(|l| l.to_resolved_value(context))\n    }\n\n    #[inline]\n    fn from_resolved_value(value: Self::ResolvedValue) -> Self {\n        value\n    }\n}\n\nimpl EqualsPercentage for LengthPercentage {\n    fn equals_percentage(&self, v: CSSFloat) -> bool {\n        match self.unpack() {\n            Unpacked::Percentage(p) => p.0 == v,\n            _ => false,\n        }\n    }\n}\n\n/// An unpacked `<length-percentage>` that borrows the `calc()` variant.\n#[derive(Clone, Debug, PartialEq, ToCss, ToTyped)]\npub enum Unpacked<'a> {\n    /// A `calc()` value\n    Calc(&'a CalcLengthPercentage),\n    /// A length value\n    Length(Length),\n    /// A percentage value\n    Percentage(Percentage),\n}\n\n/// An unpacked `<length-percentage>` that mutably borrows the `calc()` variant.\nenum UnpackedMut<'a> {\n    Calc(&'a mut CalcLengthPercentage),\n    Length(Length),\n    Percentage(Percentage),\n}\n\n/// An unpacked `<length-percentage>` that owns the `calc()` variant, for\n/// serialization purposes.\n#[derive(Deserialize, PartialEq, Serialize)]\nenum Serializable {\n    Calc(CalcLengthPercentage),\n    Length(Length),\n    Percentage(Percentage),\n}\n\nimpl LengthPercentage {\n    /// 1px length value for SVG defaults\n    #[inline]\n    pub fn one() -> Self {\n        Self::new_length(Length::new(1.))\n    }\n\n    /// 0%\n    #[inline]\n    pub fn zero_percent() -> Self {\n        Self::new_percent(Percentage::zero())\n    }\n\n    /// 100%\n    #[inline]\n    pub fn hundred_percent() -> Self {\n        Self::new_percent(Percentage::hundred())\n    }\n\n    fn to_calc_node(&self) -> CalcNode {\n        match self.unpack() {\n            Unpacked::Length(l) => CalcNode::Leaf(CalcLengthPercentageLeaf::Length(l)),\n            Unpacked::Percentage(p) => CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)),\n            Unpacked::Calc(p) => p.node.clone(),\n        }\n    }\n\n    fn map_lengths(&self, mut map_fn: impl FnMut(Length) -> Length) -> Self {\n        match self.unpack() {\n            Unpacked::Length(l) => Self::new_length(map_fn(l)),\n            Unpacked::Percentage(p) => Self::new_percent(p),\n            Unpacked::Calc(lp) => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {\n                clamping_mode: lp.clamping_mode,\n                node: lp.node.map_leaves(|leaf| match *leaf {\n                    CalcLengthPercentageLeaf::Length(ref l) => {\n                        CalcLengthPercentageLeaf::Length(map_fn(*l))\n                    },\n                    ref l => l.clone(),\n                }),\n            })),\n        }\n    }\n\n    /// Constructs a length value.\n    #[inline]\n    pub fn new_length(length: Length) -> Self {\n        let length = Self(LengthPercentageUnion {\n            length: LengthVariant {\n                tag: LengthPercentageUnion::TAG_LENGTH,\n                length,\n            },\n        });\n        debug_assert_eq!(length.tag(), Tag::Length);\n        length\n    }\n\n    /// Constructs a percentage value.\n    #[inline]\n    pub fn new_percent(percentage: Percentage) -> Self {\n        let percent = Self(LengthPercentageUnion {\n            percentage: PercentageVariant {\n                tag: LengthPercentageUnion::TAG_PERCENTAGE,\n                percentage,\n            },\n        });\n        debug_assert_eq!(percent.tag(), Tag::Percentage);\n        percent\n    }\n\n    /// Given a `LengthPercentage` value `v`, construct the value representing\n    /// `calc(100% - v)`.\n    pub fn hundred_percent_minus(v: Self, clamping_mode: AllowedNumericType) -> Self {\n        // TODO: This could in theory take ownership of the calc node in `v` if\n        // possible instead of cloning.\n        let mut node = v.to_calc_node();\n        node.negate();\n\n        let new_node = CalcNode::Sum(\n            vec![\n                CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(Percentage::hundred())),\n                node,\n            ]\n            .into(),\n        );\n\n        Self::new_calc(new_node, clamping_mode)\n    }\n\n    /// Given a list of `LengthPercentage` values, construct the value representing\n    /// `calc(100% - the sum of the list)`.\n    pub fn hundred_percent_minus_list(list: &[&Self], clamping_mode: AllowedNumericType) -> Self {\n        let mut new_list = vec![CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(\n            Percentage::hundred(),\n        ))];\n\n        for lp in list.iter() {\n            let mut node = lp.to_calc_node();\n            node.negate();\n            new_list.push(node)\n        }\n\n        Self::new_calc(CalcNode::Sum(new_list.into()), clamping_mode)\n    }\n\n    /// Constructs a `calc()` value.\n    #[inline]\n    pub fn new_calc(mut node: CalcNode, clamping_mode: AllowedNumericType) -> Self {\n        node.simplify_and_sort();\n\n        match node {\n            CalcNode::Leaf(l) => {\n                return match l {\n                    CalcLengthPercentageLeaf::Length(l) => {\n                        Self::new_length(Length::new(clamping_mode.clamp(l.px())).normalized())\n                    },\n                    CalcLengthPercentageLeaf::Percentage(p) => Self::new_percent(Percentage(\n                        clamping_mode.clamp(crate::values::normalize(p.0)),\n                    )),\n                    CalcLengthPercentageLeaf::Number(number) => {\n                        debug_assert!(\n                            false,\n                            \"The final result of a <length-percentage> should never be a number\"\n                        );\n                        Self::new_length(Length::new(number))\n                    },\n                };\n            },\n            _ => Self::new_calc_unchecked(Box::new(CalcLengthPercentage {\n                clamping_mode,\n                node,\n            })),\n        }\n    }\n\n    /// Private version of new_calc() that constructs a calc() variant without\n    /// checking.\n    fn new_calc_unchecked(calc: Box<CalcLengthPercentage>) -> Self {\n        let ptr = Box::into_raw(calc);\n\n        #[cfg(target_pointer_width = \"32\")]\n        let calc = CalcVariant {\n            tag: LengthPercentageUnion::TAG_CALC,\n            ptr: ptr as *mut (),\n        };\n\n        #[cfg(target_pointer_width = \"64\")]\n        let calc = CalcVariant {\n            #[cfg(target_endian = \"little\")]\n            ptr: ptr as usize,\n            #[cfg(target_endian = \"big\")]\n            ptr: (ptr as usize).swap_bytes(),\n        };\n\n        let calc = Self(LengthPercentageUnion { calc });\n        debug_assert_eq!(calc.tag(), Tag::Calc);\n        calc\n    }\n\n    #[inline]\n    fn tag(&self) -> Tag {\n        match unsafe { self.0.tag.tag & LengthPercentageUnion::TAG_MASK } {\n            LengthPercentageUnion::TAG_CALC => Tag::Calc,\n            LengthPercentageUnion::TAG_LENGTH => Tag::Length,\n            LengthPercentageUnion::TAG_PERCENTAGE => Tag::Percentage,\n            _ => unsafe { debug_unreachable!(\"Bogus tag?\") },\n        }\n    }\n\n    #[inline]\n    fn unpack_mut<'a>(&'a mut self) -> UnpackedMut<'a> {\n        unsafe {\n            match self.tag() {\n                Tag::Calc => UnpackedMut::Calc(&mut *self.calc_ptr()),\n                Tag::Length => UnpackedMut::Length(self.0.length.length),\n                Tag::Percentage => UnpackedMut::Percentage(self.0.percentage.percentage),\n            }\n        }\n    }\n\n    /// Unpack the tagged pointer representation of a length-percentage into an enum\n    /// representation with separate tag and value.\n    #[inline]\n    pub fn unpack<'a>(&'a self) -> Unpacked<'a> {\n        unsafe {\n            match self.tag() {\n                Tag::Calc => Unpacked::Calc(&*self.calc_ptr()),\n                Tag::Length => Unpacked::Length(self.0.length.length),\n                Tag::Percentage => Unpacked::Percentage(self.0.percentage.percentage),\n            }\n        }\n    }\n\n    #[inline]\n    unsafe fn calc_ptr(&self) -> *mut CalcLengthPercentage {\n        #[cfg(not(all(target_endian = \"big\", target_pointer_width = \"64\")))]\n        {\n            self.0.calc.ptr as *mut _\n        }\n        #[cfg(all(target_endian = \"big\", target_pointer_width = \"64\"))]\n        {\n            self.0.calc.ptr.swap_bytes() as *mut _\n        }\n    }\n\n    #[inline]\n    fn to_serializable(&self) -> Serializable {\n        match self.unpack() {\n            Unpacked::Calc(c) => Serializable::Calc(c.clone()),\n            Unpacked::Length(l) => Serializable::Length(l),\n            Unpacked::Percentage(p) => Serializable::Percentage(p),\n        }\n    }\n\n    #[inline]\n    fn from_serializable(s: Serializable) -> Self {\n        match s {\n            Serializable::Calc(c) => Self::new_calc_unchecked(Box::new(c)),\n            Serializable::Length(l) => Self::new_length(l),\n            Serializable::Percentage(p) => Self::new_percent(p),\n        }\n    }\n\n    /// Resolves the percentage.\n    #[inline]\n    pub fn resolve(&self, basis: Length) -> Length {\n        match self.unpack() {\n            Unpacked::Length(l) => l,\n            Unpacked::Percentage(p) => (basis * p.0).normalized(),\n            Unpacked::Calc(ref c) => c.resolve(basis),\n        }\n    }\n\n    /// Resolves the percentage. Just an alias of resolve().\n    #[inline]\n    pub fn percentage_relative_to(&self, basis: Length) -> Length {\n        self.resolve(basis)\n    }\n\n    /// Return whether there's any percentage in this value.\n    #[inline]\n    pub fn has_percentage(&self) -> bool {\n        match self.unpack() {\n            Unpacked::Length(..) => false,\n            Unpacked::Percentage(..) | Unpacked::Calc(..) => true,\n        }\n    }\n\n    /// Converts to a `<length>` if possible.\n    pub fn to_length(&self) -> Option<Length> {\n        match self.unpack() {\n            Unpacked::Length(l) => Some(l),\n            Unpacked::Percentage(..) | Unpacked::Calc(..) => {\n                debug_assert!(self.has_percentage());\n                return None;\n            },\n        }\n    }\n\n    /// Converts to a `<percentage>` if possible.\n    #[inline]\n    pub fn to_percentage(&self) -> Option<Percentage> {\n        match self.unpack() {\n            Unpacked::Percentage(p) => Some(p),\n            Unpacked::Length(..) | Unpacked::Calc(..) => None,\n        }\n    }\n\n    /// Converts to a `<percentage>` with given basis. Returns None if the basis is 0.\n    #[inline]\n    pub fn to_percentage_of(&self, basis: Length) -> Option<Percentage> {\n        if basis.px() == 0. {\n            return None;\n        }\n        Some(match self.unpack() {\n            Unpacked::Length(l) => Percentage(l.px() / basis.px()),\n            Unpacked::Percentage(p) => p,\n            Unpacked::Calc(ref c) => Percentage(c.resolve(basis).px() / basis.px()),\n        })\n    }\n\n    /// Returns the used value.\n    #[inline]\n    pub fn to_used_value(&self, containing_length: Au) -> Au {\n        let length = self.to_pixel_length(containing_length);\n        if let Unpacked::Percentage(_) = self.unpack() {\n            return Au::from_f32_px_trunc(length.px());\n        }\n        Au::from(length)\n    }\n\n    /// Returns the used value as CSSPixelLength.\n    #[inline]\n    pub fn to_pixel_length(&self, containing_length: Au) -> Length {\n        self.resolve(containing_length.into())\n    }\n\n    /// Convert the computed value into used value.\n    #[inline]\n    pub fn maybe_to_used_value(&self, container_len: Option<Au>) -> Option<Au> {\n        self.maybe_percentage_relative_to(container_len.map(Length::from))\n            .map(if let Unpacked::Percentage(_) = self.unpack() {\n                |length: Length| Au::from_f32_px_trunc(length.px())\n            } else {\n                Au::from\n            })\n    }\n\n    /// If there are special rules for computing percentages in a value (e.g.\n    /// the height property), they apply whenever a calc() expression contains\n    /// percentages.\n    pub fn maybe_percentage_relative_to(&self, container_len: Option<Length>) -> Option<Length> {\n        if let Unpacked::Length(l) = self.unpack() {\n            return Some(l);\n        }\n        Some(self.resolve(container_len?))\n    }\n}\n\nimpl ClampToNonNegative for LengthPercentage {\n    /// Returns the clamped non-negative values.\n    #[inline]\n    fn clamp_to_non_negative(mut self) -> Self {\n        match self.unpack_mut() {\n            UnpackedMut::Length(l) => Self::new_length(l.clamp_to_non_negative()),\n            UnpackedMut::Percentage(p) => Self::new_percent(p.clamp_to_non_negative()),\n            UnpackedMut::Calc(ref mut c) => {\n                c.clamping_mode = AllowedNumericType::NonNegative;\n                self\n            },\n        }\n    }\n}\n\nimpl PartialEq for LengthPercentage {\n    fn eq(&self, other: &Self) -> bool {\n        self.unpack() == other.unpack()\n    }\n}\n\nimpl fmt::Debug for LengthPercentage {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        self.unpack().fmt(formatter)\n    }\n}\n\nimpl ToAnimatedZero for LengthPercentage {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(match self.unpack() {\n            Unpacked::Length(l) => Self::new_length(l.to_animated_zero()?),\n            Unpacked::Percentage(p) => Self::new_percent(p.to_animated_zero()?),\n            Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.to_animated_zero()?)),\n        })\n    }\n}\n\nimpl Clone for LengthPercentage {\n    fn clone(&self) -> Self {\n        match self.unpack() {\n            Unpacked::Length(l) => Self::new_length(l),\n            Unpacked::Percentage(p) => Self::new_percent(p),\n            Unpacked::Calc(c) => Self::new_calc_unchecked(Box::new(c.clone())),\n        }\n    }\n}\n\nimpl ToComputedValue for specified::LengthPercentage {\n    type ComputedValue = LengthPercentage;\n\n    fn to_computed_value(&self, context: &Context) -> LengthPercentage {\n        match *self {\n            specified::LengthPercentage::Length(ref value) => {\n                LengthPercentage::new_length(value.to_computed_value(context))\n            },\n            specified::LengthPercentage::Percentage(value) => LengthPercentage::new_percent(value),\n            specified::LengthPercentage::Calc(ref calc) => (**calc).to_computed_value(context),\n        }\n    }\n\n    fn from_computed_value(computed: &LengthPercentage) -> Self {\n        match computed.unpack() {\n            Unpacked::Length(ref l) => {\n                specified::LengthPercentage::Length(ToComputedValue::from_computed_value(l))\n            },\n            Unpacked::Percentage(p) => specified::LengthPercentage::Percentage(p),\n            Unpacked::Calc(c) => {\n                // We simplify before constructing the LengthPercentage if\n                // needed, so this is always fine.\n                specified::LengthPercentage::Calc(Box::new(\n                    specified::CalcLengthPercentage::from_computed_value(c),\n                ))\n            },\n        }\n    }\n}\n\nimpl ComputeSquaredDistance for LengthPercentage {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        // A somewhat arbitrary base, it doesn't really make sense to mix\n        // lengths with percentages, but we can't do much better here, and this\n        // ensures that the distance between length-only and percentage-only\n        // lengths makes sense.\n        let basis = Length::new(100.);\n        self.resolve(basis)\n            .compute_squared_distance(&other.resolve(basis))\n    }\n}\n\nimpl ToCss for LengthPercentage {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.unpack().to_css(dest)\n    }\n}\n\nimpl ToTyped for LengthPercentage {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        self.unpack().to_typed(dest)\n    }\n}\n\nimpl Zero for LengthPercentage {\n    fn zero() -> Self {\n        LengthPercentage::new_length(Length::zero())\n    }\n\n    /// Returns true if the computed value is absolute 0 or 0%.\n    #[inline]\n    fn is_zero(&self) -> bool {\n        match self.unpack() {\n            Unpacked::Length(l) => l.px() == 0.0,\n            Unpacked::Percentage(p) => p.0 == 0.0,\n            Unpacked::Calc(..) => false,\n        }\n    }\n}\n\nimpl ZeroNoPercent for LengthPercentage {\n    #[inline]\n    fn is_zero_no_percent(&self) -> bool {\n        self.to_length().is_some_and(|l| l.px() == 0.0)\n    }\n}\n\nimpl Serialize for LengthPercentage {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.to_serializable().serialize(serializer)\n    }\n}\n\nimpl<'de> Deserialize<'de> for LengthPercentage {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        Ok(Self::from_serializable(Serializable::deserialize(\n            deserializer,\n        )?))\n    }\n}\n\n/// The leaves of a `<length-percentage>` calc expression.\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum CalcLengthPercentageLeaf {\n    Length(Length),\n    Percentage(Percentage),\n    Number(f32),\n}\n\nimpl CalcLengthPercentageLeaf {\n    fn is_zero_length(&self) -> bool {\n        match *self {\n            Self::Length(ref l) => l.is_zero(),\n            Self::Percentage(..) => false,\n            Self::Number(..) => false,\n        }\n    }\n}\n\nimpl calc::CalcNodeLeaf for CalcLengthPercentageLeaf {\n    fn unit(&self) -> CalcUnits {\n        match self {\n            Self::Length(_) => CalcUnits::LENGTH,\n            Self::Percentage(_) => CalcUnits::PERCENTAGE,\n            Self::Number(_) => CalcUnits::empty(),\n        }\n    }\n\n    fn unitless_value(&self) -> Option<f32> {\n        Some(match *self {\n            Self::Length(ref l) => l.px(),\n            Self::Percentage(ref p) => p.0,\n            Self::Number(n) => n,\n        })\n    }\n\n    fn new_number(value: f32) -> Self {\n        Self::Number(value)\n    }\n\n    fn as_number(&self) -> Option<f32> {\n        match *self {\n            Self::Length(_) | Self::Percentage(_) => None,\n            Self::Number(value) => Some(value),\n        }\n    }\n\n    fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<std::cmp::Ordering> {\n        use self::CalcLengthPercentageLeaf::*;\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {\n            return None;\n        }\n\n        let Ok(self_negative) = self.is_negative() else {\n            return None;\n        };\n        let Ok(other_negative) = other.is_negative() else {\n            return None;\n        };\n        if self_negative != other_negative {\n            return Some(if self_negative {\n                std::cmp::Ordering::Less\n            } else {\n                std::cmp::Ordering::Greater\n            });\n        }\n\n        match (self, other) {\n            (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),\n            (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),\n            (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),\n            _ => unsafe {\n                match *self {\n                    Length(..) | Percentage(..) | Number(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in compare()\")\n            },\n        }\n    }\n\n    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {\n        use self::CalcLengthPercentageLeaf::*;\n\n        // 0px plus anything else is equal to the right hand side.\n        if self.is_zero_length() {\n            *self = other.clone();\n            return Ok(());\n        }\n\n        if other.is_zero_length() {\n            return Ok(());\n        }\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        match (self, other) {\n            (&mut Length(ref mut one), &Length(ref other)) => {\n                *one += *other;\n            },\n            (&mut Percentage(ref mut one), &Percentage(ref other)) => {\n                one.0 += other.0;\n            },\n            (&mut Number(ref mut one), &Number(ref other)) => {\n                *one += *other;\n            },\n            _ => unsafe {\n                match *other {\n                    Length(..) | Percentage(..) | Number(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_sum_in_place()\")\n            },\n        }\n\n        Ok(())\n    }\n\n    fn try_product_in_place(&mut self, other: &mut Self) -> bool {\n        if let Self::Number(ref mut left) = *self {\n            if let Self::Number(ref right) = *other {\n                // Both sides are numbers, so we can just modify the left side.\n                *left *= *right;\n                true\n            } else {\n                // The right side is not a number, so the result should be in the units of the right\n                // side.\n                if other.map(|v| v * *left).is_ok() {\n                    std::mem::swap(self, other);\n                    true\n                } else {\n                    false\n                }\n            }\n        } else if let Self::Number(ref right) = *other {\n            // The left side is not a number, but the right side is, so the result is the left\n            // side unit.\n            self.map(|v| v * *right).is_ok()\n        } else {\n            // Neither side is a number, so a product is not possible.\n            false\n        }\n    }\n\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::CalcLengthPercentageLeaf::*;\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n        Ok(match (self, other) {\n            (&Length(ref one), &Length(ref other)) => {\n                Length(super::Length::new(op(one.px(), other.px())))\n            },\n            (&Percentage(one), &Percentage(other)) => {\n                Self::Percentage(super::Percentage(op(one.0, other.0)))\n            },\n            (&Number(one), &Number(other)) => Self::Number(op(one, other)),\n            _ => unsafe {\n                match *self {\n                    Length(..) | Percentage(..) | Number(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_op()\")\n            },\n        })\n    }\n\n    fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {\n        Ok(match self {\n            Self::Length(value) => {\n                *value = Length::new(op(value.px()));\n            },\n            Self::Percentage(value) => {\n                *value = Percentage(op(value.0));\n            },\n            Self::Number(value) => {\n                *value = op(*value);\n            },\n        })\n    }\n\n    fn simplify(&mut self) {}\n\n    fn sort_key(&self) -> calc::SortKey {\n        match *self {\n            Self::Length(..) => calc::SortKey::Px,\n            Self::Percentage(..) => calc::SortKey::Percentage,\n            Self::Number(..) => calc::SortKey::Number,\n        }\n    }\n}\n\n/// The computed version of a calc() node for `<length-percentage>` values.\npub type CalcNode = calc::GenericCalcNode<CalcLengthPercentageLeaf>;\n\n/// The representation of a calc() function with mixed lengths and percentages.\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToCss,\n    ToTyped,\n)]\n#[repr(C)]\npub struct CalcLengthPercentage {\n    #[animation(constant)]\n    #[css(skip)]\n    clamping_mode: AllowedNumericType,\n    node: CalcNode,\n}\n\n/// Type for anchor side in `calc()` and other math fucntions.\npub type CalcAnchorSide = GenericAnchorSide<Box<CalcNode>>;\n\n/// Result of resolving `CalcLengthPercentage`\npub struct CalcLengthPercentageResolution {\n    /// The resolved length.\n    pub result: Length,\n    /// Did the resolution of this calc node require resolving percentages?\n    pub percentage_used: bool,\n}\n\n/// What anchor positioning functions are allowed to resolve in calc percentage\n/// values.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub enum AllowAnchorPosResolutionInCalcPercentage {\n    /// Both `anchor()` and `anchor-size()` are valid and should be resolved.\n    Both(PhysicalSide),\n    /// Only `anchor-size()` is valid and should be resolved.\n    AnchorSizeOnly(PhysicalAxis),\n}\n\nimpl AllowAnchorPosResolutionInCalcPercentage {\n    #[cfg(feature = \"gecko\")]\n    /// Get the `anchor-size()` resolution axis.\n    pub fn to_axis(&self) -> PhysicalAxis {\n        match self {\n            Self::AnchorSizeOnly(axis) => *axis,\n            Self::Both(side) => {\n                if matches!(side, PhysicalSide::Top | PhysicalSide::Bottom) {\n                    PhysicalAxis::Vertical\n                } else {\n                    PhysicalAxis::Horizontal\n                }\n            },\n        }\n    }\n}\n\nimpl From<&CalcAnchorSide> for AnchorSide {\n    fn from(value: &CalcAnchorSide) -> Self {\n        match value {\n            CalcAnchorSide::Keyword(k) => Self::Keyword(*k),\n            CalcAnchorSide::Percentage(p) => {\n                if let CalcNode::Leaf(CalcLengthPercentageLeaf::Percentage(p)) = **p {\n                    Self::Percentage(p)\n                } else {\n                    unreachable!(\"Should have parsed simplified percentage.\");\n                }\n            },\n        }\n    }\n}\n\nimpl CalcLengthPercentage {\n    /// Resolves the percentage.\n    #[inline]\n    pub fn resolve(&self, basis: Length) -> Length {\n        // unwrap() is fine because the conversion below is infallible.\n        if let CalcLengthPercentageLeaf::Length(px) = self\n            .node\n            .resolve_map(|leaf| {\n                Ok(if let CalcLengthPercentageLeaf::Percentage(p) = leaf {\n                    CalcLengthPercentageLeaf::Length(Length::new(basis.px() * p.0))\n                } else {\n                    leaf.clone()\n                })\n            })\n            .unwrap()\n        {\n            Length::new(self.clamping_mode.clamp(px.px())).normalized()\n        } else {\n            unreachable!(\"resolve_map should turn percentages to lengths, and parsing should ensure that we don't end up with a number\");\n        }\n    }\n\n    /// Return a clone of this node with all anchor functions computed and replaced with\n    /// corresponding values, returning error if the resolution is invalid.\n    #[inline]\n    #[cfg(feature = \"gecko\")]\n    pub fn resolve_anchor(\n        &self,\n        allowed: AllowAnchorPosResolutionInCalcPercentage,\n        params: &AnchorPosOffsetResolutionParams,\n    ) -> Result<(CalcNode, AllowedNumericType), ()> {\n        use crate::values::{\n            computed::{length::resolve_anchor_size, AnchorFunction},\n            generics::{length::GenericAnchorSizeFunction, position::GenericAnchorFunction},\n        };\n\n        fn resolve_anchor_function<'a>(\n            f: &'a GenericAnchorFunction<Box<CalcNode>, Box<GenericAnchorFunctionFallback<CalcLengthPercentageLeaf>>>,\n            side: PhysicalSide,\n            params: &AnchorPosOffsetResolutionParams,\n        ) -> AnchorResolutionResult<'a, Box<CalcNode>> {\n            let anchor_side: &CalcAnchorSide = &f.side;\n            let resolved = if f.valid_for(side, params.mBaseParams.mPosition) {\n                AnchorFunction::resolve(&f.target_element, &anchor_side.into(), side, params).ok()\n            } else {\n                None\n            };\n\n            resolved.map_or_else(\n                || {\n                    let Some(fb) = f.fallback.as_ref() else {\n                        return AnchorResolutionResult::Invalid;\n                    };\n                    let mut node = Box::new(fb.node.clone());\n                    let result = node.map_node(|node| {\n                        resolve_anchor_functions(\n                            node,\n                            AllowAnchorPosResolutionInCalcPercentage::Both(side),\n                            params,\n                        )\n                    });\n                    if result.is_err() {\n                        return AnchorResolutionResult::Invalid;\n                    }\n                    AnchorResolutionResult::Resolved(node)\n                },\n                |v| {\n                    AnchorResolutionResult::Resolved(Box::new(CalcNode::Leaf(\n                        CalcLengthPercentageLeaf::Length(v),\n                    )))\n                },\n            )\n        }\n\n        fn resolve_anchor_size_function<'a>(\n            f: &'a GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<CalcLengthPercentageLeaf>>>,\n            allowed: AllowAnchorPosResolutionInCalcPercentage,\n            params: &AnchorPosOffsetResolutionParams,\n        ) -> AnchorResolutionResult<'a, Box<CalcNode>> {\n            let axis = allowed.to_axis();\n            let resolved = if f.valid_for(params.mBaseParams.mPosition) {\n                resolve_anchor_size(&f.target_element, axis, f.size, &params.mBaseParams).ok()\n            } else {\n                None\n            };\n\n            resolved.map_or_else(\n                || {\n                    let Some(fb) = f.fallback.as_ref() else {\n                        return AnchorResolutionResult::Invalid;\n                    };\n                    let mut node = Box::new(fb.node.clone());\n                    let result =\n                        node.map_node(|node| resolve_anchor_functions(node, allowed, params));\n                    if result.is_err() {\n                        return AnchorResolutionResult::Invalid;\n                    }\n                    AnchorResolutionResult::Resolved(node)\n                },\n                |v| {\n                    AnchorResolutionResult::Resolved(Box::new(CalcNode::Leaf(\n                        CalcLengthPercentageLeaf::Length(v),\n                    )))\n                },\n            )\n        }\n\n        fn resolve_anchor_functions(\n            node: &CalcNode,\n            allowed: AllowAnchorPosResolutionInCalcPercentage,\n            params: &AnchorPosOffsetResolutionParams,\n        ) -> Result<Option<CalcNode>, ()> {\n            let resolution = match node {\n                CalcNode::Anchor(f) => {\n                    let prop_side = match allowed {\n                        AllowAnchorPosResolutionInCalcPercentage::Both(side) => side,\n                        AllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(_) => {\n                            unreachable!(\"anchor() found where disallowed\")\n                        },\n                    };\n                    resolve_anchor_function(f, prop_side, params)\n                },\n                CalcNode::AnchorSize(f) => resolve_anchor_size_function(f, allowed, params),\n                _ => return Ok(None),\n            };\n\n            match resolution {\n                AnchorResolutionResult::Invalid => Err(()),\n                AnchorResolutionResult::Fallback(fb) => {\n                    // TODO(dshin, bug 1923759): At least for now, fallbacks should not contain any anchor function.\n                    Ok(Some(*fb.clone()))\n                },\n                AnchorResolutionResult::Resolved(v) => Ok(Some(*v.clone())),\n            }\n        }\n\n        let mut node = self.node.clone();\n        node.map_node(|node| resolve_anchor_functions(node, allowed, params))?;\n        Ok((node, self.clamping_mode))\n    }\n}\n\n// NOTE(emilio): We don't compare `clamping_mode` since we want to preserve the\n// invariant that `from_computed_value(length).to_computed_value(..) == length`.\n//\n// Right now for e.g. a non-negative length, we set clamping_mode to `All`\n// unconditionally for non-calc values, and to `NonNegative` for calc.\n//\n// If we determine that it's sound, from_computed_value() can generate an\n// absolute length, which then would get `All` as the clamping mode.\n//\n// We may want to just eagerly-detect whether we can clamp in\n// `LengthPercentage::new` and switch to `AllowedNumericType::NonNegative` then,\n// maybe.\nimpl PartialEq for CalcLengthPercentage {\n    fn eq(&self, other: &Self) -> bool {\n        self.node == other.node\n    }\n}\n\nimpl specified::CalcLengthPercentage {\n    /// Compute the value, zooming any absolute units by the zoom function.\n    fn to_computed_value_with_zoom<F>(\n        &self,\n        context: &Context,\n        zoom_fn: F,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> LengthPercentage\n    where\n        F: Fn(Length) -> Length,\n    {\n        use crate::values::specified::calc::Leaf;\n\n        let node = self.node.map_leaves(|leaf| match *leaf {\n            Leaf::Percentage(p) => CalcLengthPercentageLeaf::Percentage(Percentage(p)),\n            Leaf::Length(l) => CalcLengthPercentageLeaf::Length({\n                let result =\n                    l.to_computed_value_with_base_size(context, base_size, line_height_base);\n                if l.should_zoom_text() {\n                    zoom_fn(result)\n                } else {\n                    result\n                }\n            }),\n            Leaf::Number(n) => CalcLengthPercentageLeaf::Number(n),\n            Leaf::Angle(..) | Leaf::Time(..) | Leaf::Resolution(..) | Leaf::ColorComponent(..) => {\n                unreachable!(\"Shouldn't have parsed\")\n            },\n        });\n\n        LengthPercentage::new_calc(node, self.clamping_mode)\n    }\n\n    /// Compute font-size or line-height taking into account text-zoom if necessary.\n    pub fn to_computed_value_zoomed(\n        &self,\n        context: &Context,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> LengthPercentage {\n        self.to_computed_value_with_zoom(\n            context,\n            |abs| context.maybe_zoom_text(abs),\n            base_size,\n            line_height_base,\n        )\n    }\n\n    /// Compute the value into pixel length as CSSFloat without context,\n    /// so it returns Err(()) if there is any non-absolute unit.\n    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {\n        use crate::values::specified::calc::Leaf;\n        use crate::values::specified::length::NoCalcLength;\n\n        // Simplification should've turned this into an absolute length,\n        // otherwise it wouldn't have been able to.\n        match self.node {\n            calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),\n            _ => Err(()),\n        }\n    }\n\n    /// Compute the value into pixel length as CSSFloat, using the get_font_metrics function\n    /// if provided to resolve font-relative dimensions.\n    #[cfg(feature = \"gecko\")]\n    pub fn to_computed_pixel_length_with_font_metrics(\n        &self,\n        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,\n    ) -> Result<CSSFloat, ()> {\n        use crate::values::specified::calc::Leaf;\n        use crate::values::specified::length::NoCalcLength;\n\n        match self.node {\n            calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::Absolute(ref l))) => Ok(l.to_px()),\n            calc::CalcNode::Leaf(Leaf::Length(NoCalcLength::FontRelative(ref l))) => {\n                if let Some(getter) = get_font_metrics {\n                    l.to_computed_pixel_length_with_font_metrics(getter)\n                } else {\n                    Err(())\n                }\n            },\n            _ => Err(()),\n        }\n    }\n\n    /// Compute the calc using the current font-size and line-height. (and without text-zoom).\n    pub fn to_computed_value(&self, context: &Context) -> LengthPercentage {\n        self.to_computed_value_with_zoom(\n            context,\n            |abs| abs,\n            FontBaseSize::CurrentStyle,\n            LineHeightBase::CurrentStyle,\n        )\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &CalcLengthPercentage) -> Self {\n        use crate::values::specified::calc::Leaf;\n        use crate::values::specified::length::NoCalcLength;\n\n        specified::CalcLengthPercentage {\n            clamping_mode: computed.clamping_mode,\n            node: computed.node.map_leaves(|l| match l {\n                CalcLengthPercentageLeaf::Length(ref l) => {\n                    Leaf::Length(NoCalcLength::from_px(l.px()))\n                },\n                CalcLengthPercentageLeaf::Percentage(ref p) => Leaf::Percentage(p.0),\n                CalcLengthPercentageLeaf::Number(n) => Leaf::Number(*n),\n            }),\n        }\n    }\n}\n\n/// https://drafts.csswg.org/css-transitions/#animtype-lpcalc\n/// https://drafts.csswg.org/css-values-4/#combine-math\n/// https://drafts.csswg.org/css-values-4/#combine-mixed\nimpl Animate for LengthPercentage {\n    #[inline]\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        Ok(match (self.unpack(), other.unpack()) {\n            (Unpacked::Length(one), Unpacked::Length(other)) => {\n                Self::new_length(one.animate(&other, procedure)?)\n            },\n            (Unpacked::Percentage(one), Unpacked::Percentage(other)) => {\n                Self::new_percent(one.animate(&other, procedure)?)\n            },\n            _ => {\n                use calc::CalcNodeLeaf;\n\n                fn product_with(mut node: CalcNode, product: f32) -> CalcNode {\n                    let mut number = CalcNode::Leaf(CalcLengthPercentageLeaf::new_number(product));\n                    if !node.try_product_in_place(&mut number) {\n                        CalcNode::Product(vec![node, number].into())\n                    } else {\n                        node\n                    }\n                }\n\n                let (l, r) = procedure.weights();\n                let one = product_with(self.to_calc_node(), l as f32);\n                let other = product_with(other.to_calc_node(), r as f32);\n\n                Self::new_calc(\n                    CalcNode::Sum(vec![one, other].into()),\n                    AllowedNumericType::All,\n                )\n            },\n        })\n    }\n}\n\n/// A wrapper of LengthPercentage, whose value must be >= 0.\npub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;\n\nimpl NonNegativeLengthPercentage {\n    /// Returns the used value.\n    #[inline]\n    pub fn to_used_value(&self, containing_length: Au) -> Au {\n        let resolved = self.0.to_used_value(containing_length);\n        std::cmp::max(resolved, Au(0))\n    }\n\n    /// Convert the computed value into used value.\n    #[inline]\n    pub fn maybe_to_used_value(&self, containing_length: Option<Au>) -> Option<Au> {\n        let resolved = self.0.maybe_to_used_value(containing_length)?;\n        Some(std::cmp::max(resolved, Au(0)))\n    }\n}\n\nimpl TryTacticAdjustment for LengthPercentage {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        match self.unpack_mut() {\n            UnpackedMut::Calc(calc) => calc.node.try_tactic_adjustment(old_side, new_side),\n            UnpackedMut::Percentage(mut p) => {\n                p.try_tactic_adjustment(old_side, new_side);\n                *self = Self::new_percent(p);\n            },\n            UnpackedMut::Length(..) => {},\n        }\n    }\n}\n\nimpl TryTacticAdjustment for GenericAnchorFunctionFallback<CalcLengthPercentageLeaf> {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        self.node.try_tactic_adjustment(old_side, new_side)\n    }\n}\n\nimpl TryTacticAdjustment for CalcNode {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        self.visit_depth_first(|node| match node {\n            Self::Leaf(CalcLengthPercentageLeaf::Percentage(p)) => {\n                p.try_tactic_adjustment(old_side, new_side)\n            },\n            Self::Anchor(a) => a.try_tactic_adjustment(old_side, new_side),\n            Self::AnchorSize(a) => a.try_tactic_adjustment(old_side, new_side),\n            _ => {},\n        });\n    }\n}\n"
  },
  {
    "path": "style/values/computed/list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! `list` computed values.\n\npub use crate::values::specified::list::ListStyleType;\npub use crate::values::specified::list::Quotes;\n\nimpl Quotes {\n    /// Initial value for `quotes`.\n    #[inline]\n    pub fn get_initial_value() -> Quotes {\n        Quotes::Auto\n    }\n}\n"
  },
  {
    "path": "style/values/computed/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values.\n\nuse self::transform::DirectionVector;\nuse super::animated::ToAnimatedValue;\nuse super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;\nuse super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;\nuse super::generics::grid::{GenericGridLine, GenericTrackBreadth};\nuse super::generics::grid::{GenericTrackSize, TrackList as GenericTrackList};\nuse super::generics::transform::IsParallelTo;\nuse super::generics::{self, GreaterThanOrEqualToOne, NonNegative, ZeroToOne};\nuse super::specified;\nuse super::{CSSFloat, CSSInteger};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::context::QuirksMode;\nuse crate::custom_properties::ComputedCustomProperties;\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::font_metrics::{FontMetrics, FontMetricsOrientation};\n#[cfg(feature = \"gecko\")]\nuse crate::properties;\nuse crate::properties::{ComputedValues, StyleBuilder};\nuse crate::rule_cache::RuleCacheConditions;\nuse crate::rule_tree::{CascadeLevel, RuleCascadeFlags};\nuse crate::stylesheets::container_rule::{\n    ContainerInfo, ContainerSizeQuery, ContainerSizeQueryResult,\n};\nuse crate::stylist::Stylist;\nuse crate::values::generics::ClampToNonNegative;\nuse crate::values::specified::font::QueryFontMetricsFlags;\nuse crate::values::specified::length::FontBaseSize;\nuse crate::{ArcSlice, Atom, One};\nuse euclid::{default, Point2D, Rect, Size2D};\nuse servo_arc::Arc;\nuse std::cell::RefCell;\nuse std::cmp;\nuse std::f32;\nuse std::ops::{Add, Sub};\n\npub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};\npub use self::angle::Angle;\npub use self::animation::{\n    AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,\n    AnimationIterationCount, AnimationName, AnimationPlayState, AnimationRangeEnd,\n    AnimationRangeStart, AnimationTimeline, ScrollAxis, TimelineName, TransitionBehavior,\n    TransitionProperty, ViewTimelineInset, ViewTransitionClass, ViewTransitionName,\n};\npub use self::background::{BackgroundRepeat, BackgroundSize};\npub use self::basic_shape::FillRule;\npub use self::border::{\n    BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,\n    BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, LineWidth,\n};\npub use self::box_::{\n    AlignmentBaseline, Appearance, BaselineShift, BaselineSource, BreakBetween, BreakWithin, Clear,\n    Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,\n    DominantBaseline, Float, LineClamp, Overflow, OverflowAnchor, OverflowClipMargin,\n    OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,\n    ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,\n    WritingModeProperty, Zoom,\n};\npub use self::color::{\n    Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,\n};\npub use self::column::ColumnCount;\npub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};\npub use self::easing::TimingFunction;\npub use self::effects::{BoxShadow, Filter, SimpleShadow};\npub use self::flex::FlexBasis;\npub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};\npub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};\npub use self::font::{\n    FontSize, FontSizeAdjust, FontStretch, FontSynthesis, FontSynthesisStyle, LineHeight,\n};\npub use self::font::{FontVariantAlternates, FontWeight};\npub use self::font::{FontVariantEastAsian, FontVariationSettings};\npub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};\npub use self::image::{Gradient, Image, ImageRendering, LineDirection};\npub use self::length::{CSSPixelLength, NonNegativeLength};\npub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};\npub use self::length::{LengthOrAuto, LengthPercentageOrAuto, Margin, MaxSize, Size};\npub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};\npub use self::list::ListStyleType;\npub use self::list::Quotes;\npub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};\npub use self::outline::OutlineStyle;\npub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};\npub use self::percentage::{NonNegativePercentage, Percentage};\npub use self::position::AnchorFunction;\npub use self::position::AnchorName;\npub use self::position::AspectRatio;\npub use self::position::DashedIdentAndOrTryTactic;\npub use self::position::Inset;\npub use self::position::PositionAnchor;\npub use self::position::PositionTryFallbacks;\npub use self::position::PositionTryOrder;\npub use self::position::PositionVisibility;\npub use self::position::ScopedName;\npub use self::position::{\n    GridAutoFlow, GridTemplateAreas, MasonryAutoFlow, Position, PositionOrAuto, ZIndex,\n};\npub use self::position::{PositionArea, PositionAreaKeyword};\npub use self::ratio::Ratio;\npub use self::rect::NonNegativeLengthOrNumberRect;\npub use self::resolution::Resolution;\npub use self::svg::{DProperty, MozContextProperties};\npub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};\npub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};\npub use self::text::{HyphenateCharacter, HyphenateLimitChars};\npub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextIndent};\npub use self::text::{OverflowWrap, RubyPosition, TextOverflow, WordBreak, WordSpacing};\npub use self::text::{TextAlign, TextAlignLast, TextEmphasisPosition, TextEmphasisStyle};\npub use self::text::{TextAutospace, TextUnderlinePosition};\npub use self::text::{TextBoxEdge, TextBoxTrim};\npub use self::text::{\n    TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify,\n};\npub use self::time::Time;\npub use self::transform::{Rotate, Scale, Transform, TransformBox, TransformOperation};\npub use self::transform::{TransformOrigin, TransformStyle, Translate};\n#[cfg(feature = \"gecko\")]\npub use self::ui::CursorImage;\npub use self::ui::{\n    BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,\n};\npub use super::specified::TextTransform;\npub use super::specified::ViewportVariant;\npub use super::specified::{BorderStyle, TextDecorationLine};\npub use app_units::Au;\n\npub mod align;\npub mod angle;\npub mod animation;\npub mod background;\npub mod basic_shape;\npub mod border;\n#[path = \"box.rs\"]\npub mod box_;\npub mod color;\npub mod column;\npub mod counters;\npub mod easing;\npub mod effects;\npub mod flex;\npub mod font;\npub mod image;\npub mod length;\npub mod length_percentage;\npub mod list;\npub mod motion;\npub mod outline;\npub mod page;\npub mod percentage;\npub mod position;\npub mod ratio;\npub mod rect;\npub mod resolution;\npub mod svg;\npub mod table;\npub mod text;\npub mod time;\npub mod transform;\npub mod ui;\npub mod url;\n\n/// A `Context` is all the data a specified value could ever need to compute\n/// itself and be transformed to a computed value.\npub struct Context<'a> {\n    /// Values accessed through this need to be in the properties \"computed\n    /// early\": color, text-decoration, font-size, display, position, float,\n    /// border-*-style, outline-style, font-family, writing-mode...\n    pub builder: StyleBuilder<'a>,\n\n    /// A cached computed system font value, for use by gecko.\n    ///\n    /// See properties/longhands/font.mako.rs\n    #[cfg(feature = \"gecko\")]\n    pub cached_system_font: Option<properties::gecko::system_font::ComputedSystemFont>,\n\n    /// A dummy option for servo so initializing a computed::Context isn't\n    /// painful.\n    ///\n    /// TODO(emilio): Make constructors for Context, and drop this.\n    #[cfg(feature = \"servo\")]\n    pub cached_system_font: Option<()>,\n\n    /// Whether or not we are computing the media list in a media query.\n    pub in_media_query: bool,\n\n    /// Whether or not we are computing the container query condition.\n    pub in_container_query: bool,\n\n    /// The quirks mode of this context.\n    pub quirks_mode: QuirksMode,\n\n    /// Whether this computation is being done for animation.\n    ///\n    /// Allows opacity to interpolate out-of-range values\n    pub for_animation: bool,\n\n    /// Returns the container information to evaluate a given container query.\n    pub container_info: Option<ContainerInfo>,\n\n    /// Whether we're computing a value for a non-inherited property.\n    /// False if we are computed a value for an inherited property or not computing for a property\n    /// at all (e.g. in a media query evaluation).\n    pub for_non_inherited_property: bool,\n\n    /// The conditions to cache a rule node on the rule cache.\n    ///\n    /// FIXME(emilio): Drop the refcell.\n    pub rule_cache_conditions: RefCell<&'a mut RuleCacheConditions>,\n\n    /// The cascade level in the shadow tree hierarchy.\n    pub scope: CascadeLevel,\n\n    /// The set of RuleCascadeFlags whose rules should be included during the\n    /// cascade. STARTING_STYLE is set from the caller for re-cascade.\n    /// APPEARANCE_BASE is added dynamically after the appearance property is\n    /// resolved to a non-None value.\n    pub included_cascade_flags: RuleCascadeFlags,\n\n    /// Container size query for this context.\n    container_size_query: RefCell<ContainerSizeQuery<'a>>,\n}\n\nimpl<'a> Context<'a> {\n    /// Lazily evaluate the container size query, returning the result.\n    pub fn get_container_size_query(&self) -> ContainerSizeQueryResult {\n        let mut resolved = self.container_size_query.borrow_mut();\n        resolved.get().clone()\n    }\n\n    /// Creates a suitable context for media query evaluation, in which\n    /// font-relative units compute against the system_font, and executes `f`\n    /// with it.\n    pub fn for_media_query_evaluation<F, R>(device: &Device, quirks_mode: QuirksMode, f: F) -> R\n    where\n        F: FnOnce(&Context) -> R,\n    {\n        let mut conditions = RuleCacheConditions::default();\n        let context = Context {\n            builder: StyleBuilder::for_inheritance(device, None, None, None),\n            cached_system_font: None,\n            in_media_query: true,\n            in_container_query: false,\n            quirks_mode,\n            for_animation: false,\n            container_info: None,\n            for_non_inherited_property: false,\n            rule_cache_conditions: RefCell::new(&mut conditions),\n            scope: CascadeLevel::same_tree_author_normal(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n            container_size_query: RefCell::new(ContainerSizeQuery::none()),\n        };\n        f(&context)\n    }\n\n    /// Creates a suitable context for container query evaluation for the style\n    /// specified.\n    pub fn for_container_query_evaluation<F, R>(\n        device: &Device,\n        stylist: Option<&Stylist>,\n        container_info_and_style: Option<(ContainerInfo, Arc<ComputedValues>)>,\n        container_size_query: ContainerSizeQuery,\n        f: F,\n    ) -> R\n    where\n        F: FnOnce(&Context) -> R,\n    {\n        let mut conditions = RuleCacheConditions::default();\n\n        let (container_info, style) = match container_info_and_style {\n            Some((ci, s)) => (Some(ci), Some(s)),\n            None => (None, None),\n        };\n\n        let style = style.as_ref().map(|s| &**s);\n        let quirks_mode = device.quirks_mode();\n        let context = Context {\n            builder: StyleBuilder::for_inheritance(device, stylist, style, None),\n            cached_system_font: None,\n            in_media_query: false,\n            in_container_query: true,\n            quirks_mode,\n            for_animation: false,\n            container_info,\n            for_non_inherited_property: false,\n            rule_cache_conditions: RefCell::new(&mut conditions),\n            scope: CascadeLevel::same_tree_author_normal(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n            container_size_query: RefCell::new(container_size_query),\n        };\n\n        f(&context)\n    }\n\n    /// Creates a context suitable for more general cases.\n    pub fn new(\n        builder: StyleBuilder<'a>,\n        quirks_mode: QuirksMode,\n        rule_cache_conditions: &'a mut RuleCacheConditions,\n        container_size_query: ContainerSizeQuery<'a>,\n        mut included_cascade_flags: RuleCascadeFlags,\n    ) -> Self {\n        if builder\n            .flags()\n            .intersects(ComputedValueFlags::IS_IN_APPEARANCE_BASE_SUBTREE)\n        {\n            included_cascade_flags.insert(RuleCascadeFlags::APPEARANCE_BASE);\n        }\n        Self {\n            builder,\n            cached_system_font: None,\n            in_media_query: false,\n            in_container_query: false,\n            quirks_mode,\n            container_info: None,\n            for_animation: false,\n            for_non_inherited_property: false,\n            rule_cache_conditions: RefCell::new(rule_cache_conditions),\n            scope: CascadeLevel::same_tree_author_normal(),\n            included_cascade_flags,\n            container_size_query: RefCell::new(container_size_query),\n        }\n    }\n\n    /// Creates a context suitable for computing animations.\n    pub fn new_for_animation(\n        builder: StyleBuilder<'a>,\n        quirks_mode: QuirksMode,\n        rule_cache_conditions: &'a mut RuleCacheConditions,\n        container_size_query: ContainerSizeQuery<'a>,\n    ) -> Self {\n        Self {\n            builder,\n            cached_system_font: None,\n            in_media_query: false,\n            in_container_query: false,\n            quirks_mode,\n            container_info: None,\n            for_animation: true,\n            for_non_inherited_property: false,\n            rule_cache_conditions: RefCell::new(rule_cache_conditions),\n            scope: CascadeLevel::same_tree_author_normal(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n            container_size_query: RefCell::new(container_size_query),\n        }\n    }\n\n    /// Creates a context suitable for computing the initial value of @property.\n    pub fn new_for_initial_at_property_value(\n        stylist: &'a Stylist,\n        rule_cache_conditions: &'a mut RuleCacheConditions,\n    ) -> Self {\n        Self {\n            builder: StyleBuilder::new(stylist.device(), Some(stylist), None, None, None, false),\n            cached_system_font: None,\n            // Because font-relative values are disallowed in @property initial values, we do not\n            // need to keep track of whether we're in a media query, whether we're in a container\n            // query, and so on.\n            in_media_query: false,\n            in_container_query: false,\n            quirks_mode: stylist.quirks_mode(),\n            container_info: None,\n            for_animation: false,\n            for_non_inherited_property: false,\n            rule_cache_conditions: RefCell::new(rule_cache_conditions),\n            scope: CascadeLevel::same_tree_author_normal(),\n            included_cascade_flags: RuleCascadeFlags::empty(),\n            container_size_query: RefCell::new(ContainerSizeQuery::none()),\n        }\n    }\n\n    /// The current device.\n    pub fn device(&self) -> &Device {\n        self.builder.device\n    }\n\n    /// Get the inherited custom properties map.\n    pub fn inherited_custom_properties(&self) -> &ComputedCustomProperties {\n        &self.builder.inherited_custom_properties()\n    }\n\n    /// Whether the style is for the root element.\n    pub fn is_root_element(&self) -> bool {\n        self.builder.is_root_element\n    }\n\n    /// Queries font metrics.\n    pub fn query_font_metrics(\n        &self,\n        base_size: FontBaseSize,\n        orientation: FontMetricsOrientation,\n        mut flags: QueryFontMetricsFlags,\n    ) -> FontMetrics {\n        if self.for_non_inherited_property {\n            self.rule_cache_conditions.borrow_mut().set_uncacheable();\n        }\n        self.builder.add_flags(match base_size {\n            FontBaseSize::CurrentStyle => ComputedValueFlags::DEPENDS_ON_SELF_FONT_METRICS,\n            FontBaseSize::InheritedStyle => ComputedValueFlags::DEPENDS_ON_INHERITED_FONT_METRICS,\n        });\n        let size = base_size.resolve(self).used_size();\n        let style = self.style();\n\n        let (wm, font) = match base_size {\n            FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()),\n            // This is only used for font-size computation.\n            FontBaseSize::InheritedStyle => {\n                (*style.inherited_writing_mode(), style.get_parent_font())\n            },\n        };\n\n        let vertical = match orientation {\n            FontMetricsOrientation::MatchContextPreferHorizontal => {\n                wm.is_vertical() && wm.is_upright()\n            },\n            FontMetricsOrientation::MatchContextPreferVertical => wm.is_text_vertical(),\n            FontMetricsOrientation::Horizontal => false,\n        };\n        if !self.in_media_query {\n            flags |= QueryFontMetricsFlags::USE_USER_FONT_SET\n        }\n        self.device()\n            .query_font_metrics(vertical, font, size, flags, /* track_changes = */ true)\n    }\n\n    /// The current viewport size, used to resolve viewport units.\n    pub fn viewport_size_for_viewport_unit_resolution(\n        &self,\n        variant: ViewportVariant,\n    ) -> default::Size2D<Au> {\n        self.builder\n            .add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS);\n        self.builder\n            .device\n            .au_viewport_size_for_viewport_unit_resolution(variant)\n    }\n\n    /// Whether we're in a media or container query.\n    pub fn in_media_or_container_query(&self) -> bool {\n        self.in_media_query || self.in_container_query\n    }\n\n    /// The default computed style we're getting our reset style from.\n    pub fn default_style(&self) -> &ComputedValues {\n        self.builder.default_style()\n    }\n\n    /// The current style.\n    pub fn style(&self) -> &StyleBuilder<'a> {\n        &self.builder\n    }\n\n    /// The current tree scope.\n    pub fn current_scope(&self) -> CascadeLevel {\n        self.scope\n    }\n\n    /// Apply text-zoom if enabled.\n    #[cfg(feature = \"gecko\")]\n    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {\n        if self\n            .style()\n            .get_font()\n            .clone__x_text_scale()\n            .text_zoom_enabled()\n        {\n            self.device().zoom_text(size)\n        } else {\n            size\n        }\n    }\n\n    /// (Servo doesn't do text-zoom)\n    #[cfg(feature = \"servo\")]\n    pub fn maybe_zoom_text(&self, size: CSSPixelLength) -> CSSPixelLength {\n        size\n    }\n}\n\n/// An iterator over a slice of computed values\n#[derive(Clone)]\npub struct ComputedVecIter<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> {\n    cx: &'cx Context<'cx_a>,\n    values: &'a [S],\n}\n\nimpl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_a, S> {\n    /// Construct an iterator from a slice of specified values and a context\n    pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self {\n        ComputedVecIter { cx, values }\n    }\n}\n\nimpl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ExactSizeIterator\n    for ComputedVecIter<'a, 'cx, 'cx_a, S>\n{\n    fn len(&self) -> usize {\n        self.values.len()\n    }\n}\n\nimpl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> Iterator for ComputedVecIter<'a, 'cx, 'cx_a, S> {\n    type Item = S::ComputedValue;\n    fn next(&mut self) -> Option<Self::Item> {\n        if let Some((next, rest)) = self.values.split_first() {\n            let ret = next.to_computed_value(self.cx);\n            self.values = rest;\n            Some(ret)\n        } else {\n            None\n        }\n    }\n\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        (self.values.len(), Some(self.values.len()))\n    }\n}\n\n/// A trait to represent the conversion between computed and specified values.\n///\n/// This trait is derivable with `#[derive(ToComputedValue)]`. The derived\n/// implementation just calls `ToComputedValue::to_computed_value` on each field\n/// of the passed value. The deriving code assumes that if the type isn't\n/// generic, then the trait can be implemented as simple `Clone::clone` calls,\n/// this means that a manual implementation with `ComputedValue = Self` is bogus\n/// if it returns anything else than a clone.\npub trait ToComputedValue {\n    /// The computed value type we're going to be converted to.\n    type ComputedValue;\n\n    /// Convert a specified value to a computed value, using itself and the data\n    /// inside the `Context`.\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue;\n\n    /// Convert a computed value to specified value form.\n    ///\n    /// This will be used for recascading during animation.\n    /// Such from_computed_valued values should recompute to the same value.\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self;\n}\n\nimpl<A, B> ToComputedValue for (A, B)\nwhere\n    A: ToComputedValue,\n    B: ToComputedValue,\n{\n    type ComputedValue = (\n        <A as ToComputedValue>::ComputedValue,\n        <B as ToComputedValue>::ComputedValue,\n    );\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        (\n            self.0.to_computed_value(context),\n            self.1.to_computed_value(context),\n        )\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        (\n            A::from_computed_value(&computed.0),\n            B::from_computed_value(&computed.1),\n        )\n    }\n}\n\nimpl<T> ToComputedValue for Option<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = Option<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.as_ref().map(|item| item.to_computed_value(context))\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.as_ref().map(T::from_computed_value)\n    }\n}\n\nimpl<T> ToComputedValue for default::Size2D<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = default::Size2D<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        Size2D::new(\n            self.width.to_computed_value(context),\n            self.height.to_computed_value(context),\n        )\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Size2D::new(\n            T::from_computed_value(&computed.width),\n            T::from_computed_value(&computed.height),\n        )\n    }\n}\n\nimpl<T> ToComputedValue for Vec<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = Vec<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.iter()\n            .map(|item| item.to_computed_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.iter().map(T::from_computed_value).collect()\n    }\n}\n\nimpl<T> ToComputedValue for Box<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = Box<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        Box::new(T::to_computed_value(self, context))\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Box::new(T::from_computed_value(computed))\n    }\n}\n\nimpl<T> ToComputedValue for Box<[T]>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = Box<[<T as ToComputedValue>::ComputedValue]>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.iter()\n            .map(|item| item.to_computed_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.iter().map(T::from_computed_value).collect()\n    }\n}\n\nimpl<T> ToComputedValue for crate::OwnedSlice<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = crate::OwnedSlice<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.iter()\n            .map(|item| item.to_computed_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.iter().map(T::from_computed_value).collect()\n    }\n}\n\nimpl<T> ToComputedValue for thin_vec::ThinVec<T>\nwhere\n    T: ToComputedValue,\n{\n    type ComputedValue = thin_vec::ThinVec<<T as ToComputedValue>::ComputedValue>;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        self.iter()\n            .map(|item| item.to_computed_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        computed.iter().map(T::from_computed_value).collect()\n    }\n}\n\n// NOTE(emilio): This is implementable more generically, but it's unlikely\n// what you want there, as it forces you to have an extra allocation.\n//\n// We could do that if needed, ideally with specialization for the case where\n// ComputedValue = T. But we don't need it for now.\nimpl<T> ToComputedValue for Arc<T>\nwhere\n    T: ToComputedValue<ComputedValue = T>,\n{\n    type ComputedValue = Self;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self {\n        self.clone()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self) -> Self {\n        computed.clone()\n    }\n}\n\n// Same caveat as above applies.\nimpl<T> ToComputedValue for ArcSlice<T>\nwhere\n    T: ToComputedValue<ComputedValue = T>,\n{\n    type ComputedValue = Self;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self {\n        self.clone()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self) -> Self {\n        computed.clone()\n    }\n}\n\ntrivial_to_computed_value!(());\ntrivial_to_computed_value!(bool);\ntrivial_to_computed_value!(f32);\ntrivial_to_computed_value!(i32);\ntrivial_to_computed_value!(u8);\ntrivial_to_computed_value!(i8);\ntrivial_to_computed_value!(u16);\ntrivial_to_computed_value!(u32);\ntrivial_to_computed_value!(usize);\ntrivial_to_computed_value!(Atom);\ntrivial_to_computed_value!(crate::values::AtomIdent);\n#[cfg(feature = \"servo\")]\ntrivial_to_computed_value!(crate::Namespace);\n#[cfg(feature = \"servo\")]\ntrivial_to_computed_value!(crate::Prefix);\ntrivial_to_computed_value!(crate::stylesheets::UrlExtraData);\ntrivial_to_computed_value!(String);\ntrivial_to_computed_value!(Box<str>);\ntrivial_to_computed_value!(crate::OwnedStr);\ntrivial_to_computed_value!(style_traits::values::specified::AllowedNumericType);\ntrivial_to_computed_value!(crate::values::generics::color::ColorMixFlags);\n\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n)]\n#[repr(C, u8)]\npub enum AngleOrPercentage {\n    Percentage(Percentage),\n    Angle(Angle),\n}\n\nimpl ToComputedValue for specified::AngleOrPercentage {\n    type ComputedValue = AngleOrPercentage;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> AngleOrPercentage {\n        match *self {\n            specified::AngleOrPercentage::Percentage(percentage) => {\n                AngleOrPercentage::Percentage(percentage.to_computed_value(context))\n            },\n            specified::AngleOrPercentage::Angle(angle) => {\n                AngleOrPercentage::Angle(angle.to_computed_value(context))\n            },\n        }\n    }\n    #[inline]\n    fn from_computed_value(computed: &AngleOrPercentage) -> Self {\n        match *computed {\n            AngleOrPercentage::Percentage(percentage) => specified::AngleOrPercentage::Percentage(\n                ToComputedValue::from_computed_value(&percentage),\n            ),\n            AngleOrPercentage::Angle(angle) => {\n                specified::AngleOrPercentage::Angle(ToComputedValue::from_computed_value(&angle))\n            },\n        }\n    }\n}\n\n/// A `<number>` value.\npub type Number = CSSFloat;\n\nimpl IsParallelTo for (Number, Number, Number) {\n    fn is_parallel_to(&self, vector: &DirectionVector) -> bool {\n        use euclid::approxeq::ApproxEq;\n        // If a and b is parallel, the angle between them is 0deg, so\n        // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.\n        let self_vector = DirectionVector::new(self.0, self.1, self.2);\n        self_vector\n            .cross(*vector)\n            .square_length()\n            .approx_eq(&0.0f32)\n    }\n}\n\n/// A wrapper of Number, but the value >= 0.\npub type NonNegativeNumber = NonNegative<CSSFloat>;\n\nimpl From<CSSFloat> for NonNegativeNumber {\n    #[inline]\n    fn from(number: CSSFloat) -> NonNegativeNumber {\n        NonNegative::<CSSFloat>(number)\n    }\n}\n\nimpl From<NonNegativeNumber> for CSSFloat {\n    #[inline]\n    fn from(number: NonNegativeNumber) -> CSSFloat {\n        number.0\n    }\n}\n\nimpl One for NonNegativeNumber {\n    #[inline]\n    fn one() -> Self {\n        NonNegative(1.0)\n    }\n\n    #[inline]\n    fn is_one(&self) -> bool {\n        self.0 == 1.0\n    }\n}\n\n/// A wrapper of Number, but the value between 0 and 1\npub type ZeroToOneNumber = ZeroToOne<CSSFloat>;\n\nimpl ToAnimatedValue for ZeroToOneNumber {\n    type AnimatedValue = Self;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        Self(animated.0.max(0.).min(1.))\n    }\n}\n\nimpl From<CSSFloat> for ZeroToOneNumber {\n    #[inline]\n    fn from(number: CSSFloat) -> Self {\n        Self(number)\n    }\n}\n\n/// A wrapper of Number, but the value >= 1.\npub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<CSSFloat>;\n\nimpl ToAnimatedValue for GreaterThanOrEqualToOneNumber {\n    type AnimatedValue = CSSFloat;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.0\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        animated.max(1.).into()\n    }\n}\n\nimpl From<CSSFloat> for GreaterThanOrEqualToOneNumber {\n    #[inline]\n    fn from(number: CSSFloat) -> GreaterThanOrEqualToOneNumber {\n        GreaterThanOrEqualToOne::<CSSFloat>(number)\n    }\n}\n\nimpl From<GreaterThanOrEqualToOneNumber> for CSSFloat {\n    #[inline]\n    fn from(number: GreaterThanOrEqualToOneNumber) -> CSSFloat {\n        number.0\n    }\n}\n\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedZero,\n    ToAnimatedValue,\n    ToCss,\n    ToResolvedValue,\n)]\n#[repr(C, u8)]\npub enum NumberOrPercentage {\n    Percentage(Percentage),\n    Number(Number),\n}\n\nimpl ClampToNonNegative for NumberOrPercentage {\n    fn clamp_to_non_negative(self) -> Self {\n        match self {\n            NumberOrPercentage::Percentage(p) => {\n                NumberOrPercentage::Percentage(p.clamp_to_non_negative())\n            },\n            NumberOrPercentage::Number(n) => NumberOrPercentage::Number(n.clamp_to_non_negative()),\n        }\n    }\n}\n\nimpl ToComputedValue for specified::NumberOrPercentage {\n    type ComputedValue = NumberOrPercentage;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> NumberOrPercentage {\n        match *self {\n            specified::NumberOrPercentage::Percentage(percentage) => {\n                NumberOrPercentage::Percentage(percentage.to_computed_value(context))\n            },\n            specified::NumberOrPercentage::Number(number) => {\n                NumberOrPercentage::Number(number.to_computed_value(context))\n            },\n        }\n    }\n    #[inline]\n    fn from_computed_value(computed: &NumberOrPercentage) -> Self {\n        match *computed {\n            NumberOrPercentage::Percentage(percentage) => {\n                specified::NumberOrPercentage::Percentage(ToComputedValue::from_computed_value(\n                    &percentage,\n                ))\n            },\n            NumberOrPercentage::Number(number) => {\n                specified::NumberOrPercentage::Number(ToComputedValue::from_computed_value(&number))\n            },\n        }\n    }\n}\n\n/// A non-negative <number-percentage>.\npub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;\n\nimpl NonNegativeNumberOrPercentage {\n    /// Returns the `100%` value.\n    #[inline]\n    pub fn hundred_percent() -> Self {\n        NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))\n    }\n}\n\n/// A type used for opacity.\npub type Opacity = CSSFloat;\n\n/// A `<integer>` value.\npub type Integer = CSSInteger;\n\n/// A wrapper of Integer, but only accept a value >= 1.\npub type PositiveInteger = GreaterThanOrEqualToOne<CSSInteger>;\n\nimpl ToAnimatedValue for PositiveInteger {\n    type AnimatedValue = CSSInteger;\n\n    #[inline]\n    fn to_animated_value(self, _: &crate::values::animated::Context) -> Self::AnimatedValue {\n        self.0\n    }\n\n    #[inline]\n    fn from_animated_value(animated: Self::AnimatedValue) -> Self {\n        cmp::max(animated, 1).into()\n    }\n}\n\nimpl From<CSSInteger> for PositiveInteger {\n    #[inline]\n    fn from(int: CSSInteger) -> PositiveInteger {\n        GreaterThanOrEqualToOne::<CSSInteger>(int)\n    }\n}\n\n/// rect(...) | auto\npub type ClipRect = generics::GenericClipRect<LengthOrAuto>;\n\n/// rect(...) | auto\npub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;\n\n/// The computed value of a grid `<track-breadth>`\npub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;\n\n/// The computed value of a grid `<track-size>`\npub type TrackSize = GenericTrackSize<LengthPercentage>;\n\n/// The computed value of a grid `<track-size>+`\npub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;\n\n/// The computed value of a grid `<track-list>`\n/// (could also be `<auto-track-list>` or `<explicit-track-list>`)\npub type TrackList = GenericTrackList<LengthPercentage, Integer>;\n\n/// The computed value of a `<grid-line>`.\npub type GridLine = GenericGridLine<Integer>;\n\n/// `<grid-template-rows> | <grid-template-columns>`\npub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;\n\nimpl ClipRect {\n    /// Given a border box, resolves the clip rect against the border box\n    /// in the same space the border box is in\n    pub fn for_border_rect<T: Copy + From<Length> + Add<Output = T> + Sub<Output = T>, U>(\n        &self,\n        border_box: Rect<T, U>,\n    ) -> Rect<T, U> {\n        fn extract_clip_component<T: From<Length>>(p: &LengthOrAuto, or: T) -> T {\n            match *p {\n                LengthOrAuto::Auto => or,\n                LengthOrAuto::LengthPercentage(ref length) => T::from(*length),\n            }\n        }\n\n        let clip_origin = Point2D::new(\n            From::from(self.left.auto_is(|| Length::new(0.))),\n            From::from(self.top.auto_is(|| Length::new(0.))),\n        );\n        let right = extract_clip_component(&self.right, border_box.size.width);\n        let bottom = extract_clip_component(&self.bottom, border_box.size.height);\n        let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);\n\n        Rect::new(clip_origin, clip_size).translate(border_box.origin.to_vector())\n    }\n}\n"
  },
  {
    "path": "style/values/computed/motion.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values that are related to motion path.\n\nuse crate::derives::*;\nuse crate::values::computed::basic_shape::BasicShape;\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{Angle, LengthPercentage, Position};\nuse crate::values::generics::motion::{\n    GenericOffsetPath, GenericOffsetPathFunction, GenericOffsetPosition, GenericRayFunction,\n};\nuse crate::Zero;\n\n/// The computed value of ray() function.\npub type RayFunction = GenericRayFunction<Angle, Position>;\n\n/// The computed value of <offset-path>.\npub type OffsetPathFunction = GenericOffsetPathFunction<BasicShape, RayFunction, ComputedUrl>;\n\n/// The computed value of `offset-path`.\npub type OffsetPath = GenericOffsetPath<OffsetPathFunction>;\n\n/// The computed value of `offset-position`.\npub type OffsetPosition = GenericOffsetPosition<LengthPercentage, LengthPercentage>;\n\n#[inline]\nfn is_auto_zero_angle(auto: &bool, angle: &Angle) -> bool {\n    *auto && angle.is_zero()\n}\n\n/// A computed offset-rotate.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct OffsetRotate {\n    /// If auto is false, this is a fixed angle which indicates a\n    /// constant clockwise rotation transformation applied to it by this\n    /// specified rotation angle. Otherwise, the angle will be added to\n    /// the angle of the direction in layout.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub auto: bool,\n    /// The angle value.\n    #[css(contextual_skip_if = \"is_auto_zero_angle\")]\n    pub angle: Angle,\n}\n\nimpl OffsetRotate {\n    /// Returns \"auto 0deg\".\n    #[inline]\n    pub fn auto() -> Self {\n        OffsetRotate {\n            auto: true,\n            angle: Zero::zero(),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/computed/outline.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values for outline properties\n\npub use crate::values::specified::OutlineStyle;\n"
  },
  {
    "path": "style/values/computed/page.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed @page at-rule properties and named-page style properties\n\nuse crate::derives::*;\nuse crate::values::computed::length::NonNegativeLength;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics;\nuse crate::values::generics::size::Size2D;\n\nuse crate::values::specified::page as specified;\npub use generics::page::GenericPageSize;\npub use generics::page::PageOrientation;\npub use generics::page::PageSizeOrientation;\npub use generics::page::PaperSize;\npub use specified::PageName;\n\n/// Computed value of the @page size descriptor\n///\n/// The spec says that the computed value should be the same as the specified\n/// value but with all absolute units, but it's not currently possibly observe\n/// the computed value of page-size.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem, ToTyped)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum PageSize {\n    /// Specified size, paper size, or paper size and orientation.\n    Size(Size2D<NonNegativeLength>),\n    /// `landscape` or `portrait` value, no specified size.\n    Orientation(PageSizeOrientation),\n    /// `auto` value\n    Auto,\n}\n\nimpl ToComputedValue for specified::PageSize {\n    type ComputedValue = PageSize;\n\n    fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {\n        match &*self {\n            Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),\n            Self::PaperSize(p, PageSizeOrientation::Landscape) => PageSize::Size(Size2D {\n                width: p.long_edge().to_computed_value(ctx),\n                height: p.short_edge().to_computed_value(ctx),\n            }),\n            Self::PaperSize(p, PageSizeOrientation::Portrait) => PageSize::Size(Size2D {\n                width: p.short_edge().to_computed_value(ctx),\n                height: p.long_edge().to_computed_value(ctx),\n            }),\n            Self::Orientation(o) => PageSize::Orientation(*o),\n            Self::Auto => PageSize::Auto,\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match *computed {\n            PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)),\n            PageSize::Orientation(o) => Self::Orientation(o),\n            PageSize::Auto => Self::Auto,\n        }\n    }\n}\n\nimpl PageSize {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        PageSize::Auto\n    }\n\n    /// Whether this is the `auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, PageSize::Auto)\n    }\n}\n"
  },
  {
    "path": "style/values/computed/percentage.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed percentages.\n\nuse crate::derives::*;\nuse crate::values::generics::{ClampToNonNegative, NonNegative};\nuse crate::values::specified::percentage::ToPercentage;\nuse crate::values::{reify_percentage, serialize_normalized_percentage, CSSFloat};\nuse crate::Zero;\nuse std::fmt;\nuse style_traits::{CssWriter, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\n/// A computed percentage.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Default,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct Percentage(pub CSSFloat);\n\nimpl ClampToNonNegative for Percentage {\n    #[inline]\n    fn clamp_to_non_negative(self) -> Self {\n        Percentage(self.0.max(0.))\n    }\n}\n\nimpl Percentage {\n    /// 100%\n    #[inline]\n    pub fn hundred() -> Self {\n        Percentage(1.)\n    }\n\n    /// Returns the absolute value for this percentage.\n    #[inline]\n    pub fn abs(&self) -> Self {\n        Percentage(self.0.abs())\n    }\n}\n\nimpl Zero for Percentage {\n    fn zero() -> Self {\n        Percentage(0.)\n    }\n\n    fn is_zero(&self) -> bool {\n        self.0 == 0.\n    }\n}\n\nimpl ToPercentage for Percentage {\n    fn to_percentage(&self) -> CSSFloat {\n        self.0\n    }\n}\n\nimpl std::ops::AddAssign for Percentage {\n    fn add_assign(&mut self, other: Self) {\n        self.0 += other.0\n    }\n}\n\nimpl std::ops::Add for Percentage {\n    type Output = Self;\n\n    fn add(self, other: Self) -> Self {\n        Percentage(self.0 + other.0)\n    }\n}\n\nimpl std::ops::Sub for Percentage {\n    type Output = Self;\n\n    fn sub(self, other: Self) -> Self {\n        Percentage(self.0 - other.0)\n    }\n}\n\nimpl std::ops::Rem for Percentage {\n    type Output = Self;\n\n    fn rem(self, other: Self) -> Self {\n        Percentage(self.0 % other.0)\n    }\n}\n\nimpl ToCss for Percentage {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        serialize_normalized_percentage(self.0, dest)\n    }\n}\n\nimpl ToTyped for Percentage {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        reify_percentage(self.0, /* was_calc = */ false, dest)\n    }\n}\n\n/// A wrapper over a `Percentage`, whose value should be clamped to 0.\npub type NonNegativePercentage = NonNegative<Percentage>;\n\nimpl NonNegativePercentage {\n    /// 100%\n    #[inline]\n    pub fn hundred() -> Self {\n        NonNegative(Percentage::hundred())\n    }\n}\n"
  },
  {
    "path": "style/values/computed/position.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the computed value of\n//! [`position`][position] values.\n//!\n//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position\n\nuse crate::logical_geometry::PhysicalSide;\nuse crate::values::computed::{\n    Context, Integer, LengthPercentage, NonNegativeNumber, Percentage, ToComputedValue,\n};\nuse crate::values::generics;\n#[cfg(feature = \"gecko\")]\nuse crate::values::generics::position::TreeScoped;\nuse crate::values::generics::position::{\n    AnchorSideKeyword, AspectRatio as GenericAspectRatio, GenericAnchorFunction, GenericAnchorSide,\n    GenericInset, Position as GenericPosition, PositionComponent as GenericPositionComponent,\n    PositionOrAuto as GenericPositionOrAuto, ZIndex as GenericZIndex,\n};\npub use crate::values::specified::position::{\n    AnchorName, DashedIdentAndOrTryTactic, GridAutoFlow, GridTemplateAreas, MasonryAutoFlow,\n    PositionAnchor, PositionArea, PositionAreaAxis, PositionAreaKeyword, PositionAreaType,\n    PositionTryFallbacks, PositionTryFallbacksTryTactic, PositionTryFallbacksTryTacticKeyword,\n    PositionTryOrder, PositionVisibility, ScopedName,\n};\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// The computed value of a CSS `<position>`\npub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;\n\n/// The computed value of an `auto | <position>`\npub type PositionOrAuto = GenericPositionOrAuto<Position>;\n\n/// The computed value of a CSS horizontal position.\npub type HorizontalPosition = LengthPercentage;\n\n/// The computed value of a CSS vertical position.\npub type VerticalPosition = LengthPercentage;\n\n/// The computed value of anchor side.\npub type AnchorSide = GenericAnchorSide<Percentage>;\n\nimpl AnchorSide {\n    /// Break down given anchor side into its equivalent keyword and percentage.\n    pub fn keyword_and_percentage(&self) -> (AnchorSideKeyword, Percentage) {\n        match self {\n            Self::Percentage(p) => (AnchorSideKeyword::Start, *p),\n            Self::Keyword(k) => {\n                if matches!(k, AnchorSideKeyword::Center) {\n                    (AnchorSideKeyword::Start, Percentage(0.5))\n                } else {\n                    (*k, Percentage::zero())\n                }\n            },\n        }\n    }\n}\n\n/// The computed value of an `anchor()` function.\npub type AnchorFunction = GenericAnchorFunction<Percentage, Inset>;\n\n#[cfg(feature = \"gecko\")]\nuse crate::{\n    gecko_bindings::structs::AnchorPosOffsetResolutionParams,\n    values::{computed::Length, DashedIdent},\n};\n\nimpl AnchorFunction {\n    /// Resolve the anchor function with the given resolver. Returns `Err()` if no anchor is found.\n    #[cfg(feature = \"gecko\")]\n    pub fn resolve(\n        anchor_name: &TreeScoped<DashedIdent>,\n        anchor_side: &AnchorSide,\n        prop_side: PhysicalSide,\n        params: &AnchorPosOffsetResolutionParams,\n    ) -> Result<Length, ()> {\n        use crate::gecko_bindings::structs::Gecko_GetAnchorPosOffset;\n\n        let (keyword, percentage) = anchor_side.keyword_and_percentage();\n        let mut offset = Length::zero();\n        let valid = unsafe {\n            Gecko_GetAnchorPosOffset(\n                params,\n                anchor_name.value.0.as_ptr(),\n                &anchor_name.scope,\n                prop_side as u8,\n                keyword as u8,\n                percentage.0,\n                &mut offset,\n            )\n        };\n\n        if !valid {\n            return Err(());\n        }\n\n        Ok(offset)\n    }\n}\n\n/// Perform the adjustment of a given value for a given try tactic, as per:\n/// https://drafts.csswg.org/css-anchor-position-1/#swap-due-to-a-try-tactic\npub(crate) trait TryTacticAdjustment {\n    /// Performs the adjustments necessary given an old side we're relative to, and a new side\n    /// we're relative to.\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide);\n}\n\nimpl<T: TryTacticAdjustment> TryTacticAdjustment for Box<T> {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        (**self).try_tactic_adjustment(old_side, new_side);\n    }\n}\n\nimpl<T: TryTacticAdjustment> TryTacticAdjustment for generics::NonNegative<T> {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        self.0.try_tactic_adjustment(old_side, new_side);\n    }\n}\n\nimpl<Percentage: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSide<Percentage> {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        match self {\n            Self::Percentage(p) => p.try_tactic_adjustment(old_side, new_side),\n            Self::Keyword(side) => side.try_tactic_adjustment(old_side, new_side),\n        }\n    }\n}\n\nimpl<Percentage: TryTacticAdjustment, Fallback: TryTacticAdjustment> TryTacticAdjustment\n    for GenericAnchorFunction<Percentage, Fallback>\n{\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        self.side.try_tactic_adjustment(old_side, new_side);\n        if let Some(fallback) = self.fallback.as_mut() {\n            fallback.try_tactic_adjustment(old_side, new_side);\n        }\n    }\n}\n\n/// A computed type for `inset` properties.\npub type Inset = GenericInset<Percentage, LengthPercentage>;\nimpl TryTacticAdjustment for Inset {\n    // https://drafts.csswg.org/css-anchor-position-1/#swap-due-to-a-try-tactic:\n    //\n    //     For inset properties, change the specified side in anchor() functions to maintain the\n    //     same relative relationship to the new direction that they had to the old.\n    //\n    //     If a <percentage> is used, and directions are opposing, change it to 100% minus the\n    //     original percentage.\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        match self {\n            Self::Auto => {},\n            Self::AnchorContainingCalcFunction(lp) | Self::LengthPercentage(lp) => {\n                lp.try_tactic_adjustment(old_side, new_side)\n            },\n            Self::AnchorFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),\n            Self::AnchorSizeFunction(anchor) => anchor.try_tactic_adjustment(old_side, new_side),\n        }\n    }\n}\n\nimpl Position {\n    /// `50% 50%`\n    #[inline]\n    pub fn center() -> Self {\n        Self::new(\n            LengthPercentage::new_percent(Percentage(0.5)),\n            LengthPercentage::new_percent(Percentage(0.5)),\n        )\n    }\n\n    /// `0% 0%`\n    #[inline]\n    pub fn zero() -> Self {\n        Self::new(LengthPercentage::zero(), LengthPercentage::zero())\n    }\n}\n\nimpl ToCss for Position {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.horizontal.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.vertical.to_css(dest)\n    }\n}\n\nimpl GenericPositionComponent for LengthPercentage {\n    fn is_center(&self) -> bool {\n        match self.to_percentage() {\n            Some(Percentage(per)) => per == 0.5,\n            _ => false,\n        }\n    }\n}\n\n#[inline]\nfn block_or_inline_to_inferred(keyword: PositionAreaKeyword) -> PositionAreaKeyword {\n    if matches!(\n        keyword.axis(),\n        PositionAreaAxis::Block | PositionAreaAxis::Inline\n    ) {\n        keyword.with_axis(PositionAreaAxis::Inferred)\n    } else {\n        keyword\n    }\n}\n\n#[inline]\nfn inferred_to_block(keyword: PositionAreaKeyword) -> PositionAreaKeyword {\n    keyword.with_inferred_axis(PositionAreaAxis::Block)\n}\n\n#[inline]\nfn inferred_to_inline(keyword: PositionAreaKeyword) -> PositionAreaKeyword {\n    keyword.with_inferred_axis(PositionAreaAxis::Inline)\n}\n\n// This exists because the spec currently says that further simplifications\n// should be made to the computed value. That's confusing though, and probably\n// all these simplifications should be wrapped up into the simplifications that\n// we make to the specified value. I.e. all this should happen in\n// PositionArea::parse_internal().\n// See also https://github.com/w3c/csswg-drafts/issues/12759\nimpl ToComputedValue for PositionArea {\n    type ComputedValue = Self;\n\n    fn to_computed_value(&self, _context: &Context) -> Self {\n        let mut computed = self.clone();\n        let pair_type = self.get_type();\n        if pair_type == PositionAreaType::Logical || pair_type == PositionAreaType::SelfLogical {\n            if computed.second != PositionAreaKeyword::None {\n                computed.first = block_or_inline_to_inferred(computed.first);\n                computed.second = block_or_inline_to_inferred(computed.second);\n            }\n        } else if pair_type == PositionAreaType::Inferred\n            || pair_type == PositionAreaType::SelfInferred\n        {\n            if computed.second == PositionAreaKeyword::SpanAll {\n                // We remove the superfluous span-all, converting the inferred\n                // keyword to a logical keyword to avoid ambiguity, per spec.\n                computed.first = inferred_to_block(computed.first);\n                computed.second = PositionAreaKeyword::None;\n            } else if computed.first == PositionAreaKeyword::SpanAll {\n                computed.first = computed.second;\n                computed.first = inferred_to_inline(computed.first);\n                computed.second = PositionAreaKeyword::None;\n            }\n        }\n\n        if computed.first == computed.second {\n            computed.second = PositionAreaKeyword::None;\n        }\n        computed\n    }\n\n    fn from_computed_value(computed: &Self) -> Self {\n        computed.clone()\n    }\n}\n\n/// A computed value for the `z-index` property.\npub type ZIndex = GenericZIndex<Integer>;\n\n/// A computed value for the `aspect-ratio` property.\npub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;\n"
  },
  {
    "path": "style/values/computed/ratio.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! `<ratio>` computed values.\n\nuse crate::values::animated::{Animate, Procedure};\nuse crate::values::computed::NonNegativeNumber;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::ratio::Ratio as GenericRatio;\nuse crate::values::generics::NonNegative;\nuse crate::Zero;\nuse std::cmp::Ordering;\n\n/// A computed <ratio> value.\npub type Ratio = GenericRatio<NonNegativeNumber>;\n\nimpl PartialOrd for Ratio {\n    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n        f64::partial_cmp(\n            &((self.0).0 as f64 * (other.1).0 as f64),\n            &((self.1).0 as f64 * (other.0).0 as f64),\n        )\n    }\n}\n\nimpl Ratio {\n    /// Returns the f32 value by dividing the first value by the second one.\n    #[inline]\n    fn to_f32(&self) -> f32 {\n        debug_assert!(!self.is_degenerate());\n        (self.0).0 / (self.1).0\n    }\n    /// Returns a new Ratio.\n    #[inline]\n    pub fn new(a: f32, b: f32) -> Self {\n        GenericRatio(a.into(), b.into())\n    }\n}\n\n/// https://drafts.csswg.org/css-values/#combine-ratio\nimpl Animate for Ratio {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        // If either <ratio> is degenerate, the values cannot be interpolated.\n        if self.is_degenerate() || other.is_degenerate() {\n            return Err(());\n        }\n\n        // Addition of <ratio>s is not possible, and based on\n        // https://drafts.csswg.org/css-values-4/#not-additive,\n        // we simply use the first value as the result value.\n        // Besides, the procedure for accumulation should be identical to addition here.\n        if matches!(procedure, Procedure::Add | Procedure::Accumulate { .. }) {\n            return Ok(self.clone());\n        }\n\n        // The interpolation of a <ratio> is defined by converting each <ratio> to a number by\n        // dividing the first value by the second (so a ratio of 3 / 2 would become 1.5), taking\n        // the logarithm of that result (so the 1.5 would become approximately 0.176), then\n        // interpolating those values.\n        //\n        // The result during the interpolation is converted back to a <ratio> by inverting the\n        // logarithm, then interpreting the result as a <ratio> with the result as the first value\n        // and 1 as the second value.\n        let start = self.to_f32().ln();\n        let end = other.to_f32().ln();\n        let e = std::f32::consts::E;\n        let result = e.powf(start.animate(&end, procedure)?);\n        // The range of the result is [0, inf), based on the easing function.\n        if result.is_zero() || result.is_infinite() {\n            return Err(());\n        }\n        Ok(GenericRatio(NonNegative(result), NonNegative(1.0)))\n    }\n}\n\nimpl ComputeSquaredDistance for Ratio {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        if self.is_degenerate() || other.is_degenerate() {\n            return Err(());\n        }\n        // Use the distance of their logarithm values. (This is used by testing, so don't\n        // need to care about the base. Here we use the same base as that in animate().)\n        self.to_f32()\n            .ln()\n            .compute_squared_distance(&other.to_f32().ln())\n    }\n}\n"
  },
  {
    "path": "style/values/computed/rect.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS borders.\n\nuse crate::values::computed::length::NonNegativeLengthOrNumber;\nuse crate::values::generics::rect::Rect;\n\n/// A specified rectangle made of four `<length-or-number>` values.\npub type NonNegativeLengthOrNumberRect = Rect<NonNegativeLengthOrNumber>;\n"
  },
  {
    "path": "style/values/computed/resolution.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolution values:\n//!\n//! https://drafts.csswg.org/css-values/#resolution\n\nuse crate::derives::*;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::specified;\nuse crate::values::CSSFloat;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A computed `<resolution>`.\n#[repr(C)]\n#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem)]\npub struct Resolution(CSSFloat);\n\nimpl Resolution {\n    /// Returns this resolution value as dppx.\n    #[inline]\n    pub fn dppx(&self) -> CSSFloat {\n        self.0\n    }\n\n    /// Return a computed `resolution` value from a dppx float value.\n    #[inline]\n    pub fn from_dppx(dppx: CSSFloat) -> Self {\n        Resolution(dppx)\n    }\n}\n\nimpl ToComputedValue for specified::Resolution {\n    type ComputedValue = Resolution;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        Resolution(crate::values::normalize(self.dppx().max(0.0)))\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        specified::Resolution::from_dppx(computed.dppx())\n    }\n}\n\nimpl ToCss for Resolution {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.dppx().to_css(dest)?;\n        dest.write_str(\"dppx\")\n    }\n}\n"
  },
  {
    "path": "style/values/computed/svg.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for SVG properties.\n\nuse crate::values::computed::color::Color;\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opacity};\nuse crate::values::generics::svg as generic;\nuse crate::Zero;\n\npub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder, VectorEffect};\n\n/// Computed SVG Paint value\npub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;\n\n/// Computed SVG Paint Kind value\npub type SVGPaintKind = generic::GenericSVGPaintKind<Color, ComputedUrl>;\n\nimpl SVGPaint {\n    /// Opaque black color\n    pub const BLACK: Self = Self {\n        kind: generic::SVGPaintKind::Color(Color::BLACK),\n        fallback: generic::SVGPaintFallback::Unset,\n    };\n}\n\n/// <length> | <percentage> | <number> | context-value\npub type SVGLength = generic::GenericSVGLength<LengthPercentage>;\n\nimpl SVGLength {\n    /// `0px`\n    pub fn zero() -> Self {\n        generic::SVGLength::LengthPercentage(LengthPercentage::zero())\n    }\n}\n\n/// An non-negative wrapper of SVGLength.\npub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;\n\nimpl SVGWidth {\n    /// `1px`.\n    pub fn one() -> Self {\n        use crate::values::generics::NonNegative;\n        generic::SVGLength::LengthPercentage(NonNegative(LengthPercentage::one()))\n    }\n}\n\n/// [ <length> | <percentage> | <number> ]# | context-value\npub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;\n\nimpl Default for SVGStrokeDashArray {\n    fn default() -> Self {\n        generic::SVGStrokeDashArray::Values(Default::default())\n    }\n}\n\n/// <opacity-value> | context-fill-opacity | context-stroke-opacity\npub type SVGOpacity = generic::GenericSVGOpacity<Opacity>;\n\nimpl Default for SVGOpacity {\n    fn default() -> Self {\n        generic::SVGOpacity::Opacity(1.)\n    }\n}\n"
  },
  {
    "path": "style/values/computed/table.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values related to tables.\n\npub use super::specified::table::CaptionSide;\n"
  },
  {
    "path": "style/values/computed/text.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for text properties.\n\nuse crate::derives::*;\nuse crate::values::computed::length::{Length, LengthPercentage};\nuse crate::values::generics::text::{\n    GenericHyphenateLimitChars, GenericInitialLetter, GenericTextDecorationInset,\n    GenericTextDecorationLength, GenericTextIndent,\n};\nuse crate::values::generics::NumberOrAuto;\nuse crate::values::specified::text as specified;\nuse crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};\nuse crate::values::{CSSFloat, CSSInteger};\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssString, CssWriter, KeywordValue, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\npub use crate::values::specified::text::{\n    HyphenateCharacter, LineBreak, MozControlCharacterVisibility, OverflowWrap, RubyPosition,\n    TextAlignLast, TextAutospace, TextBoxEdge, TextBoxTrim, TextDecorationLine,\n    TextDecorationSkipInk, TextEmphasisPosition, TextJustify, TextOverflow, TextTransform,\n    TextUnderlinePosition, WordBreak,\n};\n\n/// A computed value for the `initial-letter` property.\npub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;\n\n/// Implements type for `text-decoration-thickness` property.\npub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;\n\n/// Implements type for `text-decoration-inset` property.\npub type TextDecorationInset = GenericTextDecorationInset<Length>;\n\n/// The computed value of `text-align`.\npub type TextAlign = specified::TextAlignKeyword;\n\n/// The computed value of `text-indent`.\npub type TextIndent = GenericTextIndent<LengthPercentage>;\n\n/// A computed value for the `hyphenate-character` property.\npub type HyphenateLimitChars = GenericHyphenateLimitChars<CSSInteger>;\n\nimpl HyphenateLimitChars {\n    /// Return the `auto` value, which has all three component values as `auto`.\n    #[inline]\n    pub fn auto() -> Self {\n        Self {\n            total_word_length: NumberOrAuto::Auto,\n            pre_hyphen_length: NumberOrAuto::Auto,\n            post_hyphen_length: NumberOrAuto::Auto,\n        }\n    }\n}\n\n/// A computed value for the `letter-spacing` property.\n#[repr(transparent)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToResolvedValue,\n)]\npub struct GenericLetterSpacing<L>(pub L);\n/// This is generic just to make the #[derive()] code do the right thing for lengths.\npub type LetterSpacing = GenericLetterSpacing<LengthPercentage>;\n\nimpl LetterSpacing {\n    /// Return the `normal` computed value, which is just zero.\n    #[inline]\n    pub fn normal() -> Self {\n        Self(LengthPercentage::zero())\n    }\n}\n\nimpl ToCss for LetterSpacing {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        // https://drafts.csswg.org/css-text/#propdef-letter-spacing\n        //\n        // For legacy reasons, a computed letter-spacing of zero yields a\n        // resolved value (getComputedStyle() return value) of normal.\n        if self.0.is_zero() {\n            return dest.write_str(\"normal\");\n        }\n        self.0.to_css(dest)\n    }\n}\n\nimpl ToTyped for LetterSpacing {\n    // XXX The specification does not currently define how this property should\n    // be reified into Typed OM. The current behavior follows existing WPT\n    // coverage (letter-spacing.html). We may file a spec issue once more data\n    // is collected to update the Property-specific Rules section to align with\n    // observed test expectations.\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        if !self.0.has_percentage() && self.0.is_zero() {\n            dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\"normal\"))));\n            return Ok(());\n        }\n        self.0.to_typed(dest)\n    }\n}\n\n/// A computed value for the `word-spacing` property.\npub type WordSpacing = LengthPercentage;\n\nimpl WordSpacing {\n    /// Return the `normal` computed value, which is just zero.\n    #[inline]\n    pub fn normal() -> Self {\n        LengthPercentage::zero()\n    }\n}\n\n/// Computed value for the text-emphasis-style property\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToTyped)]\n#[allow(missing_docs)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum TextEmphasisStyle {\n    /// [ <fill> || <shape> ]\n    Keyword {\n        #[css(skip_if = \"TextEmphasisFillMode::is_filled\")]\n        fill: TextEmphasisFillMode,\n        shape: TextEmphasisShapeKeyword,\n    },\n    /// `none`\n    None,\n    /// `<string>` (of which only the first grapheme cluster will be used).\n    String(crate::OwnedStr),\n}\n"
  },
  {
    "path": "style/values/computed/time.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed time values.\n\nuse crate::derives::*;\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssString, CssWriter, NumericValue, ToCss, ToTyped, TypedValue, UnitValue};\nuse thin_vec::ThinVec;\n\n/// A computed `<time>` value.\n#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct Time {\n    seconds: CSSFloat,\n}\n\nimpl Time {\n    /// Creates a time value from a seconds amount.\n    pub fn from_seconds(seconds: CSSFloat) -> Self {\n        Time { seconds }\n    }\n\n    /// Returns the amount of seconds this time represents.\n    #[inline]\n    pub fn seconds(&self) -> CSSFloat {\n        self.seconds\n    }\n}\n\nimpl ToCss for Time {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.seconds().to_css(dest)?;\n        dest.write_char('s')\n    }\n}\n\nimpl ToTyped for Time {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {\n            value: self.seconds(),\n            unit: CssString::from(\"s\"),\n        })));\n        Ok(())\n    }\n}\n\nimpl Zero for Time {\n    fn zero() -> Self {\n        Self::from_seconds(0.0)\n    }\n\n    fn is_zero(&self) -> bool {\n        self.seconds == 0.\n    }\n}\n"
  },
  {
    "path": "style/values/computed/transform.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed types for CSS values that are related to transformations.\n\nuse super::CSSFloat;\nuse crate::values::animated::transform::{Perspective, Scale3D, Translate3D};\nuse crate::values::animated::ToAnimatedZero;\nuse crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};\nuse crate::values::generics::transform as generic;\nuse crate::Zero;\nuse euclid::default::{Transform3D, Vector3D};\n\npub use crate::values::generics::transform::TransformStyle;\npub use crate::values::specified::transform::TransformBox;\n\n/// A single operation in a computed CSS `transform`\npub type TransformOperation =\n    generic::GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>;\n/// A computed CSS `transform`\npub type Transform = generic::GenericTransform<TransformOperation>;\n\n/// The computed value of a CSS `<transform-origin>`\npub type TransformOrigin =\n    generic::GenericTransformOrigin<LengthPercentage, LengthPercentage, Length>;\n\n/// The computed value of the `perspective()` transform function.\npub type PerspectiveFunction = generic::PerspectiveFunction<Length>;\n\n/// A vector to represent the direction vector (rotate axis) for Rotate3D.\npub type DirectionVector = Vector3D<CSSFloat>;\n\nimpl TransformOrigin {\n    /// Returns the initial computed value for `transform-origin`.\n    #[inline]\n    pub fn initial_value() -> Self {\n        Self::new(\n            LengthPercentage::new_percent(Percentage(0.5)),\n            LengthPercentage::new_percent(Percentage(0.5)),\n            Length::new(0.),\n        )\n    }\n}\n\n/// computed value of matrix3d()\npub type Matrix3D = generic::Matrix3D<Number>;\n\n/// computed value of matrix()\npub type Matrix = generic::Matrix<Number>;\n\n// we rustfmt_skip here because we want the matrices to look like\n// matrices instead of being split across lines\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl Matrix3D {\n    /// Get an identity matrix\n    #[inline]\n    pub fn identity() -> Self {\n        Self {\n            m11: 1.0, m12: 0.0, m13: 0.0, m14: 0.0,\n            m21: 0.0, m22: 1.0, m23: 0.0, m24: 0.0,\n            m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,\n            m41: 0., m42: 0., m43: 0., m44: 1.0\n        }\n    }\n\n    /// Convert to a 2D Matrix\n    #[inline]\n    pub fn into_2d(self) -> Result<Matrix, ()> {\n        if self.m13 == 0. && self.m23 == 0. &&\n           self.m31 == 0. && self.m32 == 0. &&\n           self.m33 == 1. && self.m34 == 0. &&\n           self.m14 == 0. && self.m24 == 0. &&\n           self.m43 == 0. && self.m44 == 1. {\n            Ok(Matrix {\n                a: self.m11, c: self.m21, e: self.m41,\n                b: self.m12, d: self.m22, f: self.m42,\n            })\n        } else {\n            Err(())\n        }\n    }\n\n    /// Return true if this has 3D components.\n    #[inline]\n    pub fn is_3d(&self) -> bool {\n        self.m13 != 0.0 || self.m14 != 0.0 ||\n        self.m23 != 0.0 || self.m24 != 0.0 ||\n        self.m31 != 0.0 || self.m32 != 0.0 ||\n        self.m33 != 1.0 || self.m34 != 0.0 ||\n        self.m43 != 0.0 || self.m44 != 1.0\n    }\n\n    /// Return determinant value.\n    #[inline]\n    pub fn determinant(&self) -> CSSFloat {\n        self.m14 * self.m23 * self.m32 * self.m41 -\n        self.m13 * self.m24 * self.m32 * self.m41 -\n        self.m14 * self.m22 * self.m33 * self.m41 +\n        self.m12 * self.m24 * self.m33 * self.m41 +\n        self.m13 * self.m22 * self.m34 * self.m41 -\n        self.m12 * self.m23 * self.m34 * self.m41 -\n        self.m14 * self.m23 * self.m31 * self.m42 +\n        self.m13 * self.m24 * self.m31 * self.m42 +\n        self.m14 * self.m21 * self.m33 * self.m42 -\n        self.m11 * self.m24 * self.m33 * self.m42 -\n        self.m13 * self.m21 * self.m34 * self.m42 +\n        self.m11 * self.m23 * self.m34 * self.m42 +\n        self.m14 * self.m22 * self.m31 * self.m43 -\n        self.m12 * self.m24 * self.m31 * self.m43 -\n        self.m14 * self.m21 * self.m32 * self.m43 +\n        self.m11 * self.m24 * self.m32 * self.m43 +\n        self.m12 * self.m21 * self.m34 * self.m43 -\n        self.m11 * self.m22 * self.m34 * self.m43 -\n        self.m13 * self.m22 * self.m31 * self.m44 +\n        self.m12 * self.m23 * self.m31 * self.m44 +\n        self.m13 * self.m21 * self.m32 * self.m44 -\n        self.m11 * self.m23 * self.m32 * self.m44 -\n        self.m12 * self.m21 * self.m33 * self.m44 +\n        self.m11 * self.m22 * self.m33 * self.m44\n    }\n\n    /// Transpose a matrix.\n    #[inline]\n    pub fn transpose(&self) -> Self {\n        Self {\n            m11: self.m11, m12: self.m21, m13: self.m31, m14: self.m41,\n            m21: self.m12, m22: self.m22, m23: self.m32, m24: self.m42,\n            m31: self.m13, m32: self.m23, m33: self.m33, m34: self.m43,\n            m41: self.m14, m42: self.m24, m43: self.m34, m44: self.m44,\n        }\n    }\n\n    /// Return inverse matrix.\n    pub fn inverse(&self) -> Result<Matrix3D, ()> {\n        let mut det = self.determinant();\n\n        if det == 0.0 {\n            return Err(());\n        }\n\n        det = 1.0 / det;\n        let x = Matrix3D {\n            m11: det *\n            (self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42 +\n             self.m24 * self.m32 * self.m43 - self.m22 * self.m34 * self.m43 -\n             self.m23 * self.m32 * self.m44 + self.m22 * self.m33 * self.m44),\n            m12: det *\n            (self.m14 * self.m33 * self.m42 - self.m13 * self.m34 * self.m42 -\n             self.m14 * self.m32 * self.m43 + self.m12 * self.m34 * self.m43 +\n             self.m13 * self.m32 * self.m44 - self.m12 * self.m33 * self.m44),\n            m13: det *\n            (self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42 +\n             self.m14 * self.m22 * self.m43 - self.m12 * self.m24 * self.m43 -\n             self.m13 * self.m22 * self.m44 + self.m12 * self.m23 * self.m44),\n            m14: det *\n            (self.m14 * self.m23 * self.m32 - self.m13 * self.m24 * self.m32 -\n             self.m14 * self.m22 * self.m33 + self.m12 * self.m24 * self.m33 +\n             self.m13 * self.m22 * self.m34 - self.m12 * self.m23 * self.m34),\n            m21: det *\n            (self.m24 * self.m33 * self.m41 - self.m23 * self.m34 * self.m41 -\n             self.m24 * self.m31 * self.m43 + self.m21 * self.m34 * self.m43 +\n             self.m23 * self.m31 * self.m44 - self.m21 * self.m33 * self.m44),\n            m22: det *\n            (self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41 +\n             self.m14 * self.m31 * self.m43 - self.m11 * self.m34 * self.m43 -\n             self.m13 * self.m31 * self.m44 + self.m11 * self.m33 * self.m44),\n            m23: det *\n            (self.m14 * self.m23 * self.m41 - self.m13 * self.m24 * self.m41 -\n             self.m14 * self.m21 * self.m43 + self.m11 * self.m24 * self.m43 +\n             self.m13 * self.m21 * self.m44 - self.m11 * self.m23 * self.m44),\n            m24: det *\n            (self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31 +\n             self.m14 * self.m21 * self.m33 - self.m11 * self.m24 * self.m33 -\n             self.m13 * self.m21 * self.m34 + self.m11 * self.m23 * self.m34),\n            m31: det *\n            (self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41 +\n             self.m24 * self.m31 * self.m42 - self.m21 * self.m34 * self.m42 -\n             self.m22 * self.m31 * self.m44 + self.m21 * self.m32 * self.m44),\n            m32: det *\n            (self.m14 * self.m32 * self.m41 - self.m12 * self.m34 * self.m41 -\n             self.m14 * self.m31 * self.m42 + self.m11 * self.m34 * self.m42 +\n             self.m12 * self.m31 * self.m44 - self.m11 * self.m32 * self.m44),\n            m33: det *\n            (self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 +\n             self.m14 * self.m21 * self.m42 - self.m11 * self.m24 * self.m42 -\n             self.m12 * self.m21 * self.m44 + self.m11 * self.m22 * self.m44),\n            m34: det *\n            (self.m14 * self.m22 * self.m31 - self.m12 * self.m24 * self.m31 -\n             self.m14 * self.m21 * self.m32 + self.m11 * self.m24 * self.m32 +\n             self.m12 * self.m21 * self.m34 - self.m11 * self.m22 * self.m34),\n            m41: det *\n            (self.m23 * self.m32 * self.m41 - self.m22 * self.m33 * self.m41 -\n             self.m23 * self.m31 * self.m42 + self.m21 * self.m33 * self.m42 +\n             self.m22 * self.m31 * self.m43 - self.m21 * self.m32 * self.m43),\n            m42: det *\n            (self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41 +\n             self.m13 * self.m31 * self.m42 - self.m11 * self.m33 * self.m42 -\n             self.m12 * self.m31 * self.m43 + self.m11 * self.m32 * self.m43),\n            m43: det *\n            (self.m13 * self.m22 * self.m41 - self.m12 * self.m23 * self.m41 -\n             self.m13 * self.m21 * self.m42 + self.m11 * self.m23 * self.m42 +\n             self.m12 * self.m21 * self.m43 - self.m11 * self.m22 * self.m43),\n            m44: det *\n            (self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31 +\n             self.m13 * self.m21 * self.m32 - self.m11 * self.m23 * self.m32 -\n             self.m12 * self.m21 * self.m33 + self.m11 * self.m22 * self.m33),\n        };\n\n        Ok(x)\n    }\n\n    /// Multiply `pin * self`.\n    #[inline]\n    pub fn pre_mul_point4(&self, pin: &[f32; 4]) -> [f32; 4] {\n        [\n            pin[0] * self.m11 + pin[1] * self.m21 + pin[2] * self.m31 + pin[3] * self.m41,\n            pin[0] * self.m12 + pin[1] * self.m22 + pin[2] * self.m32 + pin[3] * self.m42,\n            pin[0] * self.m13 + pin[1] * self.m23 + pin[2] * self.m33 + pin[3] * self.m43,\n            pin[0] * self.m14 + pin[1] * self.m24 + pin[2] * self.m34 + pin[3] * self.m44,\n        ]\n    }\n\n    /// Return the multiplication of two 4x4 matrices.\n    #[inline]\n    pub fn multiply(&self, other: &Self) -> Self {\n        Matrix3D {\n            m11: self.m11 * other.m11 + self.m12 * other.m21 +\n                 self.m13 * other.m31 + self.m14 * other.m41,\n            m12: self.m11 * other.m12 + self.m12 * other.m22 +\n                 self.m13 * other.m32 + self.m14 * other.m42,\n            m13: self.m11 * other.m13 + self.m12 * other.m23 +\n                 self.m13 * other.m33 + self.m14 * other.m43,\n            m14: self.m11 * other.m14 + self.m12 * other.m24 +\n                 self.m13 * other.m34 + self.m14 * other.m44,\n            m21: self.m21 * other.m11 + self.m22 * other.m21 +\n                 self.m23 * other.m31 + self.m24 * other.m41,\n            m22: self.m21 * other.m12 + self.m22 * other.m22 +\n                 self.m23 * other.m32 + self.m24 * other.m42,\n            m23: self.m21 * other.m13 + self.m22 * other.m23 +\n                 self.m23 * other.m33 + self.m24 * other.m43,\n            m24: self.m21 * other.m14 + self.m22 * other.m24 +\n                 self.m23 * other.m34 + self.m24 * other.m44,\n            m31: self.m31 * other.m11 + self.m32 * other.m21 +\n                 self.m33 * other.m31 + self.m34 * other.m41,\n            m32: self.m31 * other.m12 + self.m32 * other.m22 +\n                 self.m33 * other.m32 + self.m34 * other.m42,\n            m33: self.m31 * other.m13 + self.m32 * other.m23 +\n                 self.m33 * other.m33 + self.m34 * other.m43,\n            m34: self.m31 * other.m14 + self.m32 * other.m24 +\n                 self.m33 * other.m34 + self.m34 * other.m44,\n            m41: self.m41 * other.m11 + self.m42 * other.m21 +\n                 self.m43 * other.m31 + self.m44 * other.m41,\n            m42: self.m41 * other.m12 + self.m42 * other.m22 +\n                 self.m43 * other.m32 + self.m44 * other.m42,\n            m43: self.m41 * other.m13 + self.m42 * other.m23 +\n                 self.m43 * other.m33 + self.m44 * other.m43,\n            m44: self.m41 * other.m14 + self.m42 * other.m24 +\n                 self.m43 * other.m34 + self.m44 * other.m44,\n        }\n    }\n\n    /// Scale the matrix by a factor.\n    #[inline]\n    pub fn scale_by_factor(&mut self, scaling_factor: CSSFloat) {\n        self.m11 *= scaling_factor;\n        self.m12 *= scaling_factor;\n        self.m13 *= scaling_factor;\n        self.m14 *= scaling_factor;\n        self.m21 *= scaling_factor;\n        self.m22 *= scaling_factor;\n        self.m23 *= scaling_factor;\n        self.m24 *= scaling_factor;\n        self.m31 *= scaling_factor;\n        self.m32 *= scaling_factor;\n        self.m33 *= scaling_factor;\n        self.m34 *= scaling_factor;\n        self.m41 *= scaling_factor;\n        self.m42 *= scaling_factor;\n        self.m43 *= scaling_factor;\n        self.m44 *= scaling_factor;\n    }\n\n    /// Return the matrix 3x3 part (top-left corner).\n    /// This is used by retrieving the scale and shear factors\n    /// during decomposing a 3d matrix.\n    #[inline]\n    pub fn get_matrix_3x3_part(&self) -> [[f32; 3]; 3] {\n        [\n            [ self.m11, self.m12, self.m13 ],\n            [ self.m21, self.m22, self.m23 ],\n            [ self.m31, self.m32, self.m33 ],\n        ]\n    }\n\n    /// Set perspective on the matrix.\n    #[inline]\n    pub fn set_perspective(&mut self, perspective: &Perspective) {\n        self.m14 = perspective.0;\n        self.m24 = perspective.1;\n        self.m34 = perspective.2;\n        self.m44 = perspective.3;\n    }\n\n    /// Apply translate on the matrix.\n    #[inline]\n    pub fn apply_translate(&mut self, translate: &Translate3D) {\n        self.m41 += translate.0 * self.m11 + translate.1 * self.m21 + translate.2 * self.m31;\n        self.m42 += translate.0 * self.m12 + translate.1 * self.m22 + translate.2 * self.m32;\n        self.m43 += translate.0 * self.m13 + translate.1 * self.m23 + translate.2 * self.m33;\n        self.m44 += translate.0 * self.m14 + translate.1 * self.m24 + translate.2 * self.m34;\n    }\n\n    /// Apply scale on the matrix.\n    #[inline]\n    pub fn apply_scale(&mut self, scale: &Scale3D) {\n        self.m11 *= scale.0;\n        self.m12 *= scale.0;\n        self.m13 *= scale.0;\n        self.m14 *= scale.0;\n        self.m21 *= scale.1;\n        self.m22 *= scale.1;\n        self.m23 *= scale.1;\n        self.m24 *= scale.1;\n        self.m31 *= scale.2;\n        self.m32 *= scale.2;\n        self.m33 *= scale.2;\n        self.m34 *= scale.2;\n    }\n}\n\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl Matrix {\n    #[inline]\n    /// Get an identity matrix\n    pub fn identity() -> Self {\n        Self {\n            a: 1., c: 0., /* 0      0*/\n            b: 0., d: 1., /* 0      0*/\n            /* 0      0      1      0 */\n            e: 0., f: 0., /* 0      1 */\n        }\n    }\n}\n\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl From<Matrix> for Matrix3D {\n    fn from(m: Matrix) -> Self {\n        Self {\n            m11: m.a, m12: m.b, m13: 0.0, m14: 0.0,\n            m21: m.c, m22: m.d, m23: 0.0, m24: 0.0,\n            m31: 0.0, m32: 0.0, m33: 1.0, m34: 0.0,\n            m41: m.e, m42: m.f, m43: 0.0, m44: 1.0\n        }\n    }\n}\n\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl From<Transform3D<CSSFloat>> for Matrix3D {\n    #[inline]\n    fn from(m: Transform3D<CSSFloat>) -> Self {\n        Matrix3D {\n            m11: m.m11, m12: m.m12, m13: m.m13, m14: m.m14,\n            m21: m.m21, m22: m.m22, m23: m.m23, m24: m.m24,\n            m31: m.m31, m32: m.m32, m33: m.m33, m34: m.m34,\n            m41: m.m41, m42: m.m42, m43: m.m43, m44: m.m44\n        }\n    }\n}\n\nimpl TransformOperation {\n    /// Convert to a Translate3D.\n    ///\n    /// Must be called on a Translate function\n    pub fn to_translate_3d(&self) -> Self {\n        match *self {\n            generic::TransformOperation::Translate3D(..) => self.clone(),\n            generic::TransformOperation::TranslateX(ref x) => {\n                generic::TransformOperation::Translate3D(\n                    x.clone(),\n                    LengthPercentage::zero(),\n                    Length::zero(),\n                )\n            },\n            generic::TransformOperation::Translate(ref x, ref y) => {\n                generic::TransformOperation::Translate3D(x.clone(), y.clone(), Length::zero())\n            },\n            generic::TransformOperation::TranslateY(ref y) => {\n                generic::TransformOperation::Translate3D(\n                    LengthPercentage::zero(),\n                    y.clone(),\n                    Length::zero(),\n                )\n            },\n            generic::TransformOperation::TranslateZ(ref z) => {\n                generic::TransformOperation::Translate3D(\n                    LengthPercentage::zero(),\n                    LengthPercentage::zero(),\n                    z.clone(),\n                )\n            },\n            _ => unreachable!(),\n        }\n    }\n\n    /// Convert to a Rotate3D.\n    ///\n    /// Must be called on a Rotate function.\n    pub fn to_rotate_3d(&self) -> Self {\n        match *self {\n            generic::TransformOperation::Rotate3D(..) => self.clone(),\n            generic::TransformOperation::RotateZ(ref angle)\n            | generic::TransformOperation::Rotate(ref angle) => {\n                generic::TransformOperation::Rotate3D(0., 0., 1., angle.clone())\n            },\n            generic::TransformOperation::RotateX(ref angle) => {\n                generic::TransformOperation::Rotate3D(1., 0., 0., angle.clone())\n            },\n            generic::TransformOperation::RotateY(ref angle) => {\n                generic::TransformOperation::Rotate3D(0., 1., 0., angle.clone())\n            },\n            _ => unreachable!(),\n        }\n    }\n\n    /// Convert to a Scale3D.\n    ///\n    /// Must be called on a Scale function\n    pub fn to_scale_3d(&self) -> Self {\n        match *self {\n            generic::TransformOperation::Scale3D(..) => self.clone(),\n            generic::TransformOperation::Scale(x, y) => {\n                generic::TransformOperation::Scale3D(x, y, 1.)\n            },\n            generic::TransformOperation::ScaleX(x) => {\n                generic::TransformOperation::Scale3D(x, 1., 1.)\n            },\n            generic::TransformOperation::ScaleY(y) => {\n                generic::TransformOperation::Scale3D(1., y, 1.)\n            },\n            generic::TransformOperation::ScaleZ(z) => {\n                generic::TransformOperation::Scale3D(1., 1., z)\n            },\n            _ => unreachable!(),\n        }\n    }\n}\n\n/// Build an equivalent 'identity transform function list' based\n/// on an existing transform list.\n/// http://dev.w3.org/csswg/css-transforms/#none-transform-animation\nimpl ToAnimatedZero for TransformOperation {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        match *self {\n            generic::TransformOperation::Matrix3D(..) => {\n                Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))\n            },\n            generic::TransformOperation::Matrix(..) => {\n                Ok(generic::TransformOperation::Matrix(Matrix::identity()))\n            },\n            generic::TransformOperation::Skew(sx, sy) => Ok(generic::TransformOperation::Skew(\n                sx.to_animated_zero()?,\n                sy.to_animated_zero()?,\n            )),\n            generic::TransformOperation::SkewX(s) => {\n                Ok(generic::TransformOperation::SkewX(s.to_animated_zero()?))\n            },\n            generic::TransformOperation::SkewY(s) => {\n                Ok(generic::TransformOperation::SkewY(s.to_animated_zero()?))\n            },\n            generic::TransformOperation::Translate3D(ref tx, ref ty, ref tz) => {\n                Ok(generic::TransformOperation::Translate3D(\n                    tx.to_animated_zero()?,\n                    ty.to_animated_zero()?,\n                    tz.to_animated_zero()?,\n                ))\n            },\n            generic::TransformOperation::Translate(ref tx, ref ty) => {\n                Ok(generic::TransformOperation::Translate(\n                    tx.to_animated_zero()?,\n                    ty.to_animated_zero()?,\n                ))\n            },\n            generic::TransformOperation::TranslateX(ref t) => Ok(\n                generic::TransformOperation::TranslateX(t.to_animated_zero()?),\n            ),\n            generic::TransformOperation::TranslateY(ref t) => Ok(\n                generic::TransformOperation::TranslateY(t.to_animated_zero()?),\n            ),\n            generic::TransformOperation::TranslateZ(ref t) => Ok(\n                generic::TransformOperation::TranslateZ(t.to_animated_zero()?),\n            ),\n            generic::TransformOperation::Scale3D(..) => {\n                Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0))\n            },\n            generic::TransformOperation::Scale(_, _) => {\n                Ok(generic::TransformOperation::Scale(1.0, 1.0))\n            },\n            generic::TransformOperation::ScaleX(..) => Ok(generic::TransformOperation::ScaleX(1.0)),\n            generic::TransformOperation::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)),\n            generic::TransformOperation::ScaleZ(..) => Ok(generic::TransformOperation::ScaleZ(1.0)),\n            generic::TransformOperation::Rotate3D(x, y, z, a) => {\n                let (x, y, z, _) = generic::get_normalized_vector_and_angle(x, y, z, a);\n                Ok(generic::TransformOperation::Rotate3D(\n                    x,\n                    y,\n                    z,\n                    Angle::zero(),\n                ))\n            },\n            generic::TransformOperation::RotateX(_) => {\n                Ok(generic::TransformOperation::RotateX(Angle::zero()))\n            },\n            generic::TransformOperation::RotateY(_) => {\n                Ok(generic::TransformOperation::RotateY(Angle::zero()))\n            },\n            generic::TransformOperation::RotateZ(_) => {\n                Ok(generic::TransformOperation::RotateZ(Angle::zero()))\n            },\n            generic::TransformOperation::Rotate(_) => {\n                Ok(generic::TransformOperation::Rotate(Angle::zero()))\n            },\n            generic::TransformOperation::Perspective(_) => Ok(\n                generic::TransformOperation::Perspective(generic::PerspectiveFunction::None),\n            ),\n            generic::TransformOperation::AccumulateMatrix { .. }\n            | generic::TransformOperation::InterpolateMatrix { .. } => {\n                // AccumulateMatrix/InterpolateMatrix: We do interpolation on\n                //     AccumulateMatrix/InterpolateMatrix by reading it as a ComputedMatrix\n                //     (with layout information), and then do matrix interpolation.\n                //\n                // Therefore, we use an identity matrix to represent the identity transform list.\n                // http://dev.w3.org/csswg/css-transforms/#identity-transform-function\n                Ok(generic::TransformOperation::Matrix3D(Matrix3D::identity()))\n            },\n        }\n    }\n}\n\nimpl ToAnimatedZero for Transform {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Ok(generic::Transform(\n            self.0\n                .iter()\n                .map(|op| op.to_animated_zero())\n                .collect::<Result<crate::OwnedSlice<_>, _>>()?,\n        ))\n    }\n}\n\n/// A computed CSS `rotate`\npub type Rotate = generic::GenericRotate<Number, Angle>;\n\n/// A computed CSS `translate`\npub type Translate = generic::GenericTranslate<LengthPercentage, Length>;\n\n/// A computed CSS `scale`\npub type Scale = generic::GenericScale<Number>;\n"
  },
  {
    "path": "style/values/computed/ui.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Computed values for UI properties\n\nuse crate::values::computed::color::Color;\nuse crate::values::computed::image::Image;\nuse crate::values::computed::Number;\nuse crate::values::generics::ui as generics;\n\npub use crate::values::specified::ui::{\n    BoolInteger, CursorKind, Inert, MozTheme, PointerEvents, UserFocus, UserSelect,\n};\n\n/// A computed value for the `cursor` property.\npub type Cursor = generics::GenericCursor<CursorImage>;\n\n/// A computed value for item of `image cursors`.\npub type CursorImage = generics::GenericCursorImage<Image, Number>;\n\n/// A computed value for `scrollbar-color` property.\npub type ScrollbarColor = generics::GenericScrollbarColor<Color>;\n"
  },
  {
    "path": "style/values/computed/url.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common handling for the computed value CSS url() values.\n\nuse crate::values::generics::url::UrlOrNone as GenericUrlOrNone;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::url::ComputedUrl;\n#[cfg(feature = \"servo\")]\npub use crate::servo::url::ComputedUrl;\n\n/// Computed <url> | <none>\npub type UrlOrNone = GenericUrlOrNone<ComputedUrl>;\n"
  },
  {
    "path": "style/values/distance.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Machinery to compute distances between animatable values.\n\nuse crate::derives::*;\nuse app_units::Au;\nuse euclid::default::Size2D;\nuse std::iter::Sum;\nuse std::ops::Add;\n\n/// A trait to compute squared distances between two animatable values.\n///\n/// This trait is derivable with `#[derive(ComputeSquaredDistance)]`. The derived\n/// implementation uses a `match` expression with identical patterns for both\n/// `self` and `other`, calling `ComputeSquaredDistance::compute_squared_distance`\n/// on each fields of the values.\n///\n/// If a variant is annotated with `#[animation(error)]`, the corresponding\n/// `match` arm returns an error.\n///\n/// Trait bounds for type parameter `Foo` can be opted out of with\n/// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for\n/// fields can be opted into with `#[distance(field_bound)]` on the field.\npub trait ComputeSquaredDistance {\n    /// Computes the squared distance between two animatable values.\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()>;\n}\n\n/// A distance between two animatable values.\n#[derive(Add, Clone, Copy, Debug, From, PartialEq, PartialOrd)]\npub struct SquaredDistance {\n    value: f64,\n}\n\nimpl SquaredDistance {\n    /// Returns a squared distance from its square root.\n    #[inline]\n    pub fn from_sqrt(sqrt: f64) -> Self {\n        Self { value: sqrt * sqrt }\n    }\n}\n\nimpl ComputeSquaredDistance for u16 {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt(\n            ((*self as f64) - (*other as f64)).abs(),\n        ))\n    }\n}\n\nimpl ComputeSquaredDistance for i16 {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))\n    }\n}\n\nimpl ComputeSquaredDistance for i32 {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))\n    }\n}\n\nimpl ComputeSquaredDistance for f32 {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt((*self - *other).abs() as f64))\n    }\n}\n\nimpl ComputeSquaredDistance for f64 {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(SquaredDistance::from_sqrt((*self - *other).abs()))\n    }\n}\n\nimpl ComputeSquaredDistance for Au {\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        self.0.compute_squared_distance(&other.0)\n    }\n}\n\nimpl<T> ComputeSquaredDistance for Box<T>\nwhere\n    T: ComputeSquaredDistance,\n{\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        (**self).compute_squared_distance(&**other)\n    }\n}\n\nimpl<T> ComputeSquaredDistance for Option<T>\nwhere\n    T: ComputeSquaredDistance,\n{\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        match (self.as_ref(), other.as_ref()) {\n            (Some(this), Some(other)) => this.compute_squared_distance(other),\n            (None, None) => Ok(SquaredDistance::from_sqrt(0.)),\n            _ => Err(()),\n        }\n    }\n}\n\nimpl<T> ComputeSquaredDistance for Size2D<T>\nwhere\n    T: ComputeSquaredDistance,\n{\n    #[inline]\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        Ok(self.width.compute_squared_distance(&other.width)?\n            + self.height.compute_squared_distance(&other.height)?)\n    }\n}\n\nimpl SquaredDistance {\n    /// Returns the square root of this squared distance.\n    #[inline]\n    pub fn sqrt(self) -> f64 {\n        self.value.sqrt()\n    }\n}\n\nimpl Sum for SquaredDistance {\n    fn sum<I>(iter: I) -> Self\n    where\n        I: Iterator<Item = Self>,\n    {\n        iter.fold(SquaredDistance::from_sqrt(0.), Add::add)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/animation.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic values for properties related to animations and transitions.\n\nuse crate::derives::*;\nuse crate::values::generics::length::GenericLengthPercentageOrAuto;\nuse crate::values::specified::animation::{\n    ScrollAxis, ScrollFunction, TimelineName, TimelineRangeName,\n};\nuse crate::values::specified::length::EqualsPercentage;\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssString, CssWriter, KeywordValue, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\n/// The `animation-duration` property.\n///\n/// https://drafts.csswg.org/css-animations-2/#animation-duration\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericAnimationDuration<T> {\n    /// The initial value. However, we serialize this as 0s if the preference is disabled.\n    Auto,\n    /// The time value, <time [0s,∞]>.\n    Time(T),\n}\n\npub use self::GenericAnimationDuration as AnimationDuration;\n\nimpl<T> AnimationDuration<T> {\n    /// Returns the `auto` value.\n    pub fn auto() -> Self {\n        Self::Auto\n    }\n\n    /// Returns true if it is `auto`.\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, Self::Auto)\n    }\n}\n\nimpl<T: Zero> Zero for AnimationDuration<T> {\n    fn zero() -> Self {\n        Self::Time(T::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        match *self {\n            Self::Time(ref t) => t.is_zero(),\n            _ => false,\n        }\n    }\n}\n\nimpl<T: ToCss + Zero> ToCss for AnimationDuration<T> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Self::Auto => {\n                if static_prefs::pref!(\"layout.css.scroll-driven-animations.enabled\") {\n                    dest.write_str(\"auto\")\n                } else {\n                    Self::Time(T::zero()).to_css(dest)\n                }\n            },\n            Self::Time(ref t) => t.to_css(dest),\n        }\n    }\n}\n\n// TODO: Switch to ToTyped derive once the pref goes away.\nimpl<T: ToTyped + Zero> ToTyped for AnimationDuration<T> {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        match *self {\n            Self::Auto => {\n                if static_prefs::pref!(\"layout.css.scroll-driven-animations.enabled\") {\n                    dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\"auto\"))));\n                    Ok(())\n                } else {\n                    Self::Time(T::zero()).to_typed(dest)\n                }\n            },\n            Self::Time(ref t) => t.to_typed(dest),\n        }\n    }\n}\n\n/// The view() notation.\n/// https://drafts.csswg.org/scroll-animations-1/#view-notation\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function = \"view\")]\n#[repr(C)]\npub struct GenericViewFunction<LengthPercent> {\n    /// The axis of scrolling that drives the progress of the timeline.\n    #[css(skip_if = \"ScrollAxis::is_default\")]\n    pub axis: ScrollAxis,\n    /// An adjustment of the view progress visibility range.\n    #[css(skip_if = \"GenericViewTimelineInset::is_auto\")]\n    #[css(field_bound)]\n    pub inset: GenericViewTimelineInset<LengthPercent>,\n}\n\npub use self::GenericViewFunction as ViewFunction;\n\n/// A value for the <single-animation-timeline>.\n///\n/// https://drafts.csswg.org/css-animations-2/#typedef-single-animation-timeline\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericAnimationTimeline<LengthPercent> {\n    /// Use default timeline. The animation’s timeline is a DocumentTimeline.\n    Auto,\n    /// The scroll-timeline name or view-timeline-name.\n    /// This also includes `none` value by using an empty atom.\n    /// https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name\n    /// https://drafts.csswg.org/scroll-animations-1/#view-timeline-name\n    Timeline(TimelineName),\n    /// The scroll() notation.\n    /// https://drafts.csswg.org/scroll-animations-1/#scroll-notation\n    Scroll(ScrollFunction),\n    /// The view() notation.\n    /// https://drafts.csswg.org/scroll-animations-1/#view-notation\n    View(#[css(field_bound)] GenericViewFunction<LengthPercent>),\n}\n\npub use self::GenericAnimationTimeline as AnimationTimeline;\n\nimpl<LengthPercent> AnimationTimeline<LengthPercent> {\n    /// Returns the `auto` value.\n    pub fn auto() -> Self {\n        Self::Auto\n    }\n\n    /// Returns true if it is auto (i.e. the default value).\n    pub fn is_auto(&self) -> bool {\n        matches!(self, Self::Auto)\n    }\n}\n\n/// A generic value for the `[ [ auto | <length-percentage> ]{1,2} ]`.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-inset\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericViewTimelineInset<LengthPercent> {\n    /// The start inset in the relevant axis.\n    pub start: GenericLengthPercentageOrAuto<LengthPercent>,\n    /// The end inset.\n    pub end: GenericLengthPercentageOrAuto<LengthPercent>,\n}\n\npub use self::GenericViewTimelineInset as ViewTimelineInset;\n\nimpl<LengthPercent> ViewTimelineInset<LengthPercent> {\n    /// Returns true if it is auto.\n    #[inline]\n    fn is_auto(&self) -> bool {\n        self.start.is_auto() && self.end.is_auto()\n    }\n}\n\nimpl<LengthPercent> ToCss for ViewTimelineInset<LengthPercent>\nwhere\n    LengthPercent: PartialEq + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.start.to_css(dest)?;\n        if self.end != self.start {\n            dest.write_char(' ')?;\n            self.end.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl<LengthPercent> ToTyped for ViewTimelineInset<LengthPercent> where\n    LengthPercent: PartialEq + ToTyped\n{\n}\n\nimpl<LengthPercent> Default for ViewTimelineInset<LengthPercent> {\n    fn default() -> Self {\n        Self {\n            start: GenericLengthPercentageOrAuto::auto(),\n            end: GenericLengthPercentageOrAuto::auto(),\n        }\n    }\n}\n\n/// A value for animation-range-start or animation-range-end.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#animation-range-start\n/// https://drafts.csswg.org/scroll-animations-1/#animation-range-end\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericAnimationRangeValue<LengthPercent> {\n    /// The specific timeline range. If it is None, the animation range only has length-percentage\n    /// component.\n    pub name: TimelineRangeName,\n    /// Used to measure the specific point from the start of the named timeline.\n    pub lp: LengthPercent,\n}\n\npub use self::GenericAnimationRangeValue as AnimationRangeValue;\n\nimpl<LengthPercent> AnimationRangeValue<LengthPercent> {\n    /// Returns the \"normal\" value\n    #[inline]\n    pub fn normal(lp: LengthPercent) -> Self {\n        Self::new(TimelineRangeName::Normal, lp)\n    }\n\n    /// Returns Self as a LengthPercentage.\n    #[inline]\n    pub fn length_percentage(lp: LengthPercent) -> Self {\n        Self::new(TimelineRangeName::None, lp)\n    }\n\n    /// Returns Self as a tuple of TimelineRangeName range name and LengthPercentage.\n    #[inline]\n    pub fn new(name: TimelineRangeName, lp: LengthPercent) -> Self {\n        Self { name, lp }\n    }\n\n    /// Returns true if it is \"normal\".\n    #[inline]\n    pub fn is_normal(&self) -> bool {\n        self.name.is_normal()\n    }\n}\n\n/// A value for animation-range-start.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#animation-range-start\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericAnimationRangeStart<LengthPercent>(pub GenericAnimationRangeValue<LengthPercent>);\n\npub use self::GenericAnimationRangeStart as AnimationRangeStart;\n\nfn to_css_with_default<LengthPercent, W>(\n    value: &AnimationRangeValue<LengthPercent>,\n    dest: &mut CssWriter<W>,\n    default: f32,\n) -> fmt::Result\nwhere\n    LengthPercent: ToCss + EqualsPercentage,\n    W: Write,\n{\n    if matches!(value.name, TimelineRangeName::Normal) {\n        return dest.write_str(\"normal\");\n    }\n    if matches!(value.name, TimelineRangeName::None) {\n        return value.lp.to_css(dest);\n    }\n    // <timeline-range-name> <length-percentage>?\n    value.name.to_css(dest)?;\n    if !value.lp.equals_percentage(default) {\n        dest.write_char(' ')?;\n        value.lp.to_css(dest)?;\n    }\n    Ok(())\n}\n\nimpl<LengthPercent: ToCss + EqualsPercentage> ToCss for AnimationRangeStart<LengthPercent> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        to_css_with_default(&self.0, dest, 0.0)\n    }\n}\n\n/// A value for animation-range-end.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#animation-range-end\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericAnimationRangeEnd<LengthPercent>(pub GenericAnimationRangeValue<LengthPercent>);\n\npub use self::GenericAnimationRangeEnd as AnimationRangeEnd;\n\nimpl<LengthPercent: ToCss + EqualsPercentage> ToCss for AnimationRangeEnd<LengthPercent> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        to_css_with_default(&self.0, dest, 1.0)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/background.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to backgrounds.\n\nuse crate::derives::*;\nuse crate::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto};\n\n/// A generic value for the `background-size` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericBackgroundSize<LengthPercent> {\n    /// `<width> <height>`\n    ExplicitSize {\n        /// Explicit width.\n        width: GenericLengthPercentageOrAuto<LengthPercent>,\n        /// Explicit height.\n        #[css(contextual_skip_if = \"width_and_height_are_auto\")]\n        #[typed(skip_if = \"GenericLengthPercentageOrAuto::is_auto\")]\n        height: GenericLengthPercentageOrAuto<LengthPercent>,\n    },\n    /// `cover`\n    #[animation(error)]\n    Cover,\n    /// `contain`\n    #[animation(error)]\n    Contain,\n}\n\n#[inline]\nfn width_and_height_are_auto<LengthPercent>(\n    width: &GenericLengthPercentageOrAuto<LengthPercent>,\n    height: &GenericLengthPercentageOrAuto<LengthPercent>,\n) -> bool {\n    width.is_auto() && height.is_auto()\n}\n\npub use self::GenericBackgroundSize as BackgroundSize;\n\nimpl<LengthPercentage> BackgroundSize<LengthPercentage> {\n    /// Returns `auto auto`.\n    pub fn auto() -> Self {\n        GenericBackgroundSize::ExplicitSize {\n            width: LengthPercentageOrAuto::Auto,\n            height: LengthPercentageOrAuto::Auto,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/generics/basic_shape.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the [`basic-shape`](https://drafts.csswg.org/css-shapes/#typedef-basic-shape)\n//! types that are generic over their `ToCss` implementations.\n\nuse crate::derives::*;\nuse crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};\nuse crate::values::computed::Percentage;\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::{\n    border::GenericBorderRadius, position::GenericPositionOrAuto, rect::Rect, NonNegative, Optional,\n};\nuse crate::values::specified::svg_path::{PathCommand, SVGPathData};\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// <https://drafts.fxtf.org/css-masking-1/#typedef-geometry-box>\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ShapeGeometryBox {\n    /// Depending on which kind of element this style value applied on, the\n    /// default value of the reference-box can be different.  For an HTML\n    /// element, the default value of reference-box is border-box; for an SVG\n    /// element, the default value is fill-box.  Since we can not determine the\n    /// default value at parsing time, we keep this value to make a decision on\n    /// it.\n    #[css(skip)]\n    ElementDependent,\n    FillBox,\n    StrokeBox,\n    ViewBox,\n    ShapeBox(ShapeBox),\n}\n\nimpl Default for ShapeGeometryBox {\n    fn default() -> Self {\n        Self::ElementDependent\n    }\n}\n\n/// Skip the serialization if the author omits the box or specifies border-box.\n#[inline]\nfn is_default_box_for_clip_path(b: &ShapeGeometryBox) -> bool {\n    // Note: for clip-path, ElementDependent is always border-box, so we have to check both of them\n    // for serialization.\n    matches!(b, ShapeGeometryBox::ElementDependent)\n        || matches!(b, ShapeGeometryBox::ShapeBox(ShapeBox::BorderBox))\n}\n\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-box\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    ComputeSquaredDistance,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ShapeBox {\n    MarginBox,\n    BorderBox,\n    PaddingBox,\n    ContentBox,\n}\n\nimpl Default for ShapeBox {\n    fn default() -> Self {\n        ShapeBox::MarginBox\n    }\n}\n\n/// A value for the `clip-path` property.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[animation(no_bound(U))]\n#[repr(u8)]\npub enum GenericClipPath<BasicShape, U> {\n    #[animation(error)]\n    None,\n    #[animation(error)]\n    // XXX This will likely change to skip since it seems Typed OM Level 1\n    // won't be updated to cover this case even though there's some preparation\n    // in WPT tests for this.\n    #[typed(todo)]\n    Url(U),\n    #[typed(skip)]\n    Shape(\n        #[animation(field_bound)] Box<BasicShape>,\n        #[css(skip_if = \"is_default_box_for_clip_path\")] ShapeGeometryBox,\n    ),\n    #[animation(error)]\n    Box(ShapeGeometryBox),\n}\n\npub use self::GenericClipPath as ClipPath;\n\n/// A value for the `shape-outside` property.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[animation(no_bound(I))]\n#[repr(u8)]\n#[typed(todo_derive_fields)]\npub enum GenericShapeOutside<BasicShape, I> {\n    #[animation(error)]\n    None,\n    #[animation(error)]\n    Image(I),\n    Shape(Box<BasicShape>, #[css(skip_if = \"is_default\")] ShapeBox),\n    #[animation(error)]\n    Box(ShapeBox),\n}\n\npub use self::GenericShapeOutside as ShapeOutside;\n\n/// The <basic-shape>.\n///\n/// https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect> {\n    /// The <basic-shape-rect>.\n    Rect(BasicShapeRect),\n    /// Defines a circle with a center and a radius.\n    Circle(\n        #[animation(field_bound)]\n        #[css(field_bound)]\n        #[shmem(field_bound)]\n        Circle<Position, LengthPercentage>,\n    ),\n    /// Defines an ellipse with a center and x-axis/y-axis radii.\n    Ellipse(\n        #[animation(field_bound)]\n        #[css(field_bound)]\n        #[shmem(field_bound)]\n        Ellipse<Position, LengthPercentage>,\n    ),\n    /// Defines a polygon with pair arguments.\n    Polygon(GenericPolygon<LengthPercentage>),\n    /// Defines a path() or shape().\n    PathOrShape(\n        #[animation(field_bound)]\n        #[css(field_bound)]\n        #[compute(field_bound)]\n        GenericPathOrShapeFunction<Angle, Position, LengthPercentage>,\n    ),\n}\n\npub use self::GenericBasicShape as BasicShape;\n\n/// <https://drafts.csswg.org/css-shapes/#funcdef-inset>\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function = \"inset\")]\n#[repr(C)]\npub struct GenericInsetRect<LengthPercentage> {\n    pub rect: Rect<LengthPercentage>,\n    #[shmem(field_bound)]\n    #[animation(field_bound)]\n    pub round: GenericBorderRadius<NonNegative<LengthPercentage>>,\n}\n\npub use self::GenericInsetRect as InsetRect;\n\n/// <https://drafts.csswg.org/css-shapes/#funcdef-circle>\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function)]\n#[repr(C)]\npub struct Circle<Position, LengthPercentage> {\n    pub position: GenericPositionOrAuto<Position>,\n    #[animation(field_bound)]\n    pub radius: GenericShapeRadius<LengthPercentage>,\n}\n\n/// <https://drafts.csswg.org/css-shapes/#funcdef-ellipse>\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function)]\n#[repr(C)]\npub struct Ellipse<Position, LengthPercentage> {\n    pub position: GenericPositionOrAuto<Position>,\n    #[animation(field_bound)]\n    pub semiaxis_x: GenericShapeRadius<LengthPercentage>,\n    #[animation(field_bound)]\n    pub semiaxis_y: GenericShapeRadius<LengthPercentage>,\n}\n\n/// <https://drafts.csswg.org/css-shapes/#typedef-shape-radius>\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericShapeRadius<LengthPercentage> {\n    Length(\n        #[animation(field_bound)]\n        #[parse(field_bound)]\n        NonNegative<LengthPercentage>,\n    ),\n    #[animation(error)]\n    ClosestSide,\n    #[animation(error)]\n    FarthestSide,\n}\n\npub use self::GenericShapeRadius as ShapeRadius;\n\n/// A generic type for representing the `polygon()` function\n///\n/// <https://drafts.csswg.org/css-shapes/#funcdef-polygon>\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(comma, function = \"polygon\")]\n#[repr(C)]\npub struct GenericPolygon<LengthPercentage> {\n    /// The filling rule for a polygon.\n    #[css(skip_if = \"is_default\")]\n    pub fill: FillRule,\n    /// A collection of (x, y) coordinates to draw the polygon.\n    #[css(iterable)]\n    pub coordinates: crate::OwnedSlice<PolygonCoord<LengthPercentage>>,\n}\n\npub use self::GenericPolygon as Polygon;\n\n/// Coordinates for Polygon.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);\n\n/// path() function or shape() function.\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericPathOrShapeFunction<Angle, Position, LengthPercentage> {\n    /// Defines a path with SVG path syntax.\n    Path(Path),\n    /// Defines a shape function, which is identical to path() but it uses the CSS syntax.\n    Shape(\n        #[css(field_bound)]\n        #[compute(field_bound)]\n        Shape<Angle, Position, LengthPercentage>,\n    ),\n}\n\n// https://drafts.csswg.org/css-shapes/#typedef-fill-rule\n// NOTE: Basic shapes spec says that these are the only two values, however\n// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty\n// says that it can also be `inherit`\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum FillRule {\n    Nonzero,\n    Evenodd,\n}\n\n/// The path function.\n///\n/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-path\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(comma, function = \"path\")]\n#[repr(C)]\npub struct Path {\n    /// The filling rule for the svg path.\n    #[css(skip_if = \"is_default\")]\n    pub fill: FillRule,\n    /// The svg path data.\n    pub path: SVGPathData,\n}\n\nimpl Path {\n    /// Returns the slice of PathCommand.\n    #[inline]\n    pub fn commands(&self) -> &[PathCommand] {\n        self.path.commands()\n    }\n}\n\nimpl<B, U> ToAnimatedZero for ClipPath<B, U> {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\nimpl<B, U> ToAnimatedZero for ShapeOutside<B, U> {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\nimpl<Length> ToCss for InsetRect<Length>\nwhere\n    Length: ToCss + PartialEq + Zero,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"inset(\")?;\n        self.rect.to_css(dest)?;\n        if !self.round.is_zero() {\n            dest.write_str(\" round \")?;\n            self.round.to_css(dest)?;\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl<Position, LengthPercentage> ToCss for Circle<Position, LengthPercentage>\nwhere\n    LengthPercentage: ToCss + PartialEq,\n    Position: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let has_radius = self.radius != Default::default();\n\n        dest.write_str(\"circle(\")?;\n        if has_radius {\n            self.radius.to_css(dest)?;\n        }\n\n        // Preserve the `at <position>` even if it specified the default value.\n        // https://github.com/w3c/csswg-drafts/issues/8695\n        if !matches!(self.position, GenericPositionOrAuto::Auto) {\n            if has_radius {\n                dest.write_char(' ')?;\n            }\n            dest.write_str(\"at \")?;\n            self.position.to_css(dest)?;\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl<Position, LengthPercentage> ToCss for Ellipse<Position, LengthPercentage>\nwhere\n    LengthPercentage: ToCss + PartialEq,\n    Position: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let has_radii =\n            self.semiaxis_x != Default::default() || self.semiaxis_y != Default::default();\n\n        dest.write_str(\"ellipse(\")?;\n        if has_radii {\n            self.semiaxis_x.to_css(dest)?;\n            dest.write_char(' ')?;\n            self.semiaxis_y.to_css(dest)?;\n        }\n\n        // Preserve the `at <position>` even if it specified the default value.\n        // https://github.com/w3c/csswg-drafts/issues/8695\n        if !matches!(self.position, GenericPositionOrAuto::Auto) {\n            if has_radii {\n                dest.write_char(' ')?;\n            }\n            dest.write_str(\"at \")?;\n            self.position.to_css(dest)?;\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl<L> Default for ShapeRadius<L> {\n    #[inline]\n    fn default() -> Self {\n        ShapeRadius::ClosestSide\n    }\n}\n\nimpl<L> Animate for Polygon<L>\nwhere\n    L: Animate,\n{\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.fill != other.fill {\n            return Err(());\n        }\n        let coordinates =\n            lists::by_computed_value::animate(&self.coordinates, &other.coordinates, procedure)?;\n        Ok(Polygon {\n            fill: self.fill,\n            coordinates,\n        })\n    }\n}\n\nimpl<L> ComputeSquaredDistance for Polygon<L>\nwhere\n    L: ComputeSquaredDistance,\n{\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        if self.fill != other.fill {\n            return Err(());\n        }\n        lists::by_computed_value::squared_distance(&self.coordinates, &other.coordinates)\n    }\n}\n\nimpl Default for FillRule {\n    #[inline]\n    fn default() -> Self {\n        FillRule::Nonzero\n    }\n}\n\n#[inline]\nfn is_default<T: Default + PartialEq>(fill: &T) -> bool {\n    *fill == Default::default()\n}\n\n/// The shape function defined in css-shape-2.\n/// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)\n///\n/// https://drafts.csswg.org/css-shapes-2/#shape-function\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct Shape<Angle, Position, LengthPercentage> {\n    /// The filling rule for this shape.\n    pub fill: FillRule,\n    /// The shape command data. Note that the starting point will be the first command in this\n    /// slice.\n    // Note: The first command is always GenericShapeCommand::Move.\n    #[compute(field_bound)]\n    pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, Position, LengthPercentage>>,\n}\n\nimpl<Angle, Position, LengthPercentage> Shape<Angle, Position, LengthPercentage> {\n    /// Returns the slice of GenericShapeCommand<..>.\n    #[inline]\n    pub fn commands(&self) -> &[GenericShapeCommand<Angle, Position, LengthPercentage>] {\n        &self.commands\n    }\n}\n\nimpl<Angle, Position, LengthPercentage> Animate for Shape<Angle, Position, LengthPercentage>\nwhere\n    Angle: Animate,\n    Position: Animate,\n    LengthPercentage: Animate,\n{\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.fill != other.fill {\n            return Err(());\n        }\n        let commands =\n            lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?;\n        Ok(Self {\n            fill: self.fill,\n            commands,\n        })\n    }\n}\n\nimpl<Angle, Position, LengthPercentage> ComputeSquaredDistance\n    for Shape<Angle, Position, LengthPercentage>\nwhere\n    Angle: ComputeSquaredDistance,\n    Position: ComputeSquaredDistance,\n    LengthPercentage: ComputeSquaredDistance,\n{\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        if self.fill != other.fill {\n            return Err(());\n        }\n        lists::by_computed_value::squared_distance(&self.commands, &other.commands)\n    }\n}\n\nimpl<Angle, Position, LengthPercentage> ToCss for Shape<Angle, Position, LengthPercentage>\nwhere\n    Angle: ToCss + Zero,\n    Position: ToCss,\n    LengthPercentage: PartialEq + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        use style_traits::values::SequenceWriter;\n\n        // Per spec, we must have the first move command and at least one following command.\n        debug_assert!(self.commands.len() > 1);\n\n        dest.write_str(\"shape(\")?;\n        if !is_default(&self.fill) {\n            self.fill.to_css(dest)?;\n            dest.write_char(' ')?;\n        }\n        dest.write_str(\"from \")?;\n        match &self.commands[0] {\n            ShapeCommand::Move {\n                point: CommandEndPoint::ToPosition(pos),\n            } => pos.to_css(dest)?,\n            ShapeCommand::Move {\n                point: CommandEndPoint::ByCoordinate(coord),\n            } => coord.to_css(dest)?,\n            _ => unreachable!(\"The first command must be move\"),\n        }\n        dest.write_str(\", \")?;\n        {\n            let mut writer = SequenceWriter::new(dest, \", \");\n            for command in self.commands.iter().skip(1) {\n                writer.item(command)?;\n            }\n        }\n        dest.write_char(')')\n    }\n}\n\n/// This is a more general shape(path) command type, for both shape() and path().\n///\n/// https://www.w3.org/TR/SVG11/paths.html#PathData\n/// https://drafts.csswg.org/css-shapes-2/#shape-function\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(C, u8)]\npub enum GenericShapeCommand<Angle, Position, LengthPercentage> {\n    /// The move command.\n    Move {\n        point: CommandEndPoint<Position, LengthPercentage>,\n    },\n    /// The line command.\n    Line {\n        point: CommandEndPoint<Position, LengthPercentage>,\n    },\n    /// The hline command.\n    HLine {\n        #[compute(field_bound)]\n        x: AxisEndPoint<LengthPercentage>,\n    },\n    /// The vline command.\n    VLine {\n        #[compute(field_bound)]\n        y: AxisEndPoint<LengthPercentage>,\n    },\n    /// The cubic Bézier curve command.\n    CubicCurve {\n        point: CommandEndPoint<Position, LengthPercentage>,\n        control1: ControlPoint<Position, LengthPercentage>,\n        control2: ControlPoint<Position, LengthPercentage>,\n    },\n    /// The quadratic Bézier curve command.\n    QuadCurve {\n        point: CommandEndPoint<Position, LengthPercentage>,\n        control1: ControlPoint<Position, LengthPercentage>,\n    },\n    /// The smooth command.\n    SmoothCubic {\n        point: CommandEndPoint<Position, LengthPercentage>,\n        control2: ControlPoint<Position, LengthPercentage>,\n    },\n    /// The smooth quadratic Bézier curve command.\n    SmoothQuad {\n        point: CommandEndPoint<Position, LengthPercentage>,\n    },\n    /// The arc command.\n    Arc {\n        point: CommandEndPoint<Position, LengthPercentage>,\n        radii: ArcRadii<LengthPercentage>,\n        arc_sweep: ArcSweep,\n        arc_size: ArcSize,\n        rotate: Angle,\n    },\n    /// The closepath command.\n    Close,\n}\n\npub use self::GenericShapeCommand as ShapeCommand;\n\nimpl<Angle, Position, LengthPercentage> ToCss for ShapeCommand<Angle, Position, LengthPercentage>\nwhere\n    Angle: ToCss + Zero,\n    Position: ToCss,\n    LengthPercentage: PartialEq + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use self::ShapeCommand::*;\n        match *self {\n            Move { ref point } => {\n                dest.write_str(\"move \")?;\n                point.to_css(dest)\n            },\n            Line { ref point } => {\n                dest.write_str(\"line \")?;\n                point.to_css(dest)\n            },\n            HLine { ref x } => {\n                dest.write_str(\"hline \")?;\n                x.to_css(dest)\n            },\n            VLine { ref y } => {\n                dest.write_str(\"vline \")?;\n                y.to_css(dest)\n            },\n            CubicCurve {\n                ref point,\n                ref control1,\n                ref control2,\n            } => {\n                dest.write_str(\"curve \")?;\n                point.to_css(dest)?;\n                dest.write_str(\" with \")?;\n                control1.to_css(dest, point.is_abs())?;\n                dest.write_char(' ')?;\n                dest.write_char('/')?;\n                dest.write_char(' ')?;\n                control2.to_css(dest, point.is_abs())\n            },\n            QuadCurve {\n                ref point,\n                ref control1,\n            } => {\n                dest.write_str(\"curve \")?;\n                point.to_css(dest)?;\n                dest.write_str(\" with \")?;\n                control1.to_css(dest, point.is_abs())\n            },\n            SmoothCubic {\n                ref point,\n                ref control2,\n            } => {\n                dest.write_str(\"smooth \")?;\n                point.to_css(dest)?;\n                dest.write_str(\" with \")?;\n                control2.to_css(dest, point.is_abs())\n            },\n            SmoothQuad { ref point } => {\n                dest.write_str(\"smooth \")?;\n                point.to_css(dest)\n            },\n            Arc {\n                ref point,\n                ref radii,\n                arc_sweep,\n                arc_size,\n                ref rotate,\n            } => {\n                dest.write_str(\"arc \")?;\n                point.to_css(dest)?;\n                dest.write_str(\" of \")?;\n                radii.to_css(dest)?;\n\n                if matches!(arc_sweep, ArcSweep::Cw) {\n                    dest.write_str(\" cw\")?;\n                }\n\n                if matches!(arc_size, ArcSize::Large) {\n                    dest.write_str(\" large\")?;\n                }\n\n                if !rotate.is_zero() {\n                    dest.write_str(\" rotate \")?;\n                    rotate.to_css(dest)?;\n                }\n                Ok(())\n            },\n            Close => dest.write_str(\"close\"),\n        }\n    }\n}\n\n/// Defines the end point of the command, which can be specified in absolute or relative coordinates,\n/// determined by their \"to\" or \"by\" components respectively.\n/// https://drafts.csswg.org/css-shapes/#typedef-shape-command-end-point\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum CommandEndPoint<Position, LengthPercentage> {\n    ToPosition(Position),\n    ByCoordinate(CoordinatePair<LengthPercentage>),\n}\n\nimpl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {\n    /// Return true if it is absolute, i.e. it is To.\n    #[inline]\n    pub fn is_abs(&self) -> bool {\n        matches!(self, CommandEndPoint::ToPosition(_))\n    }\n}\n\nimpl<Position, LengthPercentage> CommandEndPoint<Position, LengthPercentage> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n        Position: ToCss,\n        LengthPercentage: ToCss,\n    {\n        match self {\n            CommandEndPoint::ToPosition(pos) => {\n                dest.write_str(\"to \")?;\n                pos.to_css(dest)\n            },\n            CommandEndPoint::ByCoordinate(coord) => {\n                dest.write_str(\"by \")?;\n                coord.to_css(dest)\n            },\n        }\n    }\n}\n\n/// Defines the end point for the commands <horizontal-line-command> and <vertical-line-command>, which\n/// can be specified in absolute or relative values, determined by their \"to\" or \"by\" components respectively.\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum AxisEndPoint<LengthPercentage> {\n    ToPosition(#[compute(field_bound)] AxisPosition<LengthPercentage>),\n    ByCoordinate(LengthPercentage),\n}\n\nimpl<LengthPercentage> AxisEndPoint<LengthPercentage> {\n    /// Return true if it is absolute, i.e. it is To.\n    #[inline]\n    pub fn is_abs(&self) -> bool {\n        matches!(self, AxisEndPoint::ToPosition(_))\n    }\n}\n\nimpl<LengthPercentage: ToCss> ToCss for AxisEndPoint<LengthPercentage> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_abs() {\n            dest.write_str(\"to \")?;\n        } else {\n            dest.write_str(\"by \")?;\n        }\n        match self {\n            AxisEndPoint::ToPosition(pos) => pos.to_css(dest),\n            AxisEndPoint::ByCoordinate(coord) => coord.to_css(dest),\n        }\n    }\n}\n\n/// Defines how the absolutely positioned end point for <horizontal-line-command> and\n/// <vertical-line-command> is positioned.\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum AxisPosition<LengthPercentage> {\n    LengthPercent(LengthPercentage),\n    Keyword(AxisPositionKeyword),\n}\n\n/// The set of position keywords used in <horizontal-line-command> and <vertical-line-command>\n/// for absolute positioning. Note: this is the shared union list between hline and vline, so\n/// not every value is valid for either. I.e. hline cannot be positioned with top or y-start.\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-horizontal-line-command\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-vertical-line-command\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum AxisPositionKeyword {\n    Center,\n    Left,\n    Right,\n    Top,\n    Bottom,\n    XStart,\n    XEnd,\n    YStart,\n    YEnd,\n}\n\nimpl AxisPositionKeyword {\n    /// Returns the axis position keyword as its corresponding percentage.\n    #[inline]\n    pub fn as_percentage(&self) -> Percentage {\n        match self {\n            Self::Center => Percentage(0.5),\n            Self::Left | Self::Top | Self::XStart | Self::YStart => Percentage(0.),\n            Self::Right | Self::Bottom | Self::XEnd | Self::YEnd => Percentage(1.),\n        }\n    }\n}\n\n/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from\n/// a specified reference point. Percentages are resolved against the width or height,\n/// respectively, of the reference box.\n/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair\n#[allow(missing_docs)]\n#[derive(\n    AddAssign,\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct CoordinatePair<LengthPercentage> {\n    pub x: LengthPercentage,\n    pub y: LengthPercentage,\n}\n\nimpl<LengthPercentage> CoordinatePair<LengthPercentage> {\n    /// Create a CoordinatePair.\n    #[inline]\n    pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {\n        Self { x, y }\n    }\n}\n\n/// Defines a control point for a quadratic or cubic Bézier curve, which can be specified\n/// in absolute or relative coordinates.\n/// https://drafts.csswg.org/css-shapes/#typedef-shape-control-point\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum ControlPoint<Position, LengthPercentage> {\n    Absolute(Position),\n    Relative(RelativeControlPoint<LengthPercentage>),\n}\n\nimpl<Position, LengthPercentage> ControlPoint<Position, LengthPercentage> {\n    /// Serialize <control-point>\n    pub fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result\n    where\n        W: Write,\n        Position: ToCss,\n        LengthPercentage: ToCss,\n    {\n        match self {\n            ControlPoint::Absolute(pos) => pos.to_css(dest),\n            ControlPoint::Relative(point) => point.to_css(dest, is_end_point_abs),\n        }\n    }\n}\n\n/// Defines a relative control point to a quadratic or cubic Bézier curve, dependent on the\n/// reference value. The default `None` is to be relative to the command’s starting point.\n/// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct RelativeControlPoint<LengthPercentage> {\n    pub coord: CoordinatePair<LengthPercentage>,\n    pub reference: ControlReference,\n}\n\nimpl<LengthPercentage: ToCss> RelativeControlPoint<LengthPercentage> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>, is_end_point_abs: bool) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.coord.to_css(dest)?;\n        match self.reference {\n            ControlReference::Origin if is_end_point_abs => Ok(()),\n            ControlReference::Start if !is_end_point_abs => Ok(()),\n            other => {\n                dest.write_str(\" from \")?;\n                other.to_css(dest)\n            },\n        }\n    }\n}\n\nimpl<LengthPercentage: ComputeSquaredDistance> ComputeSquaredDistance\n    for RelativeControlPoint<LengthPercentage>\n{\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        self.coord.compute_squared_distance(&other.coord)\n    }\n}\n\n/// Defines the point of reference for a <relative-control-point>.\n///\n/// When a reference is not specified, depending on whether the associated\n/// <command-end-point> is absolutely or relatively positioned, the default\n/// will be `Origin` or `Start`, respectively.\n/// https://drafts.csswg.org/css-shapes/#typedef-shape-relative-control-point\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub enum ControlReference {\n    Start,\n    End,\n    Origin,\n}\n\n/// Defines the radiuses for an <arc-command>.\n///\n/// The first <length-percentage> is the ellipse's horizontal radius, and the second is\n/// the vertical radius. If only one value is provided, it is used for both radii, and any\n/// <percentage> is resolved against the direction-agnostic size of the reference box.\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-arc-command\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct ArcRadii<LengthPercentage> {\n    pub rx: LengthPercentage,\n    pub ry: Optional<LengthPercentage>,\n}\n\n/// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise\n/// from the center.\n/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-sweep\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ArcSweep {\n    /// Counter-clockwise. The default value. (This also represents 0 in the svg path.)\n    Ccw = 0,\n    /// Clockwise. (This also represents 1 in the svg path.)\n    Cw = 1,\n}\n\nimpl Animate for ArcSweep {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use num_traits::FromPrimitive;\n        // If an arc command has different <arc-sweep> between its starting and ending list, then\n        // the interpolated result uses cw for any progress value between 0 and 1.\n        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.\n        let progress = procedure.weights().1 as f32 as f64;\n        let procedure = Procedure::Interpolate { progress };\n        (*self as i32 as f32)\n            .animate(&(*other as i32 as f32), procedure)\n            .map(|v| ArcSweep::from_u8((v > 0.) as u8).unwrap_or(ArcSweep::Ccw))\n    }\n}\n\nimpl ComputeSquaredDistance for ArcSweep {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        (*self as i32).compute_squared_distance(&(*other as i32))\n    }\n}\n\n/// This indicates that the larger or smaller, respectively, of the two possible arcs must be\n/// chosen.\n/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-size\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ArcSize {\n    /// Choose the small one. The default value. (This also represents 0 in the svg path.)\n    Small = 0,\n    /// Choose the large one. (This also represents 1 in the svg path.)\n    Large = 1,\n}\n\nimpl Animate for ArcSize {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        use num_traits::FromPrimitive;\n        // If it has different <arc-size> keywords, then the interpolated result uses large for any\n        // progress value between 0 and 1.\n        // Note: we cast progress from f64->f32->f64 to drop tiny noise near 0.0.\n        let progress = procedure.weights().1 as f32 as f64;\n        let procedure = Procedure::Interpolate { progress };\n        (*self as i32 as f32)\n            .animate(&(*other as i32 as f32), procedure)\n            .map(|v| ArcSize::from_u8((v > 0.) as u8).unwrap_or(ArcSize::Small))\n    }\n}\n\nimpl ComputeSquaredDistance for ArcSize {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        (*self as i32).compute_squared_distance(&(*other as i32))\n    }\n}\n"
  },
  {
    "path": "style/values/generics/border.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to borders.\n\nuse crate::derives::*;\nuse crate::values::generics::rect::Rect;\nuse crate::values::generics::size::Size2D;\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A generic value for a single side of a `border-image-width` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericBorderImageSideWidth<LP, N> {\n    /// `<number>`\n    ///\n    /// NOTE: Numbers need to be before length-percentagess, in order to parse\n    /// them first, since `0` should be a number, not the `0px` length.\n    Number(N),\n    /// `<length-or-percentage>`\n    LengthPercentage(LP),\n    /// `auto`\n    Auto,\n}\n\npub use self::GenericBorderImageSideWidth as BorderImageSideWidth;\n\n/// A generic value for the `border-image-slice` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericBorderImageSlice<NumberOrPercentage> {\n    /// The offsets.\n    #[css(field_bound)]\n    pub offsets: Rect<NumberOrPercentage>,\n    /// Whether to fill the middle part.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub fill: bool,\n}\n\npub use self::GenericBorderImageSlice as BorderImageSlice;\n\n/// A generic value for the `border-*-radius` longhand properties.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    Serialize,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericBorderCornerRadius<L>(\n    #[css(field_bound)]\n    #[shmem(field_bound)]\n    pub Size2D<L>,\n);\n\npub use self::GenericBorderCornerRadius as BorderCornerRadius;\n\nimpl<L> BorderCornerRadius<L> {\n    /// Trivially create a `BorderCornerRadius`.\n    pub fn new(w: L, h: L) -> Self {\n        BorderCornerRadius(Size2D::new(w, h))\n    }\n}\n\nimpl<L: Zero> Zero for BorderCornerRadius<L> {\n    fn zero() -> Self {\n        BorderCornerRadius(Size2D::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        self.0.is_zero()\n    }\n}\n\n/// A generic value for the `border-spacing` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericBorderSpacing<L>(\n    #[css(field_bound)]\n    #[shmem(field_bound)]\n    pub Size2D<L>,\n);\n\npub use self::GenericBorderSpacing as BorderSpacing;\nimpl<L> BorderSpacing<L> {\n    /// Trivially create a `BorderCornerRadius`.\n    pub fn new(w: L, h: L) -> Self {\n        BorderSpacing(Size2D::new(w, h))\n    }\n}\n\n/// A generic value for `border-radius` and `inset()`.\n///\n/// <https://drafts.csswg.org/css-backgrounds-3/#border-radius>\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    Serialize,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericBorderRadius<LengthPercentage> {\n    /// The top left radius.\n    #[shmem(field_bound)]\n    pub top_left: GenericBorderCornerRadius<LengthPercentage>,\n    /// The top right radius.\n    pub top_right: GenericBorderCornerRadius<LengthPercentage>,\n    /// The bottom right radius.\n    pub bottom_right: GenericBorderCornerRadius<LengthPercentage>,\n    /// The bottom left radius.\n    pub bottom_left: GenericBorderCornerRadius<LengthPercentage>,\n}\n\npub use self::GenericBorderRadius as BorderRadius;\n\nimpl<L> BorderRadius<L> {\n    /// Returns a new `BorderRadius<L>`.\n    #[inline]\n    pub fn new(\n        tl: BorderCornerRadius<L>,\n        tr: BorderCornerRadius<L>,\n        br: BorderCornerRadius<L>,\n        bl: BorderCornerRadius<L>,\n    ) -> Self {\n        BorderRadius {\n            top_left: tl,\n            top_right: tr,\n            bottom_right: br,\n            bottom_left: bl,\n        }\n    }\n\n    /// Serialises two given rects following the syntax of the `border-radius``\n    /// property.\n    pub fn serialize_rects<W>(\n        widths: Rect<&L>,\n        heights: Rect<&L>,\n        dest: &mut CssWriter<W>,\n    ) -> fmt::Result\n    where\n        L: PartialEq + ToCss,\n        W: Write,\n    {\n        widths.to_css(dest)?;\n        if widths != heights {\n            dest.write_str(\" / \")?;\n            heights.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl<L: Zero> Zero for BorderRadius<L> {\n    fn zero() -> Self {\n        Self::new(\n            BorderCornerRadius::<L>::zero(),\n            BorderCornerRadius::<L>::zero(),\n            BorderCornerRadius::<L>::zero(),\n            BorderCornerRadius::<L>::zero(),\n        )\n    }\n\n    fn is_zero(&self) -> bool {\n        self.top_left.is_zero()\n            && self.top_right.is_zero()\n            && self.bottom_right.is_zero()\n            && self.bottom_left.is_zero()\n    }\n}\n\nimpl<L> ToCss for BorderRadius<L>\nwhere\n    L: PartialEq + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let BorderRadius {\n            top_left: BorderCornerRadius(ref tl),\n            top_right: BorderCornerRadius(ref tr),\n            bottom_right: BorderCornerRadius(ref br),\n            bottom_left: BorderCornerRadius(ref bl),\n        } = *self;\n\n        let widths = Rect::new(&tl.width, &tr.width, &br.width, &bl.width);\n        let heights = Rect::new(&tl.height, &tr.height, &br.height, &bl.height);\n\n        Self::serialize_rects(widths, heights, dest)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/box.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for box properties.\n\nuse crate::derives::*;\nuse crate::values::animated::ToAnimatedZero;\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum BaselineShiftKeyword {\n    /// Lower by the offset appropriate for subscripts of the parent’s box. The UA may use the\n    /// parent’s font metrics to find this offset; otherwise it defaults to dropping by one\n    /// fifth of the parent’s used font-size.\n    Sub,\n    /// Raise by the offset appropriate for superscripts of the parent’s box. The UA may use the\n    /// parent’s font metrics to find this offset; otherwise it defaults to raising by one third\n    /// of the parent’s used font-size.\n    Super,\n    /// Align the line-over edge of the aligned subtree with the line-over edge of the line box.\n    Top,\n    /// Align the center of the aligned subtree with the center of the line box.\n    Center,\n    /// Align the line-under edge of the aligned subtree with the line-under edge of the line box.\n    Bottom,\n}\n\n/// A generic value for the `baseline-shift` property.\n/// https://drafts.csswg.org/css-inline-3/#baseline-shift\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericBaselineShift<LengthPercentage> {\n    /// One of the baseline-shift keywords\n    Keyword(BaselineShiftKeyword),\n    /// Raise (positive value) or lower (negative value) by the specified length or specified percentage of the line-height.\n    Length(LengthPercentage),\n}\n\npub use self::GenericBaselineShift as BaselineShift;\n\nimpl<L: Zero> BaselineShift<L> {\n    /// Returns the initial `0` value.\n    #[inline]\n    pub fn zero() -> Self {\n        BaselineShift::Length(Zero::zero())\n    }\n}\n\nimpl<L> ToAnimatedZero for BaselineShift<L> {\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\n/// https://drafts.csswg.org/css-sizing-4/#intrinsic-size-override\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[value_info(other_values = \"auto\")]\n#[repr(C, u8)]\npub enum GenericContainIntrinsicSize<L> {\n    /// The keyword `none`.\n    None,\n    /// The keywords 'auto none',\n    AutoNone,\n    /// A non-negative length.\n    Length(L),\n    /// \"auto <Length>\"\n    AutoLength(L),\n}\n\npub use self::GenericContainIntrinsicSize as ContainIntrinsicSize;\n\nimpl<L: ToCss> ToCss for ContainIntrinsicSize<L> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Self::None => dest.write_str(\"none\"),\n            Self::AutoNone => dest.write_str(\"auto none\"),\n            Self::Length(ref l) => l.to_css(dest),\n            Self::AutoLength(ref l) => {\n                dest.write_str(\"auto \")?;\n                l.to_css(dest)\n            },\n        }\n    }\n}\n\n/// Note that we only implement -webkit-line-clamp as a single, longhand\n/// property for now, but the spec defines line-clamp as a shorthand for\n/// separate max-lines, block-ellipsis, and continue properties.\n///\n/// https://drafts.csswg.org/css-overflow-3/#line-clamp\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[value_info(other_values = \"none\")]\npub struct GenericLineClamp<I>(pub I);\n\npub use self::GenericLineClamp as LineClamp;\n\nimpl<I: Zero> LineClamp<I> {\n    /// Returns the `none` value.\n    pub fn none() -> Self {\n        Self(crate::Zero::zero())\n    }\n\n    /// Returns whether we're the `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_zero()\n    }\n}\n\nimpl<I: Zero + ToCss> ToCss for LineClamp<I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_none() {\n            return dest.write_str(\"none\");\n        }\n        self.0.to_css(dest)\n    }\n}\n\n/// A generic value for the `perspective` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericPerspective<NonNegativeLength> {\n    /// A non-negative length.\n    Length(NonNegativeLength),\n    /// The keyword `none`.\n    None,\n}\n\npub use self::GenericPerspective as Perspective;\n\nimpl<L> Perspective<L> {\n    /// Returns `none`.\n    #[inline]\n    pub fn none() -> Self {\n        Perspective::None\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum PositionProperty {\n    Static = 0,\n    Relative,\n    Absolute,\n    Fixed,\n    Sticky,\n}\n\nimpl PositionProperty {\n    /// Is the box absolutely positioned?\n    pub fn is_absolutely_positioned(self) -> bool {\n        matches!(self, Self::Absolute | Self::Fixed)\n    }\n}\n\n/// https://drafts.csswg.org/css-overflow-4/#overflow-clip-margin's <visual-box>. Note that the\n/// spec has special behavior for the omitted keyword, but that's rather odd, see:\n/// https://github.com/w3c/csswg-drafts/issues/13185\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum OverflowClipMarginBox {\n    ContentBox,\n    PaddingBox,\n    BorderBox,\n}\n\n/// https://drafts.csswg.org/css-overflow-4/#overflow-clip-margin\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\npub struct GenericOverflowClipMargin<L> {\n    /// The offset of the clip.\n    pub offset: L,\n    /// The box that we're clipping to.\n    #[animation(constant)]\n    pub visual_box: OverflowClipMarginBox,\n}\n\npub use self::GenericOverflowClipMargin as OverflowClipMargin;\n\nimpl<L: Zero> GenericOverflowClipMargin<L> {\n    /// Returns the `none` value.\n    pub fn zero() -> Self {\n        Self {\n            offset: Zero::zero(),\n            visual_box: OverflowClipMarginBox::PaddingBox,\n        }\n    }\n}\n\nimpl<L: Zero + ToCss> ToCss for OverflowClipMargin<L> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.visual_box == OverflowClipMarginBox::PaddingBox {\n            return self.offset.to_css(dest);\n        }\n        self.visual_box.to_css(dest)?;\n        if !self.offset.is_zero() {\n            dest.write_char(' ')?;\n            self.offset.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "style/values/generics/calc.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [Calc expressions][calc].\n//!\n//! [calc]: https://drafts.csswg.org/css-values/#calc-notation\n\nuse crate::derives::*;\nuse crate::values::generics::length::GenericAnchorSizeFunction;\nuse crate::values::generics::position::{GenericAnchorFunction, GenericAnchorSide};\nuse num_traits::Zero;\nuse smallvec::SmallVec;\nuse std::convert::AsRef;\nuse std::fmt::{self, Write};\nuse std::ops::{Add, Mul, Neg, Rem, Sub};\nuse std::{cmp, mem};\nuse strum_macros::AsRefStr;\nuse style_traits::{CssWriter, MathSum, NumericValue, ToCss, ToTyped, TypedValue};\n\nuse thin_vec::ThinVec;\n\n/// Whether we're a `min` or `max` function.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum MinMaxOp {\n    /// `min()`\n    Min,\n    /// `max()`\n    Max,\n}\n\n/// Whether we're a `mod` or `rem` function.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ModRemOp {\n    /// `mod()`\n    Mod,\n    /// `rem()`\n    Rem,\n}\n\nimpl ModRemOp {\n    fn apply(self, dividend: f32, divisor: f32) -> f32 {\n        // In mod(A, B) only, if B is infinite and A has opposite sign to B\n        // (including an oppositely-signed zero), the result is NaN.\n        // https://drafts.csswg.org/css-values/#round-infinities\n        if matches!(self, Self::Mod)\n            && divisor.is_infinite()\n            && dividend.is_sign_negative() != divisor.is_sign_negative()\n        {\n            return f32::NAN;\n        }\n\n        let (r, same_sign_as) = match self {\n            Self::Mod => (dividend - divisor * (dividend / divisor).floor(), divisor),\n            Self::Rem => (dividend - divisor * (dividend / divisor).trunc(), dividend),\n        };\n        if r == 0.0 && same_sign_as.is_sign_negative() {\n            -0.0\n        } else {\n            r\n        }\n    }\n}\n\n/// The strategy used in `round()`\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum RoundingStrategy {\n    /// `round(nearest, a, b)`\n    /// round a to the nearest multiple of b\n    Nearest,\n    /// `round(up, a, b)`\n    /// round a up to the nearest multiple of b\n    Up,\n    /// `round(down, a, b)`\n    /// round a down to the nearest multiple of b\n    Down,\n    /// `round(to-zero, a, b)`\n    /// round a to the nearest multiple of b that is towards zero\n    ToZero,\n}\n\n/// This determines the order in which we serialize members of a calc() sum.\n///\n/// See https://drafts.csswg.org/css-values-4/#sort-a-calculations-children\n#[derive(\n    AsRefStr, Clone, Copy, Debug, Eq, Ord, Parse, PartialEq, PartialOrd, MallocSizeOf, ToShmem,\n)]\n#[strum(serialize_all = \"lowercase\")]\n#[allow(missing_docs)]\npub enum SortKey {\n    #[strum(serialize = \"\")]\n    Number,\n    #[css(skip)]\n    #[strum(serialize = \"%\")]\n    Percentage,\n    Cap,\n    Ch,\n    Cqb,\n    Cqh,\n    Cqi,\n    Cqmax,\n    Cqmin,\n    Cqw,\n    Deg,\n    Dppx,\n    Dvb,\n    Dvh,\n    Dvi,\n    Dvmax,\n    Dvmin,\n    Dvw,\n    Em,\n    Ex,\n    Ic,\n    Lh,\n    Lvb,\n    Lvh,\n    Lvi,\n    Lvmax,\n    Lvmin,\n    Lvw,\n    Ms,\n    Px,\n    Rcap,\n    Rch,\n    Rem,\n    Rex,\n    Ric,\n    Rlh,\n    S, // Sec\n    Svb,\n    Svh,\n    Svi,\n    Svmax,\n    Svmin,\n    Svw,\n    Vb,\n    Vh,\n    Vi,\n    Vmax,\n    Vmin,\n    Vw,\n    #[css(skip)]\n    ColorComponent,\n    #[css(skip)]\n    Other,\n}\n\n/// Fallback type for anchor functions within `calc()`.\n/// Ideally, the fallback type is initial type of the property (e.g.\n/// `GenericInset` for `left`), but that causes circular reference.\n/// TODO(dshin, bug 2034100): Investigate ways to not require this.\n/// This handles the parsing of unitless zeros, as well as ensuring\n/// that e.g. `calc(anchor(--foo left, 1px) + 10%)` round trips\n/// (sorting aside), instead of becoming\n/// `calc(anchor(--foo left, calc(1px)) + 10%)`.\n#[repr(C)]\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct GenericAnchorFunctionFallback<L> {\n    /// Was this node parsed as a calc node?\n    #[animation(constant)]\n    is_calc_node: bool,\n    /// The parsed fallback value. Stored as a calc node to break\n    /// the circular reference.\n    pub node: GenericCalcNode<L>,\n}\n\nimpl<L> GenericAnchorFunctionFallback<L> {\n    /// Create a new anchor function fallback value.\n    pub fn new(is_calc_node: bool, node: GenericCalcNode<L>) -> Self {\n        Self {\n            is_calc_node,\n            node,\n        }\n    }\n}\n\nimpl<L: CalcNodeLeaf> ToCss for GenericAnchorFunctionFallback<L> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.node.to_css_impl(\n            dest,\n            if self.is_calc_node {\n                ArgumentLevel::CalculationRoot\n            } else {\n                ArgumentLevel::ArgumentRoot\n            },\n        )\n    }\n}\n\n/// `anchor()` function used in math functions.\npub type GenericCalcAnchorFunction<L> =\n    GenericAnchorFunction<Box<GenericCalcNode<L>>, Box<GenericAnchorFunctionFallback<L>>>;\n/// `anchor-size()` function used in math functions.\npub type GenericCalcAnchorSizeFunction<L> =\n    GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<L>>>;\n\n/// A generic node in a calc expression.\n///\n/// FIXME: This would be much more elegant if we used `Self` in the types below,\n/// but we can't because of https://github.com/serde-rs/serde/issues/1565.\n///\n/// FIXME: The following annotations are to workaround an LLVM inlining bug, see\n/// bug 1631929.\n///\n/// cbindgen:destructor-attributes=MOZ_NEVER_INLINE\n/// cbindgen:copy-constructor-attributes=MOZ_NEVER_INLINE\n/// cbindgen:eq-attributes=MOZ_NEVER_INLINE\n#[repr(u8)]\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\npub enum GenericCalcNode<L> {\n    /// A leaf node.\n    Leaf(L),\n    /// A node that negates its child, e.g. Negate(1) == -1.\n    Negate(Box<GenericCalcNode<L>>),\n    /// A node that inverts its child, e.g. Invert(10) == 1 / 10 == 0.1. The child must always\n    /// resolve to a number unit.\n    Invert(Box<GenericCalcNode<L>>),\n    /// A sum node, representing `a + b + c` where a, b, and c are the\n    /// arguments.\n    Sum(crate::OwnedSlice<GenericCalcNode<L>>),\n    /// A product node, representing `a * b * c` where a, b, and c are the\n    /// arguments.\n    Product(crate::OwnedSlice<GenericCalcNode<L>>),\n    /// A `min` or `max` function.\n    MinMax(crate::OwnedSlice<GenericCalcNode<L>>, MinMaxOp),\n    /// A `clamp()` function.\n    Clamp {\n        /// The minimum value.\n        min: Box<GenericCalcNode<L>>,\n        /// The central value.\n        center: Box<GenericCalcNode<L>>,\n        /// The maximum value.\n        max: Box<GenericCalcNode<L>>,\n    },\n    /// A `round()` function.\n    Round {\n        /// The rounding strategy.\n        strategy: RoundingStrategy,\n        /// The value to round.\n        value: Box<GenericCalcNode<L>>,\n        /// The step value.\n        step: Box<GenericCalcNode<L>>,\n    },\n    /// A `mod()` or `rem()` function.\n    ModRem {\n        /// The dividend calculation.\n        dividend: Box<GenericCalcNode<L>>,\n        /// The divisor calculation.\n        divisor: Box<GenericCalcNode<L>>,\n        /// Is the function mod or rem?\n        op: ModRemOp,\n    },\n    /// A `hypot()` function\n    Hypot(crate::OwnedSlice<GenericCalcNode<L>>),\n    /// An `abs()` function.\n    Abs(Box<GenericCalcNode<L>>),\n    /// A `sign()` function.\n    Sign(Box<GenericCalcNode<L>>),\n    /// An `anchor()` function.\n    Anchor(Box<GenericCalcAnchorFunction<L>>),\n    /// An `anchor-size()` function.\n    AnchorSize(Box<GenericCalcAnchorSizeFunction<L>>),\n}\n\npub use self::GenericCalcNode as CalcNode;\n\nbitflags! {\n    /// Expected units we allow parsing within a `calc()` expression.\n    ///\n    /// This is used as a hint for the parser to fast-reject invalid\n    /// expressions. Numbers are always allowed because they multiply other\n    /// units.\n    #[derive(Clone, Copy, PartialEq, Eq)]\n    pub struct CalcUnits: u8 {\n        /// <length>\n        const LENGTH = 1 << 0;\n        /// <percentage>\n        const PERCENTAGE = 1 << 1;\n        /// <angle>\n        const ANGLE = 1 << 2;\n        /// <time>\n        const TIME = 1 << 3;\n        /// <resolution>\n        const RESOLUTION = 1 << 4;\n        /// A component of a color (r, g, b, h, s, l, alpha, etc.)\n        const COLOR_COMPONENT = 1 << 5;\n\n        /// <length-percentage>\n        const LENGTH_PERCENTAGE = Self::LENGTH.bits() | Self::PERCENTAGE.bits();\n        // NOTE: When you add to this, make sure to make Atan2 deal with these.\n        /// Allow all units.\n        const ALL = Self::LENGTH.bits() | Self::PERCENTAGE.bits() | Self::ANGLE.bits() |\n            Self::TIME.bits() | Self::RESOLUTION.bits() | Self::COLOR_COMPONENT.bits();\n    }\n}\n\nimpl CalcUnits {\n    /// Returns whether the flags only represent a single unit. This will return true for 0, which\n    /// is a \"number\" this is also fine.\n    #[inline]\n    fn is_single_unit(&self) -> bool {\n        self.bits() == 0 || self.bits() & (self.bits() - 1) == 0\n    }\n\n    /// Returns true if this unit is allowed to be summed with the given unit, otherwise false.\n    #[inline]\n    fn can_sum_with(&self, other: Self) -> bool {\n        match *self {\n            Self::LENGTH => other.intersects(Self::LENGTH | Self::PERCENTAGE),\n            Self::PERCENTAGE => other.intersects(Self::LENGTH | Self::PERCENTAGE),\n            Self::LENGTH_PERCENTAGE => other.intersects(Self::LENGTH | Self::PERCENTAGE),\n            u => u.is_single_unit() && other == u,\n        }\n    }\n}\n\n/// For percentage resolution, sometimes we can't assume that the percentage basis is positive (so\n/// we don't know whether a percentage is larger than another).\npub enum PositivePercentageBasis {\n    /// The percent basis is not known-positive, we can't compare percentages.\n    Unknown,\n    /// The percent basis is known-positive, we assume larger percentages are larger.\n    Yes,\n}\n\nmacro_rules! compare_helpers {\n    () => {\n        /// Return whether a leaf is greater than another.\n        #[allow(unused)]\n        fn gt(&self, other: &Self, basis_positive: PositivePercentageBasis) -> bool {\n            self.compare(other, basis_positive) == Some(cmp::Ordering::Greater)\n        }\n\n        /// Return whether a leaf is less than another.\n        fn lt(&self, other: &Self, basis_positive: PositivePercentageBasis) -> bool {\n            self.compare(other, basis_positive) == Some(cmp::Ordering::Less)\n        }\n\n        /// Return whether a leaf is smaller or equal than another.\n        fn lte(&self, other: &Self, basis_positive: PositivePercentageBasis) -> bool {\n            match self.compare(other, basis_positive) {\n                Some(cmp::Ordering::Less) => true,\n                Some(cmp::Ordering::Equal) => true,\n                Some(cmp::Ordering::Greater) => false,\n                None => false,\n            }\n        }\n    };\n}\n\n/// A trait that represents all the stuff a valid leaf of a calc expression.\npub trait CalcNodeLeaf: Clone + Sized + PartialEq + ToCss + ToTyped {\n    /// Returns the unit of the leaf.\n    fn unit(&self) -> CalcUnits;\n\n    /// Returns the unitless value of this leaf if one is available.\n    fn unitless_value(&self) -> Option<f32>;\n\n    /// Return true if the units of both leaves are equal. (NOTE: Does not take\n    /// the values into account)\n    fn is_same_unit_as(&self, other: &Self) -> bool {\n        std::mem::discriminant(self) == std::mem::discriminant(other)\n    }\n\n    /// Do a partial comparison of these values.\n    fn compare(\n        &self,\n        other: &Self,\n        base_is_positive: PositivePercentageBasis,\n    ) -> Option<cmp::Ordering>;\n    compare_helpers!();\n\n    /// Create a new leaf with a number value.\n    fn new_number(value: f32) -> Self;\n\n    /// Returns a float value if the leaf is a number.\n    fn as_number(&self) -> Option<f32>;\n\n    /// Whether this value is known-negative.\n    fn is_negative(&self) -> Result<bool, ()> {\n        self.unitless_value()\n            .map(|v| Ok(v.is_sign_negative()))\n            .unwrap_or_else(|| Err(()))\n    }\n\n    /// Whether this value is infinite.\n    fn is_infinite(&self) -> Result<bool, ()> {\n        self.unitless_value()\n            .map(|v| Ok(v.is_infinite()))\n            .unwrap_or_else(|| Err(()))\n    }\n\n    /// Whether this value is zero.\n    fn is_zero(&self) -> Result<bool, ()> {\n        self.unitless_value()\n            .map(|v| Ok(v.is_zero()))\n            .unwrap_or_else(|| Err(()))\n    }\n\n    /// Whether this value is NaN.\n    fn is_nan(&self) -> Result<bool, ()> {\n        self.unitless_value()\n            .map(|v| Ok(v.is_nan()))\n            .unwrap_or_else(|| Err(()))\n    }\n\n    /// Tries to merge one leaf into another using the sum, that is, perform `x` + `y`.\n    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()>;\n\n    /// Try to merge the right leaf into the left by using a multiplication. Return true if the\n    /// merge was successful, otherwise false.\n    fn try_product_in_place(&mut self, other: &mut Self) -> bool;\n\n    /// Tries a generic arithmetic operation.\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32;\n\n    /// Map the value of this node with the given operation.\n    fn map(&mut self, op: impl FnMut(f32) -> f32) -> Result<(), ()>;\n\n    /// Canonicalizes the expression if necessary.\n    fn simplify(&mut self);\n\n    /// Returns the sort key for simplification.\n    fn sort_key(&self) -> SortKey;\n\n    /// Create a new leaf containing the sign() result of the given leaf.\n    fn sign_from(leaf: &impl CalcNodeLeaf) -> Result<Self, ()> {\n        let Some(value) = leaf.unitless_value() else {\n            return Err(());\n        };\n\n        Ok(Self::new_number(if value.is_nan() {\n            f32::NAN\n        } else if value.is_zero() {\n            value\n        } else if value.is_sign_negative() {\n            -1.0\n        } else {\n            1.0\n        }))\n    }\n}\n\n/// The level of any argument being serialized in `to_css_impl`.\n#[derive(Clone)]\nenum ArgumentLevel {\n    /// The root of a calculation tree.\n    CalculationRoot,\n    /// The root of an operand node's argument, e.g. `min(10, 20)`, `10` and `20` will have this\n    /// level, but min in this case will have `TopMost`.\n    ArgumentRoot,\n    /// Any other values serialized in the tree.\n    Nested,\n}\n\nimpl<L: CalcNodeLeaf> CalcNode<L> {\n    /// Create a dummy CalcNode that can be used to do replacements of other nodes.\n    fn dummy() -> Self {\n        Self::MinMax(Default::default(), MinMaxOp::Max)\n    }\n\n    /// Change all the leaf nodes to have the given value. This is useful when\n    /// you have `calc(1px * nan)` and you want to replace the product node with\n    /// `calc(nan)`, in which case the unit will be retained.\n    fn coerce_to_value(&mut self, value: f32) -> Result<(), ()> {\n        self.map(|_| value)\n    }\n\n    /// Return true if a product is distributive over this node.\n    /// Is distributive: (2 + 3) * 4 = 8 + 12\n    /// Not distributive: sign(2 + 3) * 4 != sign(8 + 12)\n    #[inline]\n    pub fn is_product_distributive(&self) -> bool {\n        match self {\n            Self::Leaf(l) => l.unit() != CalcUnits::COLOR_COMPONENT,\n            Self::Sum(children) => children.iter().all(|c| c.is_product_distributive()),\n            _ => false,\n        }\n    }\n\n    /// If the node has a valid unit outcome, then return it, otherwise fail.\n    pub fn unit(&self) -> Result<CalcUnits, ()> {\n        Ok(match self {\n            CalcNode::Leaf(l) => l.unit(),\n            CalcNode::Negate(child) | CalcNode::Invert(child) | CalcNode::Abs(child) => {\n                child.unit()?\n            },\n            CalcNode::Sum(children) => {\n                let mut unit = children.first().unwrap().unit()?;\n                for child in children.iter().skip(1) {\n                    let child_unit = child.unit()?;\n                    if !child_unit.can_sum_with(unit) {\n                        return Err(());\n                    }\n                    unit |= child_unit;\n                }\n                unit\n            },\n            CalcNode::Product(children) => {\n                // Only one node is allowed to have a unit, the rest must be numbers.\n                let mut unit = None;\n                for child in children.iter() {\n                    let child_unit = child.unit()?;\n                    if child_unit.is_empty() {\n                        // Numbers are always allowed in a product, so continue with the next.\n                        continue;\n                    }\n\n                    if unit.is_some() {\n                        // We already have a unit for the node, so another unit node is invalid.\n                        return Err(());\n                    }\n\n                    // We have the unit for the node.\n                    unit = Some(child_unit);\n                }\n                // We only keep track of specified units, so if we end up with a None and no failure\n                // so far, then we have a number.\n                unit.unwrap_or(CalcUnits::empty())\n            },\n            CalcNode::MinMax(children, _) | CalcNode::Hypot(children) => {\n                let mut unit = children.first().unwrap().unit()?;\n                for child in children.iter().skip(1) {\n                    let child_unit = child.unit()?;\n                    if !child_unit.can_sum_with(unit) {\n                        return Err(());\n                    }\n                    unit |= child_unit;\n                }\n                unit\n            },\n            CalcNode::Clamp { min, center, max } => {\n                let min_unit = min.unit()?;\n                let center_unit = center.unit()?;\n\n                if !min_unit.can_sum_with(center_unit) {\n                    return Err(());\n                }\n\n                let max_unit = max.unit()?;\n\n                if !center_unit.can_sum_with(max_unit) {\n                    return Err(());\n                }\n\n                min_unit | center_unit | max_unit\n            },\n            CalcNode::Round { value, step, .. } => {\n                let value_unit = value.unit()?;\n                let step_unit = step.unit()?;\n                if !step_unit.can_sum_with(value_unit) {\n                    return Err(());\n                }\n                value_unit | step_unit\n            },\n            CalcNode::ModRem {\n                dividend, divisor, ..\n            } => {\n                let dividend_unit = dividend.unit()?;\n                let divisor_unit = divisor.unit()?;\n                if !divisor_unit.can_sum_with(dividend_unit) {\n                    return Err(());\n                }\n                dividend_unit | divisor_unit\n            },\n            CalcNode::Sign(ref child) => {\n                // sign() always resolves to a number, but we still need to make sure that the\n                // child units make sense.\n                let _ = child.unit()?;\n                CalcUnits::empty()\n            },\n            CalcNode::Anchor(..) | CalcNode::AnchorSize(..) => CalcUnits::LENGTH_PERCENTAGE,\n        })\n    }\n\n    /// Negate the node inline.  If the node is distributive, it is replaced by the result,\n    /// otherwise the node is wrapped in a [`Negate`] node.\n    pub fn negate(&mut self) {\n        /// Node(params) -> Negate(Node(params))\n        fn wrap_self_in_negate<L: CalcNodeLeaf>(s: &mut CalcNode<L>) {\n            let result = mem::replace(s, CalcNode::dummy());\n            *s = CalcNode::Negate(Box::new(result));\n        }\n\n        match *self {\n            CalcNode::Leaf(ref mut leaf) => {\n                if leaf.map(std::ops::Neg::neg).is_err() {\n                    wrap_self_in_negate(self)\n                }\n            },\n            CalcNode::Negate(ref mut value) => {\n                // Don't negate the value here.  Replace `self` with it's child.\n                let result = mem::replace(value.as_mut(), Self::dummy());\n                *self = result;\n            },\n            CalcNode::Invert(_) => {\n                // -(1 / -10) == -(-0.1) == 0.1\n                wrap_self_in_negate(self)\n            },\n            CalcNode::Sum(ref mut children) => {\n                for child in children.iter_mut() {\n                    child.negate();\n                }\n            },\n            CalcNode::Product(_) => {\n                // -(2 * 3 / 4) == -(1.5)\n                wrap_self_in_negate(self);\n            },\n            CalcNode::MinMax(ref mut children, ref mut op) => {\n                for child in children.iter_mut() {\n                    child.negate();\n                }\n\n                // Negating min-max means the operation is swapped.\n                *op = match *op {\n                    MinMaxOp::Min => MinMaxOp::Max,\n                    MinMaxOp::Max => MinMaxOp::Min,\n                };\n            },\n            CalcNode::Clamp {\n                ref mut min,\n                ref mut center,\n                ref mut max,\n            } => {\n                if min.lte(max, PositivePercentageBasis::Unknown) {\n                    min.negate();\n                    center.negate();\n                    max.negate();\n\n                    mem::swap(min, max);\n                } else {\n                    wrap_self_in_negate(self);\n                }\n            },\n            CalcNode::Round {\n                ref mut strategy,\n                ref mut value,\n                ref mut step,\n            } => {\n                match *strategy {\n                    RoundingStrategy::Nearest => {\n                        // Nearest is tricky because we'd have to swap the\n                        // behavior at the half-way point from using the upper\n                        // to lower bound.\n                        // Simpler to just wrap self in a negate node.\n                        wrap_self_in_negate(self);\n                        return;\n                    },\n                    RoundingStrategy::Up => *strategy = RoundingStrategy::Down,\n                    RoundingStrategy::Down => *strategy = RoundingStrategy::Up,\n                    RoundingStrategy::ToZero => (),\n                }\n                value.negate();\n                step.negate();\n            },\n            CalcNode::ModRem {\n                ref mut dividend,\n                ref mut divisor,\n                ..\n            } => {\n                dividend.negate();\n                divisor.negate();\n            },\n            CalcNode::Hypot(ref mut children) => {\n                for child in children.iter_mut() {\n                    child.negate();\n                }\n            },\n            CalcNode::Abs(_) => {\n                wrap_self_in_negate(self);\n            },\n            CalcNode::Sign(ref mut child) => {\n                child.negate();\n            },\n            CalcNode::Anchor(_) | CalcNode::AnchorSize(_) => {\n                wrap_self_in_negate(self);\n            },\n        }\n    }\n\n    fn sort_key(&self) -> SortKey {\n        match *self {\n            Self::Leaf(ref l) => l.sort_key(),\n            Self::Anchor(..) | Self::AnchorSize(..) => SortKey::Px,\n            _ => SortKey::Other,\n        }\n    }\n\n    /// Returns the leaf if we can (if simplification has allowed it).\n    pub fn as_leaf(&self) -> Option<&L> {\n        match *self {\n            Self::Leaf(ref l) => Some(l),\n            _ => None,\n        }\n    }\n\n    /// Tries to merge one node into another using the sum, that is, perform `x` + `y`.\n    pub fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {\n        match (self, other) {\n            (&mut CalcNode::Leaf(ref mut one), &CalcNode::Leaf(ref other)) => {\n                one.try_sum_in_place(other)\n            },\n            _ => Err(()),\n        }\n    }\n\n    /// Tries to merge one node into another using the product, that is, perform `x` * `y`.\n    pub fn try_product_in_place(&mut self, other: &mut Self) -> bool {\n        if let Ok(resolved) = other.resolve() {\n            if let Some(number) = resolved.as_number() {\n                if number == 1.0 {\n                    return true;\n                }\n\n                if self.is_product_distributive() {\n                    if self.map(|v| v * number).is_err() {\n                        return false;\n                    }\n                    return true;\n                }\n            }\n        }\n\n        if let Ok(resolved) = self.resolve() {\n            if let Some(number) = resolved.as_number() {\n                if number == 1.0 {\n                    std::mem::swap(self, other);\n                    return true;\n                }\n\n                if other.is_product_distributive() {\n                    if other.map(|v| v * number).is_err() {\n                        return false;\n                    }\n                    std::mem::swap(self, other);\n                    return true;\n                }\n            }\n        }\n\n        false\n    }\n\n    /// Tries to apply a generic arithmetic operator\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        match (self, other) {\n            (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => {\n                Ok(CalcNode::Leaf(one.try_op(other, op)?))\n            },\n            _ => Err(()),\n        }\n    }\n\n    /// Map the value of this node with the given operation.\n    pub fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {\n        fn map_internal<L: CalcNodeLeaf>(\n            node: &mut CalcNode<L>,\n            op: &mut impl FnMut(f32) -> f32,\n        ) -> Result<(), ()> {\n            match node {\n                CalcNode::Leaf(l) => l.map(op),\n                CalcNode::Negate(v) | CalcNode::Invert(v) => map_internal(v, op),\n                CalcNode::Sum(children) | CalcNode::Product(children) => {\n                    for node in &mut **children {\n                        map_internal(node, op)?;\n                    }\n                    Ok(())\n                },\n                CalcNode::MinMax(children, _) => {\n                    for node in &mut **children {\n                        map_internal(node, op)?;\n                    }\n                    Ok(())\n                },\n                CalcNode::Clamp { min, center, max } => {\n                    map_internal(min, op)?;\n                    map_internal(center, op)?;\n                    map_internal(max, op)\n                },\n                CalcNode::Round { value, step, .. } => {\n                    map_internal(value, op)?;\n                    map_internal(step, op)\n                },\n                CalcNode::ModRem {\n                    dividend, divisor, ..\n                } => {\n                    map_internal(dividend, op)?;\n                    map_internal(divisor, op)\n                },\n                CalcNode::Hypot(children) => {\n                    for node in &mut **children {\n                        map_internal(node, op)?;\n                    }\n                    Ok(())\n                },\n                CalcNode::Abs(child) | CalcNode::Sign(child) => map_internal(child, op),\n                // It is invalid to treat inner `CalcNode`s here - `anchor(--foo 50%) / 2` != `anchor(--foo 25%)`.\n                // Same applies to fallback, as we don't know if it will be used. Similar reasoning applies to `anchor-size()`.\n                CalcNode::Anchor(_) | CalcNode::AnchorSize(_) => Err(()),\n            }\n        }\n\n        map_internal(self, &mut op)\n    }\n\n    /// Convert this `CalcNode` into a `CalcNode` with a different leaf kind.\n    pub fn map_leaves<O, F>(&self, mut map: F) -> CalcNode<O>\n    where\n        O: CalcNodeLeaf,\n        F: FnMut(&L) -> O,\n    {\n        self.map_leaves_internal(&mut map)\n    }\n\n    fn map_leaves_internal<O, F>(&self, map: &mut F) -> CalcNode<O>\n    where\n        O: CalcNodeLeaf,\n        F: FnMut(&L) -> O,\n    {\n        fn map_children<L, O, F>(\n            children: &[CalcNode<L>],\n            map: &mut F,\n        ) -> crate::OwnedSlice<CalcNode<O>>\n        where\n            L: CalcNodeLeaf,\n            O: CalcNodeLeaf,\n            F: FnMut(&L) -> O,\n        {\n            children\n                .iter()\n                .map(|c| c.map_leaves_internal(map))\n                .collect()\n        }\n\n        match *self {\n            Self::Leaf(ref l) => CalcNode::Leaf(map(l)),\n            Self::Negate(ref c) => CalcNode::Negate(Box::new(c.map_leaves_internal(map))),\n            Self::Invert(ref c) => CalcNode::Invert(Box::new(c.map_leaves_internal(map))),\n            Self::Sum(ref c) => CalcNode::Sum(map_children(c, map)),\n            Self::Product(ref c) => CalcNode::Product(map_children(c, map)),\n            Self::MinMax(ref c, op) => CalcNode::MinMax(map_children(c, map), op),\n            Self::Clamp {\n                ref min,\n                ref center,\n                ref max,\n            } => {\n                let min = Box::new(min.map_leaves_internal(map));\n                let center = Box::new(center.map_leaves_internal(map));\n                let max = Box::new(max.map_leaves_internal(map));\n                CalcNode::Clamp { min, center, max }\n            },\n            Self::Round {\n                strategy,\n                ref value,\n                ref step,\n            } => {\n                let value = Box::new(value.map_leaves_internal(map));\n                let step = Box::new(step.map_leaves_internal(map));\n                CalcNode::Round {\n                    strategy,\n                    value,\n                    step,\n                }\n            },\n            Self::ModRem {\n                ref dividend,\n                ref divisor,\n                op,\n            } => {\n                let dividend = Box::new(dividend.map_leaves_internal(map));\n                let divisor = Box::new(divisor.map_leaves_internal(map));\n                CalcNode::ModRem {\n                    dividend,\n                    divisor,\n                    op,\n                }\n            },\n            Self::Hypot(ref c) => CalcNode::Hypot(map_children(c, map)),\n            Self::Abs(ref c) => CalcNode::Abs(Box::new(c.map_leaves_internal(map))),\n            Self::Sign(ref c) => CalcNode::Sign(Box::new(c.map_leaves_internal(map))),\n            Self::Anchor(ref f) => CalcNode::Anchor(Box::new(GenericAnchorFunction {\n                target_element: f.target_element.clone(),\n                side: match &f.side {\n                    GenericAnchorSide::Keyword(k) => GenericAnchorSide::Keyword(*k),\n                    GenericAnchorSide::Percentage(p) => {\n                        GenericAnchorSide::Percentage(Box::new(p.map_leaves_internal(map)))\n                    },\n                },\n                fallback: f\n                    .fallback\n                    .as_ref()\n                    .map(|fb| {\n                        Box::new(GenericAnchorFunctionFallback::new(\n                            fb.is_calc_node,\n                            fb.node.map_leaves_internal(map),\n                        ))\n                    })\n                    .into(),\n            })),\n            Self::AnchorSize(ref f) => CalcNode::AnchorSize(Box::new(GenericAnchorSizeFunction {\n                target_element: f.target_element.clone(),\n                size: f.size,\n                fallback: f\n                    .fallback\n                    .as_ref()\n                    .map(|fb| {\n                        Box::new(GenericAnchorFunctionFallback::new(\n                            fb.is_calc_node,\n                            fb.node.map_leaves_internal(map),\n                        ))\n                    })\n                    .into(),\n            })),\n        }\n    }\n\n    /// Resolve this node into a value.\n    pub fn resolve(&self) -> Result<L, ()> {\n        self.resolve_map(|l| Ok(l.clone()))\n    }\n\n    /// Resolve this node into a value, given a function that maps the leaf values.\n    pub fn resolve_map<F>(&self, mut leaf_to_output_fn: F) -> Result<L, ()>\n    where\n        F: FnMut(&L) -> Result<L, ()>,\n    {\n        self.resolve_internal(&mut leaf_to_output_fn)\n    }\n\n    fn resolve_internal<F>(&self, leaf_to_output_fn: &mut F) -> Result<L, ()>\n    where\n        F: FnMut(&L) -> Result<L, ()>,\n    {\n        match self {\n            Self::Leaf(l) => leaf_to_output_fn(l),\n            Self::Negate(child) => {\n                let mut result = child.resolve_internal(leaf_to_output_fn)?;\n                result.map(|v| v.neg())?;\n                Ok(result)\n            },\n            Self::Invert(child) => {\n                let mut result = child.resolve_internal(leaf_to_output_fn)?;\n                result.map(|v| 1.0 / v)?;\n                Ok(result)\n            },\n            Self::Sum(children) => {\n                let mut result = children[0].resolve_internal(leaf_to_output_fn)?;\n\n                for child in children.iter().skip(1) {\n                    let right = child.resolve_internal(leaf_to_output_fn)?;\n                    // try_op will make sure we only sum leaves with the same type.\n                    result = result.try_op(&right, |left, right| left + right)?;\n                }\n\n                Ok(result)\n            },\n            Self::Product(children) => {\n                let mut result = children[0].resolve_internal(leaf_to_output_fn)?;\n\n                for child in children.iter().skip(1) {\n                    let right = child.resolve_internal(leaf_to_output_fn)?;\n                    // Mutliply only allowed when either side is a number.\n                    match result.as_number() {\n                        Some(left) => {\n                            // Left side is a number, so we use the right node as the result.\n                            result = right;\n                            result.map(|v| v * left)?;\n                        },\n                        None => {\n                            // Left side is not a number, so check if the right side is.\n                            match right.as_number() {\n                                Some(right) => {\n                                    result.map(|v| v * right)?;\n                                },\n                                None => {\n                                    // Multiplying with both sides having units.\n                                    return Err(());\n                                },\n                            }\n                        },\n                    }\n                }\n\n                Ok(result)\n            },\n            Self::MinMax(children, op) => {\n                let mut result = children[0].resolve_internal(leaf_to_output_fn)?;\n\n                if result.is_nan()? {\n                    return Ok(result);\n                }\n\n                for child in children.iter().skip(1) {\n                    let candidate = child.resolve_internal(leaf_to_output_fn)?;\n\n                    // Leaf types must match for each child.\n                    if !result.is_same_unit_as(&candidate) {\n                        return Err(());\n                    }\n\n                    if candidate.is_nan()? {\n                        result = candidate;\n                        break;\n                    }\n\n                    let candidate_wins = match op {\n                        MinMaxOp::Min => candidate.lt(&result, PositivePercentageBasis::Yes),\n                        MinMaxOp::Max => candidate.gt(&result, PositivePercentageBasis::Yes),\n                    };\n\n                    if candidate_wins {\n                        result = candidate;\n                    }\n                }\n\n                Ok(result)\n            },\n            Self::Clamp { min, center, max } => {\n                let min = min.resolve_internal(leaf_to_output_fn)?;\n                let center = center.resolve_internal(leaf_to_output_fn)?;\n                let max = max.resolve_internal(leaf_to_output_fn)?;\n\n                if !min.is_same_unit_as(&center) || !max.is_same_unit_as(&center) {\n                    return Err(());\n                }\n\n                if min.is_nan()? {\n                    return Ok(min);\n                }\n\n                if center.is_nan()? {\n                    return Ok(center);\n                }\n\n                if max.is_nan()? {\n                    return Ok(max);\n                }\n\n                let mut result = center;\n                if result.gt(&max, PositivePercentageBasis::Yes) {\n                    result = max;\n                }\n                if result.lt(&min, PositivePercentageBasis::Yes) {\n                    result = min\n                }\n\n                Ok(result)\n            },\n            Self::Round {\n                strategy,\n                value,\n                step,\n            } => {\n                let mut value = value.resolve_internal(leaf_to_output_fn)?;\n                let step = step.resolve_internal(leaf_to_output_fn)?;\n\n                if !value.is_same_unit_as(&step) {\n                    return Err(());\n                }\n\n                let Some(step) = step.unitless_value() else {\n                    return Err(());\n                };\n                let step = step.abs();\n\n                value.map(|value| {\n                    // TODO(emilio): Seems like at least a few of these\n                    // special-cases could be removed if we do the math in a\n                    // particular order.\n                    if step.is_zero() {\n                        return f32::NAN;\n                    }\n\n                    if value.is_infinite() {\n                        if step.is_infinite() {\n                            return f32::NAN;\n                        }\n                        return value;\n                    }\n\n                    if step.is_infinite() {\n                        match strategy {\n                            RoundingStrategy::Nearest | RoundingStrategy::ToZero => {\n                                return if value.is_sign_negative() { -0.0 } else { 0.0 }\n                            },\n                            RoundingStrategy::Up => {\n                                return if !value.is_sign_negative() && !value.is_zero() {\n                                    f32::INFINITY\n                                } else if !value.is_sign_negative() && value.is_zero() {\n                                    value\n                                } else {\n                                    -0.0\n                                }\n                            },\n                            RoundingStrategy::Down => {\n                                return if value.is_sign_negative() && !value.is_zero() {\n                                    -f32::INFINITY\n                                } else if value.is_sign_negative() && value.is_zero() {\n                                    value\n                                } else {\n                                    0.0\n                                }\n                            },\n                        }\n                    }\n\n                    let div = value / step;\n                    let lower_bound = div.floor() * step;\n                    let upper_bound = div.ceil() * step;\n\n                    match strategy {\n                        RoundingStrategy::Nearest => {\n                            // In case of a tie, use the upper bound\n                            if value - lower_bound < upper_bound - value {\n                                lower_bound\n                            } else {\n                                upper_bound\n                            }\n                        },\n                        RoundingStrategy::Up => upper_bound,\n                        RoundingStrategy::Down => lower_bound,\n                        RoundingStrategy::ToZero => {\n                            // In case of a tie, use the upper bound\n                            if lower_bound.abs() < upper_bound.abs() {\n                                lower_bound\n                            } else {\n                                upper_bound\n                            }\n                        },\n                    }\n                })?;\n\n                Ok(value)\n            },\n            Self::ModRem {\n                dividend,\n                divisor,\n                op,\n            } => {\n                let mut dividend = dividend.resolve_internal(leaf_to_output_fn)?;\n                let divisor = divisor.resolve_internal(leaf_to_output_fn)?;\n\n                if !dividend.is_same_unit_as(&divisor) {\n                    return Err(());\n                }\n\n                let Some(divisor) = divisor.unitless_value() else {\n                    return Err(());\n                };\n                dividend.map(|dividend| op.apply(dividend, divisor))?;\n                Ok(dividend)\n            },\n            Self::Hypot(children) => {\n                let mut result = children[0].resolve_internal(leaf_to_output_fn)?;\n                result.map(|v| v.powi(2))?;\n\n                for child in children.iter().skip(1) {\n                    let child_value = child.resolve_internal(leaf_to_output_fn)?;\n\n                    if !result.is_same_unit_as(&child_value) {\n                        return Err(());\n                    }\n\n                    let Some(child_value) = child_value.unitless_value() else {\n                        return Err(());\n                    };\n                    result.map(|v| v + child_value.powi(2))?;\n                }\n\n                result.map(|v| v.sqrt())?;\n                Ok(result)\n            },\n            Self::Abs(ref c) => {\n                let mut result = c.resolve_internal(leaf_to_output_fn)?;\n\n                result.map(|v| v.abs())?;\n\n                Ok(result)\n            },\n            Self::Sign(ref c) => {\n                let result = c.resolve_internal(leaf_to_output_fn)?;\n                Ok(L::sign_from(&result)?)\n            },\n            Self::Anchor(_) | Self::AnchorSize(_) => Err(()),\n        }\n    }\n\n    /// Mutate nodes within this calc node tree using given the mapping function.\n    pub fn map_node<F>(&mut self, mut mapping_fn: F) -> Result<(), ()>\n    where\n        F: FnMut(&CalcNode<L>) -> Result<Option<CalcNode<L>>, ()>,\n    {\n        self.map_node_internal(&mut mapping_fn)\n    }\n\n    fn map_node_internal<F>(&mut self, mapping_fn: &mut F) -> Result<(), ()>\n    where\n        F: FnMut(&CalcNode<L>) -> Result<Option<CalcNode<L>>, ()>,\n    {\n        if let Some(node) = mapping_fn(self)? {\n            *self = node;\n            // Assume that any sub-nodes don't need to be mutated.\n            return Ok(());\n        }\n        match self {\n            Self::Leaf(_) | Self::Anchor(_) | Self::AnchorSize(_) => (),\n            Self::Negate(child) | Self::Invert(child) | Self::Abs(child) | Self::Sign(child) => {\n                child.map_node_internal(mapping_fn)?;\n            },\n            Self::Sum(children)\n            | Self::Product(children)\n            | Self::Hypot(children)\n            | Self::MinMax(children, _) => {\n                for child in children.iter_mut() {\n                    child.map_node_internal(mapping_fn)?;\n                }\n            },\n            Self::Clamp { min, center, max } => {\n                min.map_node_internal(mapping_fn)?;\n                center.map_node_internal(mapping_fn)?;\n                max.map_node_internal(mapping_fn)?;\n            },\n            Self::Round { value, step, .. } => {\n                value.map_node_internal(mapping_fn)?;\n                step.map_node_internal(mapping_fn)?;\n            },\n            Self::ModRem {\n                dividend, divisor, ..\n            } => {\n                dividend.map_node_internal(mapping_fn)?;\n                divisor.map_node_internal(mapping_fn)?;\n            },\n        };\n        Ok(())\n    }\n\n    fn is_negative_leaf(&self) -> Result<bool, ()> {\n        Ok(match *self {\n            Self::Leaf(ref l) => l.is_negative()?,\n            _ => false,\n        })\n    }\n\n    fn is_zero_leaf(&self) -> Result<bool, ()> {\n        Ok(match *self {\n            Self::Leaf(ref l) => l.is_zero()?,\n            _ => false,\n        })\n    }\n\n    fn is_infinite_leaf(&self) -> Result<bool, ()> {\n        Ok(match *self {\n            Self::Leaf(ref l) => l.is_infinite()?,\n            _ => false,\n        })\n    }\n\n    fn is_nan_leaf(&self) -> Result<bool, ()> {\n        Ok(match *self {\n            Self::Leaf(ref l) => l.is_nan()?,\n            _ => false,\n        })\n    }\n\n    /// Visits all the nodes in this calculation tree recursively, starting by\n    /// the leaves and bubbling all the way up.\n    ///\n    /// This is useful for simplification, but can also be used for validation\n    /// and such.\n    pub fn visit_depth_first(&mut self, mut f: impl FnMut(&mut Self)) {\n        self.visit_depth_first_internal(&mut f)\n    }\n\n    fn visit_depth_first_internal(&mut self, f: &mut impl FnMut(&mut Self)) {\n        match *self {\n            Self::Clamp {\n                ref mut min,\n                ref mut center,\n                ref mut max,\n            } => {\n                min.visit_depth_first_internal(f);\n                center.visit_depth_first_internal(f);\n                max.visit_depth_first_internal(f);\n            },\n            Self::Round {\n                ref mut value,\n                ref mut step,\n                ..\n            } => {\n                value.visit_depth_first_internal(f);\n                step.visit_depth_first_internal(f);\n            },\n            Self::ModRem {\n                ref mut dividend,\n                ref mut divisor,\n                ..\n            } => {\n                dividend.visit_depth_first_internal(f);\n                divisor.visit_depth_first_internal(f);\n            },\n            Self::Sum(ref mut children)\n            | Self::Product(ref mut children)\n            | Self::MinMax(ref mut children, _)\n            | Self::Hypot(ref mut children) => {\n                for child in &mut **children {\n                    child.visit_depth_first_internal(f);\n                }\n            },\n            Self::Negate(ref mut value) | Self::Invert(ref mut value) => {\n                value.visit_depth_first_internal(f);\n            },\n            Self::Abs(ref mut value) | Self::Sign(ref mut value) => {\n                value.visit_depth_first_internal(f);\n            },\n            Self::Leaf(..) | Self::Anchor(..) | Self::AnchorSize(..) => {},\n        }\n        f(self);\n    }\n\n    /// This function simplifies and sorts the calculation of the specified node. It simplifies\n    /// directly nested nodes while assuming that all nodes below it have already been simplified.\n    /// It is recommended to use this function in combination with `visit_depth_first()`.\n    ///\n    /// This function is necessary only if the node needs to be preserved after parsing,\n    /// specifically for `<length-percentage>` cases where the calculation contains percentages or\n    /// relative units. Otherwise, the node can be evaluated using `resolve()`, which will\n    /// automatically provide a simplified value.\n    ///\n    /// <https://drafts.csswg.org/css-values-4/#calc-simplification>\n    pub fn simplify_and_sort_direct_children(&mut self) {\n        macro_rules! replace_self_with {\n            ($slot:expr) => {{\n                let result = mem::replace($slot, Self::dummy());\n                *self = result;\n            }};\n        }\n\n        macro_rules! value_or_stop {\n            ($op:expr) => {{\n                match $op {\n                    Ok(value) => value,\n                    Err(_) => return,\n                }\n            }};\n        }\n\n        match *self {\n            Self::Clamp {\n                ref mut min,\n                ref mut center,\n                ref mut max,\n            } => {\n                // NOTE: clamp() is max(min, min(center, max))\n                let min_cmp_center = match min.compare(&center, PositivePercentageBasis::Unknown) {\n                    Some(o) => o,\n                    None => return,\n                };\n\n                // So if we can prove that min is more than center, then we won,\n                // as that's what we should always return.\n                if matches!(min_cmp_center, cmp::Ordering::Greater) {\n                    replace_self_with!(&mut **min);\n                    return;\n                }\n\n                // Otherwise try with max.\n                let max_cmp_center = match max.compare(&center, PositivePercentageBasis::Unknown) {\n                    Some(o) => o,\n                    None => return,\n                };\n\n                if matches!(max_cmp_center, cmp::Ordering::Less) {\n                    // max is less than center, so we need to return effectively\n                    // `max(min, max)`.\n                    let max_cmp_min = match max.compare(&min, PositivePercentageBasis::Unknown) {\n                        Some(o) => o,\n                        None => return,\n                    };\n\n                    if matches!(max_cmp_min, cmp::Ordering::Less) {\n                        replace_self_with!(&mut **min);\n                        return;\n                    }\n\n                    replace_self_with!(&mut **max);\n                    return;\n                }\n\n                // Otherwise we're the center node.\n                replace_self_with!(&mut **center);\n            },\n            Self::Round {\n                strategy,\n                ref mut value,\n                ref mut step,\n            } => {\n                if value_or_stop!(step.is_zero_leaf()) {\n                    value_or_stop!(value.coerce_to_value(f32::NAN));\n                    replace_self_with!(&mut **value);\n                    return;\n                }\n\n                if value_or_stop!(value.is_infinite_leaf())\n                    && value_or_stop!(step.is_infinite_leaf())\n                {\n                    value_or_stop!(value.coerce_to_value(f32::NAN));\n                    replace_self_with!(&mut **value);\n                    return;\n                }\n\n                if value_or_stop!(value.is_infinite_leaf()) {\n                    replace_self_with!(&mut **value);\n                    return;\n                }\n\n                if value_or_stop!(step.is_infinite_leaf()) {\n                    match strategy {\n                        RoundingStrategy::Nearest | RoundingStrategy::ToZero => {\n                            value_or_stop!(value.coerce_to_value(0.0));\n                            replace_self_with!(&mut **value);\n                            return;\n                        },\n                        RoundingStrategy::Up => {\n                            if !value_or_stop!(value.is_negative_leaf())\n                                && !value_or_stop!(value.is_zero_leaf())\n                            {\n                                value_or_stop!(value.coerce_to_value(f32::INFINITY));\n                                replace_self_with!(&mut **value);\n                                return;\n                            } else if !value_or_stop!(value.is_negative_leaf())\n                                && value_or_stop!(value.is_zero_leaf())\n                            {\n                                replace_self_with!(&mut **value);\n                                return;\n                            } else {\n                                value_or_stop!(value.coerce_to_value(0.0));\n                                replace_self_with!(&mut **value);\n                                return;\n                            }\n                        },\n                        RoundingStrategy::Down => {\n                            if value_or_stop!(value.is_negative_leaf())\n                                && !value_or_stop!(value.is_zero_leaf())\n                            {\n                                value_or_stop!(value.coerce_to_value(f32::INFINITY));\n                                replace_self_with!(&mut **value);\n                                return;\n                            } else if value_or_stop!(value.is_negative_leaf())\n                                && value_or_stop!(value.is_zero_leaf())\n                            {\n                                replace_self_with!(&mut **value);\n                                return;\n                            } else {\n                                value_or_stop!(value.coerce_to_value(0.0));\n                                replace_self_with!(&mut **value);\n                                return;\n                            }\n                        },\n                    }\n                }\n\n                if value_or_stop!(step.is_negative_leaf()) {\n                    step.negate();\n                }\n\n                let remainder = value_or_stop!(value.try_op(step, Rem::rem));\n                if value_or_stop!(remainder.is_zero_leaf()) {\n                    replace_self_with!(&mut **value);\n                    return;\n                }\n\n                let (mut lower_bound, mut upper_bound) = if value_or_stop!(value.is_negative_leaf())\n                {\n                    let upper_bound = value_or_stop!(value.try_op(&remainder, Sub::sub));\n                    let lower_bound = value_or_stop!(upper_bound.try_op(&step, Sub::sub));\n\n                    (lower_bound, upper_bound)\n                } else {\n                    let lower_bound = value_or_stop!(value.try_op(&remainder, Sub::sub));\n                    let upper_bound = value_or_stop!(lower_bound.try_op(&step, Add::add));\n\n                    (lower_bound, upper_bound)\n                };\n\n                match strategy {\n                    RoundingStrategy::Nearest => {\n                        let lower_diff = value_or_stop!(value.try_op(&lower_bound, Sub::sub));\n                        let upper_diff = value_or_stop!(upper_bound.try_op(value, Sub::sub));\n                        // In case of a tie, use the upper bound\n                        if lower_diff.lt(&upper_diff, PositivePercentageBasis::Unknown) {\n                            replace_self_with!(&mut lower_bound);\n                        } else {\n                            replace_self_with!(&mut upper_bound);\n                        }\n                    },\n                    RoundingStrategy::Up => {\n                        replace_self_with!(&mut upper_bound);\n                    },\n                    RoundingStrategy::Down => {\n                        replace_self_with!(&mut lower_bound);\n                    },\n                    RoundingStrategy::ToZero => {\n                        let mut lower_diff = lower_bound.clone();\n                        let mut upper_diff = upper_bound.clone();\n\n                        if value_or_stop!(lower_diff.is_negative_leaf()) {\n                            lower_diff.negate();\n                        }\n\n                        if value_or_stop!(upper_diff.is_negative_leaf()) {\n                            upper_diff.negate();\n                        }\n\n                        // In case of a tie, use the upper bound\n                        if lower_diff.lt(&upper_diff, PositivePercentageBasis::Unknown) {\n                            replace_self_with!(&mut lower_bound);\n                        } else {\n                            replace_self_with!(&mut upper_bound);\n                        }\n                    },\n                };\n            },\n            Self::ModRem {\n                ref dividend,\n                ref divisor,\n                op,\n            } => {\n                let mut result = value_or_stop!(dividend.try_op(divisor, |a, b| op.apply(a, b)));\n                replace_self_with!(&mut result);\n            },\n            Self::MinMax(ref mut children, op) => {\n                let winning_order = match op {\n                    MinMaxOp::Min => cmp::Ordering::Less,\n                    MinMaxOp::Max => cmp::Ordering::Greater,\n                };\n\n                if value_or_stop!(children[0].is_nan_leaf()) {\n                    replace_self_with!(&mut children[0]);\n                    return;\n                }\n\n                let mut result = 0;\n                for i in 1..children.len() {\n                    if value_or_stop!(children[i].is_nan_leaf()) {\n                        replace_self_with!(&mut children[i]);\n                        return;\n                    }\n                    let o = match children[i]\n                        .compare(&children[result], PositivePercentageBasis::Unknown)\n                    {\n                        // We can't compare all the children, so we can't\n                        // know which one will actually win. Bail out and\n                        // keep ourselves as a min / max function.\n                        //\n                        // TODO: Maybe we could simplify compatible children,\n                        // see https://github.com/w3c/csswg-drafts/issues/4756\n                        None => return,\n                        Some(o) => o,\n                    };\n\n                    if o == winning_order {\n                        result = i;\n                    }\n                }\n\n                replace_self_with!(&mut children[result]);\n            },\n            Self::Sum(ref mut children_slot) => {\n                let mut sums_to_merge = SmallVec::<[_; 3]>::new();\n                let mut extra_kids = 0;\n                for (i, child) in children_slot.iter().enumerate() {\n                    if let Self::Sum(ref children) = *child {\n                        extra_kids += children.len();\n                        sums_to_merge.push(i);\n                    }\n                }\n\n                // If we only have one kid, we've already simplified it, and it\n                // doesn't really matter whether it's a sum already or not, so\n                // lift it up and continue.\n                if children_slot.len() == 1 {\n                    replace_self_with!(&mut children_slot[0]);\n                    return;\n                }\n\n                let mut children = mem::take(children_slot).into_vec();\n\n                if !sums_to_merge.is_empty() {\n                    children.reserve(extra_kids - sums_to_merge.len());\n                    // Merge all our nested sums, in reverse order so that the\n                    // list indices are not invalidated.\n                    for i in sums_to_merge.drain(..).rev() {\n                        let kid_children = match children.swap_remove(i) {\n                            Self::Sum(c) => c,\n                            _ => unreachable!(),\n                        };\n\n                        // This would be nicer with\n                        // https://github.com/rust-lang/rust/issues/59878 fixed.\n                        children.extend(kid_children.into_vec());\n                    }\n                }\n\n                debug_assert!(children.len() >= 2, \"Should still have multiple kids!\");\n\n                // Sort by spec order.\n                children.sort_unstable_by_key(|c| c.sort_key());\n\n                // NOTE: if the function returns true, by the docs of dedup_by,\n                // a is removed.\n                children.dedup_by(|a, b| b.try_sum_in_place(a).is_ok());\n\n                if children.len() == 1 {\n                    // If only one children remains, lift it up, and carry on.\n                    replace_self_with!(&mut children[0]);\n                } else {\n                    // Else put our simplified children back.\n                    *children_slot = children.into_boxed_slice().into();\n                }\n            },\n            Self::Product(ref mut children_slot) => {\n                let mut products_to_merge = SmallVec::<[_; 3]>::new();\n                let mut extra_kids = 0;\n                for (i, child) in children_slot.iter().enumerate() {\n                    if let Self::Product(ref children) = *child {\n                        extra_kids += children.len();\n                        products_to_merge.push(i);\n                    }\n                }\n\n                // If we only have one kid, we've already simplified it, and it\n                // doesn't really matter whether it's a product already or not,\n                // so lift it up and continue.\n                if children_slot.len() == 1 {\n                    replace_self_with!(&mut children_slot[0]);\n                    return;\n                }\n\n                let mut children = mem::take(children_slot).into_vec();\n\n                if !products_to_merge.is_empty() {\n                    children.reserve(extra_kids - products_to_merge.len());\n                    // Merge all our nested sums, in reverse order so that the\n                    // list indices are not invalidated.\n                    for i in products_to_merge.drain(..).rev() {\n                        let kid_children = match children.swap_remove(i) {\n                            Self::Product(c) => c,\n                            _ => unreachable!(),\n                        };\n\n                        // This would be nicer with\n                        // https://github.com/rust-lang/rust/issues/59878 fixed.\n                        children.extend(kid_children.into_vec());\n                    }\n                }\n\n                debug_assert!(children.len() >= 2, \"Should still have multiple kids!\");\n\n                // Sort by spec order.\n                children.sort_unstable_by_key(|c| c.sort_key());\n\n                // NOTE: if the function returns true, by the docs of dedup_by,\n                // a is removed.\n                children.dedup_by(|right, left| left.try_product_in_place(right));\n\n                if children.len() == 1 {\n                    // If only one children remains, lift it up, and carry on.\n                    replace_self_with!(&mut children[0]);\n                } else {\n                    // Else put our simplified children back.\n                    *children_slot = children.into_boxed_slice().into();\n                }\n            },\n            Self::Hypot(ref children) => {\n                let mut result = value_or_stop!(children[0].try_op(&children[0], Mul::mul));\n\n                for child in children.iter().skip(1) {\n                    let square = value_or_stop!(child.try_op(&child, Mul::mul));\n                    result = value_or_stop!(result.try_op(&square, Add::add));\n                }\n\n                result = value_or_stop!(result.try_op(&result, |a, _| a.sqrt()));\n\n                replace_self_with!(&mut result);\n            },\n            Self::Abs(ref mut child) => {\n                if let CalcNode::Leaf(leaf) = child.as_mut() {\n                    value_or_stop!(leaf.map(|v| v.abs()));\n                    replace_self_with!(&mut **child);\n                }\n            },\n            Self::Sign(ref mut child) => {\n                if let CalcNode::Leaf(leaf) = child.as_mut() {\n                    let mut result = Self::Leaf(value_or_stop!(L::sign_from(leaf)));\n                    replace_self_with!(&mut result);\n                }\n            },\n            Self::Negate(ref mut child) => {\n                // Step 6.\n                match &mut **child {\n                    CalcNode::Leaf(_) => {\n                        // 1. If root’s child is a numeric value, return an equivalent numeric value, but\n                        // with the value negated (0 - value).\n                        child.negate();\n                        replace_self_with!(&mut **child);\n                    },\n                    CalcNode::Negate(value) => {\n                        // 2. If root’s child is a Negate node, return the child’s child.\n                        replace_self_with!(&mut **value);\n                    },\n                    _ => {\n                        // 3. Return root.\n                    },\n                }\n            },\n            Self::Invert(ref mut child) => {\n                // Step 7.\n                match &mut **child {\n                    CalcNode::Leaf(leaf) => {\n                        // 1. If root’s child is a number (not a percentage or dimension) return the\n                        // reciprocal of the child’s value.\n                        if leaf.unit().is_empty() {\n                            value_or_stop!(child.map(|v| 1.0 / v));\n                            replace_self_with!(&mut **child);\n                        }\n                    },\n                    CalcNode::Invert(value) => {\n                        // 2. If root’s child is an Invert node, return the child’s child.\n                        replace_self_with!(&mut **value);\n                    },\n                    _ => {\n                        // 3. Return root.\n                    },\n                }\n            },\n            Self::Leaf(ref mut l) => {\n                l.simplify();\n            },\n            Self::Anchor(ref mut f) => {\n                if let GenericAnchorSide::Percentage(ref mut n) = f.side {\n                    n.simplify_and_sort();\n                }\n                if let Some(fallback) = f.fallback.as_mut() {\n                    fallback.node.simplify_and_sort();\n                }\n            },\n            Self::AnchorSize(ref mut f) => {\n                if let Some(fallback) = f.fallback.as_mut() {\n                    fallback.node.simplify_and_sort();\n                }\n            },\n        }\n    }\n\n    /// Simplifies and sorts the kids in the whole calculation subtree.\n    pub fn simplify_and_sort(&mut self) {\n        self.visit_depth_first(|node| node.simplify_and_sort_direct_children())\n    }\n\n    fn to_css_impl<W>(&self, dest: &mut CssWriter<W>, level: ArgumentLevel) -> fmt::Result\n    where\n        W: Write,\n    {\n        let write_closing_paren = match *self {\n            Self::MinMax(_, op) => {\n                dest.write_str(match op {\n                    MinMaxOp::Max => \"max(\",\n                    MinMaxOp::Min => \"min(\",\n                })?;\n                true\n            },\n            Self::Clamp { .. } => {\n                dest.write_str(\"clamp(\")?;\n                true\n            },\n            Self::Round { strategy, .. } => {\n                match strategy {\n                    RoundingStrategy::Nearest => dest.write_str(\"round(\"),\n                    RoundingStrategy::Up => dest.write_str(\"round(up, \"),\n                    RoundingStrategy::Down => dest.write_str(\"round(down, \"),\n                    RoundingStrategy::ToZero => dest.write_str(\"round(to-zero, \"),\n                }?;\n\n                true\n            },\n            Self::ModRem { op, .. } => {\n                dest.write_str(match op {\n                    ModRemOp::Mod => \"mod(\",\n                    ModRemOp::Rem => \"rem(\",\n                })?;\n\n                true\n            },\n            Self::Hypot(_) => {\n                dest.write_str(\"hypot(\")?;\n                true\n            },\n            Self::Abs(_) => {\n                dest.write_str(\"abs(\")?;\n                true\n            },\n            Self::Sign(_) => {\n                dest.write_str(\"sign(\")?;\n                true\n            },\n            Self::Negate(_) => {\n                // We never generate a [`Negate`] node as the root of a calculation, only inside\n                // [`Sum`] nodes as a child. Because negate nodes are handled by the [`Sum`] node\n                // directly (see below), this node will never be serialized.\n                debug_assert!(\n                    false,\n                    \"We never serialize Negate nodes as they are handled inside Sum nodes.\"\n                );\n                dest.write_str(\"(-1 * \")?;\n                true\n            },\n            Self::Invert(_) => {\n                if matches!(level, ArgumentLevel::CalculationRoot) {\n                    dest.write_str(\"calc\")?;\n                }\n                dest.write_str(\"(1 / \")?;\n                true\n            },\n            Self::Sum(_) | Self::Product(_) => match level {\n                ArgumentLevel::CalculationRoot => {\n                    dest.write_str(\"calc(\")?;\n                    true\n                },\n                ArgumentLevel::ArgumentRoot => false,\n                ArgumentLevel::Nested => {\n                    dest.write_str(\"(\")?;\n                    true\n                },\n            },\n            Self::Leaf(_) => match level {\n                ArgumentLevel::CalculationRoot => {\n                    dest.write_str(\"calc(\")?;\n                    true\n                },\n                ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => false,\n            },\n            Self::Anchor(_) | Self::AnchorSize(_) => false,\n        };\n\n        match *self {\n            Self::MinMax(ref children, _) | Self::Hypot(ref children) => {\n                let mut first = true;\n                for child in &**children {\n                    if !first {\n                        dest.write_str(\", \")?;\n                    }\n                    first = false;\n                    child.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n                }\n            },\n            Self::Negate(ref value) | Self::Invert(ref value) => {\n                value.to_css_impl(dest, ArgumentLevel::Nested)?\n            },\n            Self::Sum(ref children) => {\n                let mut first = true;\n                for child in &**children {\n                    if !first {\n                        match child {\n                            Self::Leaf(l) => {\n                                if let Ok(true) = l.is_negative() {\n                                    dest.write_str(\" - \")?;\n                                    let mut negated = l.clone();\n                                    // We can unwrap here, because we already\n                                    // checked if the value inside is negative.\n                                    negated.map(std::ops::Neg::neg).unwrap();\n                                    negated.to_css(dest)?;\n                                } else {\n                                    dest.write_str(\" + \")?;\n                                    l.to_css(dest)?;\n                                }\n                            },\n                            Self::Negate(n) => {\n                                dest.write_str(\" - \")?;\n                                n.to_css_impl(dest, ArgumentLevel::Nested)?;\n                            },\n                            _ => {\n                                dest.write_str(\" + \")?;\n                                child.to_css_impl(dest, ArgumentLevel::Nested)?;\n                            },\n                        }\n                    } else {\n                        first = false;\n                        child.to_css_impl(dest, ArgumentLevel::Nested)?;\n                    }\n                }\n            },\n            Self::Product(ref children) => {\n                let mut first = true;\n                for child in &**children {\n                    if !first {\n                        match child {\n                            Self::Invert(n) => {\n                                dest.write_str(\" / \")?;\n                                n.to_css_impl(dest, ArgumentLevel::Nested)?;\n                            },\n                            _ => {\n                                dest.write_str(\" * \")?;\n                                child.to_css_impl(dest, ArgumentLevel::Nested)?;\n                            },\n                        }\n                    } else {\n                        first = false;\n                        child.to_css_impl(dest, ArgumentLevel::Nested)?;\n                    }\n                }\n            },\n            Self::Clamp {\n                ref min,\n                ref center,\n                ref max,\n            } => {\n                min.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n                dest.write_str(\", \")?;\n                center.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n                dest.write_str(\", \")?;\n                max.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n            },\n            Self::Round {\n                ref value,\n                ref step,\n                ..\n            } => {\n                value.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n                dest.write_str(\", \")?;\n                step.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n            },\n            Self::ModRem {\n                ref dividend,\n                ref divisor,\n                ..\n            } => {\n                dividend.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n                dest.write_str(\", \")?;\n                divisor.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?;\n            },\n            Self::Abs(ref v) | Self::Sign(ref v) => {\n                v.to_css_impl(dest, ArgumentLevel::ArgumentRoot)?\n            },\n            Self::Leaf(ref l) => l.to_css(dest)?,\n            Self::Anchor(ref f) => f.to_css(dest)?,\n            Self::AnchorSize(ref f) => f.to_css(dest)?,\n        }\n\n        if write_closing_paren {\n            dest.write_char(')')?;\n        }\n        Ok(())\n    }\n\n    fn to_typed_impl(\n        &self,\n        dest: &mut ThinVec<TypedValue>,\n        level: ArgumentLevel,\n    ) -> Result<(), ()> {\n        // XXX Only supporting Sum and Leaf for now\n        match *self {\n            Self::Sum(ref children) => {\n                let mut values = ThinVec::new();\n                for child in &**children {\n                    let nested = CalcNodeWithLevel {\n                        node: child,\n                        level: ArgumentLevel::Nested,\n                    };\n                    if let Some(TypedValue::Numeric(inner)) = nested.to_typed_value() {\n                        values.push(inner);\n                    }\n                }\n                dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum { values })));\n                Ok(())\n            },\n            Self::Leaf(ref l) => match l.to_typed_value() {\n                Some(TypedValue::Numeric(inner)) => {\n                    match level {\n                        ArgumentLevel::CalculationRoot => {\n                            dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {\n                                values: ThinVec::from([inner]),\n                            })));\n                        },\n                        ArgumentLevel::ArgumentRoot | ArgumentLevel::Nested => {\n                            dest.push(TypedValue::Numeric(inner));\n                        },\n                    }\n                    Ok(())\n                },\n                _ => Err(()),\n            },\n            _ => Err(()),\n        }\n    }\n\n    fn compare(\n        &self,\n        other: &Self,\n        basis_positive: PositivePercentageBasis,\n    ) -> Option<cmp::Ordering> {\n        match (self, other) {\n            (&CalcNode::Leaf(ref one), &CalcNode::Leaf(ref other)) => {\n                one.compare(other, basis_positive)\n            },\n            _ => None,\n        }\n    }\n\n    compare_helpers!();\n}\n\nimpl<L: CalcNodeLeaf> ToCss for CalcNode<L> {\n    /// <https://drafts.csswg.org/css-values/#calc-serialize>\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.to_css_impl(dest, ArgumentLevel::CalculationRoot)\n    }\n}\n\nimpl<L: CalcNodeLeaf> ToTyped for CalcNode<L> {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        self.to_typed_impl(dest, ArgumentLevel::CalculationRoot)\n    }\n}\n\nstruct CalcNodeWithLevel<'a, L> {\n    node: &'a CalcNode<L>,\n    level: ArgumentLevel,\n}\n\nimpl<'a, L: CalcNodeLeaf> ToTyped for CalcNodeWithLevel<'a, L> {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        self.node.to_typed_impl(dest, self.level.clone())\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn can_sum_with_checks() {\n        assert!(CalcUnits::LENGTH.can_sum_with(CalcUnits::LENGTH));\n        assert!(CalcUnits::LENGTH.can_sum_with(CalcUnits::PERCENTAGE));\n        assert!(CalcUnits::LENGTH.can_sum_with(CalcUnits::LENGTH_PERCENTAGE));\n\n        assert!(CalcUnits::PERCENTAGE.can_sum_with(CalcUnits::LENGTH));\n        assert!(CalcUnits::PERCENTAGE.can_sum_with(CalcUnits::PERCENTAGE));\n        assert!(CalcUnits::PERCENTAGE.can_sum_with(CalcUnits::LENGTH_PERCENTAGE));\n\n        assert!(CalcUnits::LENGTH_PERCENTAGE.can_sum_with(CalcUnits::LENGTH));\n        assert!(CalcUnits::LENGTH_PERCENTAGE.can_sum_with(CalcUnits::PERCENTAGE));\n        assert!(CalcUnits::LENGTH_PERCENTAGE.can_sum_with(CalcUnits::LENGTH_PERCENTAGE));\n\n        assert!(!CalcUnits::ANGLE.can_sum_with(CalcUnits::TIME));\n        assert!(CalcUnits::ANGLE.can_sum_with(CalcUnits::ANGLE));\n\n        assert!(!(CalcUnits::ANGLE | CalcUnits::TIME).can_sum_with(CalcUnits::ANGLE));\n        assert!(!CalcUnits::ANGLE.can_sum_with(CalcUnits::ANGLE | CalcUnits::TIME));\n        assert!(\n            !(CalcUnits::ANGLE | CalcUnits::TIME).can_sum_with(CalcUnits::ANGLE | CalcUnits::TIME)\n        );\n    }\n}\n"
  },
  {
    "path": "style/values/generics/color.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for color properties.\n\nuse crate::color::ColorMixItemList;\nuse crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};\nuse crate::derives::*;\nuse crate::values::{\n    computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,\n};\nuse std::fmt::{self, Write};\nuse style_traits::{owned_slice::OwnedSlice, CssWriter, ToCss};\n\n/// This struct represents a combined color from a numeric color and\n/// the current foreground color (currentcolor keyword).\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem, ToTyped)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub enum GenericColor<Percentage> {\n    /// The actual numeric color.\n    Absolute(AbsoluteColor),\n    /// A unresolvable color.\n    ColorFunction(Box<ColorFunction<Self>>),\n    /// The `CurrentColor` keyword.\n    CurrentColor,\n    /// The color-mix() function.\n    ColorMix(Box<GenericColorMix<Self, Percentage>>),\n    /// The contrast-color() function.\n    ContrastColor(Box<Self>),\n}\n\n/// Flags used to modify the calculation of a color mix result.\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]\n#[repr(C)]\npub struct ColorMixFlags(u8);\nbitflags! {\n    impl ColorMixFlags : u8 {\n        /// Normalize the weights of the mix.\n        const NORMALIZE_WEIGHTS = 1 << 0;\n        /// The result should always be converted to the modern color syntax.\n        const RESULT_IN_MODERN_SYNTAX = 1 << 1;\n    }\n}\n\n/// One `(color, percentage)` component of a `color-mix()` expression.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(C)]\npub struct GenericColorMixItem<Color, Percentage> {\n    pub color: Color,\n    pub percentage: Percentage,\n}\n\n/// A restricted version of the css `color-mix()` function, which only supports\n/// percentages.\n///\n/// https://drafts.csswg.org/css-color-5/#color-mix\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(C)]\npub struct GenericColorMix<Color, Percentage> {\n    pub interpolation: ColorInterpolationMethod,\n    pub items: OwnedSlice<GenericColorMixItem<Color, Percentage>>,\n    pub flags: ColorMixFlags,\n}\n\npub use self::GenericColorMix as ColorMix;\n\nimpl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"color-mix(\")?;\n\n        // If the color interpolation method is oklab (which is now the default),\n        // it can be omitted.\n        // See: https://github.com/web-platform-tests/interop/issues/1166\n        if !self.interpolation.is_default() {\n            self.interpolation.to_css(dest)?;\n            dest.write_str(\", \")?;\n        }\n\n        let uniform = self\n            .items\n            .split_first()\n            .map(|(first, rest)| {\n                rest.iter()\n                    .all(|item| item.percentage.to_percentage() == first.percentage.to_percentage())\n            })\n            .unwrap_or(false);\n        let uniform_value = 1.0 / self.items.len() as f32;\n\n        let is_pair = self.items.len() == 2;\n\n        for (index, item) in self.items.iter().enumerate() {\n            if index != 0 {\n                dest.write_str(\", \")?;\n            }\n\n            item.color.to_css(dest)?;\n\n            let omit = if is_pair {\n                let can_omit = |a: &Percentage, b: &Percentage, is_left| {\n                    if a.is_calc() {\n                        return false;\n                    }\n                    if a.to_percentage() == 0.5 {\n                        return b.to_percentage() == 0.5;\n                    }\n                    if is_left {\n                        return false;\n                    }\n                    (1.0 - a.to_percentage() - b.to_percentage()).abs() <= f32::EPSILON\n                };\n\n                let other = &self.items[1 - index].percentage;\n                can_omit(&item.percentage, other, index == 0)\n            } else {\n                !item.percentage.is_calc()\n                    && uniform\n                    && item.percentage.to_percentage() == uniform_value\n            };\n\n            if !omit {\n                dest.write_char(' ')?;\n                item.percentage.to_css(dest)?;\n            }\n        }\n\n        dest.write_char(')')\n    }\n}\n\nimpl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {\n    /// Mix the colors so that we get a single color. If any of the 2 colors are\n    /// not mixable (perhaps not absolute?), then return None.\n    pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>\n    where\n        Percentage: ToPercentage,\n    {\n        use crate::color::mix;\n\n        let mut items = ColorMixItemList::with_capacity(self.items.len());\n        for item in self.items.iter() {\n            items.push(mix::ColorMixItem::new(\n                *item.color.as_absolute()?,\n                item.percentage.to_percentage(),\n            ))\n        }\n\n        Some(mix::mix_many(self.interpolation, items, self.flags))\n    }\n}\n\npub use self::GenericColor as Color;\n\nimpl<Percentage> Color<Percentage> {\n    /// If this color is absolute return it's value, otherwise return None.\n    pub fn as_absolute(&self) -> Option<&AbsoluteColor> {\n        match *self {\n            Self::Absolute(ref absolute) => Some(absolute),\n            _ => None,\n        }\n    }\n\n    /// Returns a color value representing currentcolor.\n    pub fn currentcolor() -> Self {\n        Self::CurrentColor\n    }\n\n    /// Whether it is a currentcolor value (no numeric color component).\n    pub fn is_currentcolor(&self) -> bool {\n        matches!(*self, Self::CurrentColor)\n    }\n\n    /// Whether this color is an absolute color.\n    pub fn is_absolute(&self) -> bool {\n        matches!(*self, Self::Absolute(..))\n    }\n}\n\n/// Either `<color>` or `auto`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericColorOrAuto<C> {\n    /// A `<color>`.\n    Color(C),\n    /// `auto`\n    Auto,\n}\n\npub use self::GenericColorOrAuto as ColorOrAuto;\n\n/// Caret color is effectively a ColorOrAuto, but resolves `auto` to\n/// currentColor.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\npub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);\n\nimpl<C> GenericCaretColor<C> {\n    /// Returns the `auto` value.\n    pub fn auto() -> Self {\n        GenericCaretColor(GenericColorOrAuto::Auto)\n    }\n}\n\npub use self::GenericCaretColor as CaretColor;\n\n/// A light-dark(<light>, <dark>) function.\n#[derive(\n    Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,\n)]\n#[css(function = \"light-dark\", comma)]\n#[repr(C)]\npub struct GenericLightDark<T> {\n    /// The value returned when using a light theme.\n    pub light: T,\n    /// The value returned when using a dark theme.\n    pub dark: T,\n}\n\nimpl<T> GenericLightDark<T> {\n    /// Parse the arguments of the light-dark() function.\n    pub fn parse_args_with<'i>(\n        input: &mut Parser<'i, '_>,\n        mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,\n    ) -> Result<Self, ParseError<'i>> {\n        let light = parse_one(input)?;\n        input.expect_comma()?;\n        let dark = parse_one(input)?;\n        Ok(Self { light, dark })\n    }\n\n    /// Parse the light-dark() function.\n    pub fn parse_with<'i>(\n        input: &mut Parser<'i, '_>,\n        parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"light-dark\")?;\n        input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))\n    }\n}\n\nimpl<T: ToComputedValue> GenericLightDark<T> {\n    /// Choose the light or dark version of this value for computation purposes, and compute it.\n    pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {\n        let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);\n        if cx.for_non_inherited_property {\n            cx.rule_cache_conditions\n                .borrow_mut()\n                .set_color_scheme_dependency(cx.builder.color_scheme);\n        }\n        let chosen = if dark { &self.dark } else { &self.light };\n        chosen.to_computed_value(cx)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/column.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for the column properties.\nuse crate::derives::*;\n\n/// A generic type for `column-count` values.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[typed(todo_derive_fields)]\npub enum GenericColumnCount<PositiveInteger> {\n    /// A positive integer.\n    Integer(PositiveInteger),\n    /// The keyword `auto`.\n    #[animation(error)]\n    Auto,\n}\n\npub use self::GenericColumnCount as ColumnCount;\nimpl<I> ColumnCount<I> {\n    /// Returns whether this value is `auto`.\n    #[inline]\n    pub fn is_auto(self) -> bool {\n        matches!(self, ColumnCount::Auto)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/counters.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for counters-related CSS values.\n\nuse crate::counter_style::CounterStyle;\nuse crate::derives::*;\nuse crate::values::specified::Attr;\nuse crate::values::CustomIdent;\nuse std::fmt::{self, Write};\nuse std::ops::Deref;\nuse style_traits::{CssWriter, ToCss};\n\n/// A name / value pair for counters.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericCounterPair<Integer> {\n    /// The name of the counter.\n    pub name: CustomIdent,\n    /// The value of the counter / increment / etc.\n    pub value: Integer,\n    /// If true, then this represents `reversed(name)`.\n    /// NOTE: It can only be true on `counter-reset` values.\n    pub is_reversed: bool,\n}\npub use self::GenericCounterPair as CounterPair;\n\nimpl<Integer> ToCss for CounterPair<Integer>\nwhere\n    Integer: ToCss + PartialEq<i32>,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_reversed {\n            dest.write_str(\"reversed(\")?;\n        }\n        self.name.to_css(dest)?;\n        if self.is_reversed {\n            dest.write_char(')')?;\n            if self.value == i32::min_value() {\n                return Ok(());\n            }\n        }\n        dest.write_char(' ')?;\n        self.value.to_css(dest)\n    }\n}\n\n/// A generic value for the `counter-increment` property.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericCounterIncrement<I>(#[css(field_bound)] pub GenericCounters<I>);\npub use self::GenericCounterIncrement as CounterIncrement;\n\nimpl<I> CounterIncrement<I> {\n    /// Returns a new value for `counter-increment`.\n    #[inline]\n    pub fn new(counters: Vec<CounterPair<I>>) -> Self {\n        CounterIncrement(Counters(counters.into()))\n    }\n}\n\nimpl<I> Deref for CounterIncrement<I> {\n    type Target = [CounterPair<I>];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &(self.0).0\n    }\n}\n\n/// A generic value for the `counter-set` property.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericCounterSet<I>(#[css(field_bound)] pub GenericCounters<I>);\npub use self::GenericCounterSet as CounterSet;\n\nimpl<I> CounterSet<I> {\n    /// Returns a new value for `counter-set`.\n    #[inline]\n    pub fn new(counters: Vec<CounterPair<I>>) -> Self {\n        CounterSet(Counters(counters.into()))\n    }\n}\n\nimpl<I> Deref for CounterSet<I> {\n    type Target = [CounterPair<I>];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &(self.0).0\n    }\n}\n\n/// A generic value for the `counter-reset` property.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericCounterReset<I>(#[css(field_bound)] pub GenericCounters<I>);\npub use self::GenericCounterReset as CounterReset;\n\nimpl<I> CounterReset<I> {\n    /// Returns a new value for `counter-reset`.\n    #[inline]\n    pub fn new(counters: Vec<CounterPair<I>>) -> Self {\n        CounterReset(Counters(counters.into()))\n    }\n}\n\nimpl<I> Deref for CounterReset<I> {\n    type Target = [CounterPair<I>];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        &(self.0).0\n    }\n}\n\n/// A generic value for lists of counters.\n///\n/// Keyword `none` is represented by an empty vector.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct GenericCounters<I>(\n    #[css(field_bound)]\n    #[css(iterable, if_empty = \"none\")]\n    crate::OwnedSlice<GenericCounterPair<I>>,\n);\npub use self::GenericCounters as Counters;\n\n\n#[inline]\nfn is_decimal(counter_type: &CounterStyle) -> bool {\n    *counter_type == CounterStyle::decimal()\n}\n\n/// The non-normal, non-none values of the content property.\n#[derive(\n    Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToShmem,\n)]\n#[repr(C)]\npub struct GenericContentItems<Image> {\n    /// The actual content items. Note that, past the alt marker, only some subset (strings,\n    /// attr(), counter())\n    pub items: thin_vec::ThinVec<GenericContentItem<Image>>,\n    /// The index at which alt text starts, always non-zero. If equal to items.len(), no alt text\n    /// exists.\n    pub alt_start: usize,\n}\n\nimpl<Image> ToCss for GenericContentItems<Image>\nwhere\n    Image: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        for (i, item) in self.items.iter().enumerate() {\n            if i == self.alt_start {\n                dest.write_str(\" /\")?;\n            }\n            if i != 0 {\n                dest.write_str(\" \")?;\n            }\n            item.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// The specified value for the `content` property.\n///\n/// https://drafts.csswg.org/css-content/#propdef-content\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[typed(todo_derive_fields)]\npub enum GenericContent<Image> {\n    /// `normal` reserved keyword.\n    Normal,\n    /// `none` reserved keyword.\n    None,\n    /// Content items.\n    Items(GenericContentItems<Image>),\n}\n\npub use self::GenericContent as Content;\n\nimpl<Image> Content<Image> {\n    /// Whether `self` represents list of items.\n    #[inline]\n    pub fn is_items(&self) -> bool {\n        matches!(*self, Self::Items(..))\n    }\n\n    /// Set `content` property to `normal`.\n    #[inline]\n    pub fn normal() -> Self {\n        Content::Normal\n    }\n}\n\n/// Items for the `content` property.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    SpecifiedValueInfo,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum GenericContentItem<I> {\n    /// Literal string content.\n    String(crate::OwnedStr),\n    /// `counter(name, style)`.\n    #[css(comma, function)]\n    Counter(CustomIdent, #[css(skip_if = \"is_decimal\")] CounterStyle),\n    /// `counters(name, separator, style)`.\n    #[css(comma, function)]\n    Counters(\n        CustomIdent,\n        crate::OwnedStr,\n        #[css(skip_if = \"is_decimal\")] CounterStyle,\n    ),\n    /// `open-quote`.\n    OpenQuote,\n    /// `close-quote`.\n    CloseQuote,\n    /// `no-open-quote`.\n    NoOpenQuote,\n    /// `no-close-quote`.\n    NoCloseQuote,\n    /// `-moz-alt-content`.\n    #[cfg(feature = \"gecko\")]\n    MozAltContent,\n    /// `-moz-label-content`.\n    /// This is needed to make `accesskey` work for XUL labels. It's basically\n    /// attr(value) otherwise.\n    #[cfg(feature = \"gecko\")]\n    MozLabelContent,\n    /// `attr([namespace? `|`]? ident)`\n    Attr(Attr),\n    /// image-set(url) | url(url)\n    Image(I),\n}\n\npub use self::GenericContentItem as ContentItem;\n"
  },
  {
    "path": "style/values/generics/easing.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS Easing Functions.\n//! https://drafts.csswg.org/css-easing/#timing-functions\n\nuse crate::derives::*;\n\n/// A generic easing function.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToTyped,\n    Serialize,\n    Deserialize,\n)]\n#[value_info(ty = \"TIMING_FUNCTION\")]\n#[repr(u8, C)]\npub enum TimingFunction<Integer, Number, LinearStops> {\n    /// `linear | ease | ease-in | ease-out | ease-in-out`\n    Keyword(TimingKeyword),\n    /// `cubic-bezier(<number>, <number>, <number>, <number>)`\n    #[allow(missing_docs)]\n    #[css(comma, function)]\n    #[typed(skip)]\n    CubicBezier {\n        x1: Number,\n        y1: Number,\n        x2: Number,\n        y2: Number,\n    },\n    /// `step-start | step-end | steps(<integer>, [ <step-position> ]?)`\n    /// `<step-position> = jump-start | jump-end | jump-none | jump-both | start | end`\n    #[css(comma, function)]\n    #[typed(skip)]\n    #[value_info(other_values = \"step-start,step-end\")]\n    Steps(Integer, #[css(skip_if = \"is_end\")] StepPosition),\n    /// linear([<linear-stop>]#)\n    /// <linear-stop> = <output> && <linear-stop-length>?\n    /// <linear-stop-length> = <percentage>{1, 2}\n    #[css(function = \"linear\")]\n    #[typed(skip)]\n    LinearFunction(LinearStops),\n}\n\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n    Serialize,\n    Deserialize,\n)]\n#[repr(u8)]\npub enum TimingKeyword {\n    Linear,\n    Ease,\n    EaseIn,\n    EaseOut,\n    EaseInOut,\n}\n\n/// Before flag, defined as per https://drafts.csswg.org/css-easing/#before-flag\n/// This flag is never user-specified.\n#[allow(missing_docs)]\n#[derive(PartialEq)]\n#[repr(u8)]\npub enum BeforeFlag {\n    Unset,\n    Set,\n}\n\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    Serialize,\n    Deserialize,\n)]\n#[repr(u8)]\npub enum StepPosition {\n    JumpStart,\n    JumpEnd,\n    JumpNone,\n    JumpBoth,\n    Start,\n    End,\n}\n\n#[inline]\nfn is_end(position: &StepPosition) -> bool {\n    *position == StepPosition::JumpEnd || *position == StepPosition::End\n}\n\nimpl<Integer, Number, LinearStops> TimingFunction<Integer, Number, LinearStops> {\n    /// `ease`\n    #[inline]\n    pub fn ease() -> Self {\n        TimingFunction::Keyword(TimingKeyword::Ease)\n    }\n\n    /// Returns true if it is `ease`.\n    #[inline]\n    pub fn is_ease(&self) -> bool {\n        matches!(*self, TimingFunction::Keyword(TimingKeyword::Ease))\n    }\n}\n"
  },
  {
    "path": "style/values/generics/effects.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to effects.\nuse crate::derives::*;\n\nuse crate::values::generics::{NonNegative, ZeroToOne};\n\n/// A generic value for a single `box-shadow`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericBoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> {\n    /// The base shadow.\n    pub base: GenericSimpleShadow<Color, SizeLength, BlurShapeLength>,\n    /// The spread radius.\n    pub spread: ShapeLength,\n    /// Whether this is an inset box shadow.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub inset: bool,\n}\n\npub use self::GenericBoxShadow as BoxShadow;\n\n/// A generic value for a single `filter`.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[animation(no_bound(U))]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericFilter<Angle, Factor, Length, Shadow, U> {\n    /// `blur(<length>)`\n    #[css(function)]\n    Blur(#[animation(field_bound)] NonNegative<Length>),\n    /// `brightness(<factor>)`\n    #[css(function)]\n    Brightness(#[animation(field_bound)] NonNegative<Factor>),\n    /// `contrast(<factor>)`\n    #[css(function)]\n    Contrast(#[animation(field_bound)] NonNegative<Factor>),\n    /// `grayscale(<factor>)`\n    #[css(function)]\n    Grayscale(#[animation(field_bound)] ZeroToOne<Factor>),\n    /// `hue-rotate(<angle>)`\n    #[css(function)]\n    HueRotate(Angle),\n    /// `invert(<factor>)`\n    #[css(function)]\n    Invert(#[animation(field_bound)] ZeroToOne<Factor>),\n    /// `opacity(<factor>)`\n    #[css(function)]\n    Opacity(#[animation(field_bound)] ZeroToOne<Factor>),\n    /// `saturate(<factor>)`\n    #[css(function)]\n    Saturate(#[animation(field_bound)] NonNegative<Factor>),\n    /// `sepia(<factor>)`\n    #[css(function)]\n    Sepia(#[animation(field_bound)] ZeroToOne<Factor>),\n    /// `drop-shadow(...)`\n    #[css(function)]\n    DropShadow(Shadow),\n    /// `<url>`\n    #[animation(error)]\n    Url(U),\n}\n\npub use self::GenericFilter as Filter;\n\n/// A generic value for the `drop-shadow()` filter and the `text-shadow` property.\n///\n/// Contrary to the canonical order from the spec, the color is serialised\n/// first, like in Gecko and Webkit.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericSimpleShadow<Color, SizeLength, ShapeLength> {\n    /// Color.\n    pub color: Color,\n    /// Horizontal radius.\n    pub horizontal: SizeLength,\n    /// Vertical radius.\n    pub vertical: SizeLength,\n    /// Blur radius.\n    pub blur: ShapeLength,\n}\n\npub use self::GenericSimpleShadow as SimpleShadow;\n"
  },
  {
    "path": "style/values/generics/flex.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to flexbox.\nuse crate::derives::*;\n\n/// A generic value for the `flex-basis` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\npub enum GenericFlexBasis<S> {\n    /// `content`\n    Content,\n    /// `<width>`\n    Size(S),\n}\n\npub use self::GenericFlexBasis as FlexBasis;\n"
  },
  {
    "path": "style/values/generics/font.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for font stuff.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::animated::ToAnimatedZero;\nuse crate::{One, Zero};\nuse byteorder::{BigEndian, ReadBytesExt};\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse std::io::Cursor;\nuse style_traits::{\n    CssString, CssWriter, KeywordValue, ParseError, StyleParseErrorKind, ToCss, ToTyped, TypedValue,\n};\nuse thin_vec::ThinVec;\n\n/// A trait for values that are labelled with a FontTag (for feature and\n/// variation settings).\npub trait TaggedFontValue {\n    /// The value's tag.\n    fn tag(&self) -> FontTag;\n}\n\n/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct FeatureTagValue<Integer> {\n    /// A four-character tag, packed into a u32 (one byte per character).\n    pub tag: FontTag,\n    /// The actual value.\n    pub value: Integer,\n}\n\nimpl<T> TaggedFontValue for FeatureTagValue<T> {\n    fn tag(&self) -> FontTag {\n        self.tag\n    }\n}\n\nimpl<Integer> ToCss for FeatureTagValue<Integer>\nwhere\n    Integer: One + ToCss + PartialEq,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.tag.to_css(dest)?;\n        // Don't serialize the default value.\n        if !self.value.is_one() {\n            dest.write_char(' ')?;\n            self.value.to_css(dest)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Variation setting for a single feature, see:\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\npub struct VariationValue<Number> {\n    /// A four-character tag, packed into a u32 (one byte per character).\n    #[animation(constant)]\n    pub tag: FontTag,\n    /// The actual value.\n    pub value: Number,\n}\n\nimpl<T> TaggedFontValue for VariationValue<T> {\n    fn tag(&self) -> FontTag {\n        self.tag\n    }\n}\n\n/// A value both for font-variation-settings and font-feature-settings.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[css(comma)]\n#[typed(todo_derive_fields)]\npub struct FontSettings<T>(#[css(if_empty = \"normal\", iterable)] pub Box<[T]>);\n\nimpl<T> FontSettings<T> {\n    /// Default value of font settings as `normal`.\n    #[inline]\n    pub fn normal() -> Self {\n        FontSettings(vec![].into_boxed_slice())\n    }\n}\n\nimpl<T: Parse> Parse for FontSettings<T> {\n    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings\n    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(Self::normal());\n        }\n\n        Ok(FontSettings(\n            input\n                .parse_comma_separated(|i| T::parse(context, i))?\n                .into_boxed_slice(),\n        ))\n    }\n}\n\n/// A font four-character tag, represented as a u32 for convenience.\n///\n/// See:\n///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def\n///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings\n///\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\npub struct FontTag(pub u32);\n\nimpl ToCss for FontTag {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        use byteorder::ByteOrder;\n        use std::str;\n\n        let mut raw = [0u8; 4];\n        BigEndian::write_u32(&mut raw, self.0);\n        str::from_utf8(&raw).unwrap_or_default().to_css(dest)\n    }\n}\n\nimpl Parse for FontTag {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let tag = input.expect_string()?;\n\n        // allowed strings of length 4 containing chars: <U+20, U+7E>\n        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let mut raw = Cursor::new(tag.as_bytes());\n        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))\n    }\n}\n\n/// A generic value for the `font-style` property.\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-style-prop\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[value_info(other_values = \"normal\")]\npub enum FontStyle<Angle> {\n    // Note that 'oblique 0deg' represents 'normal', and will serialize as such.\n    #[value_info(starts_with_keyword)]\n    Oblique(Angle),\n    #[animation(error)]\n    Italic,\n}\n\nimpl<Angle: Zero> FontStyle<Angle> {\n    /// Return the 'normal' value, which is represented as 'oblique 0deg'.\n    pub fn normal() -> Self {\n        Self::Oblique(Angle::zero())\n    }\n}\n\n/// A generic value for the `font-size-adjust` property.\n///\n/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop\n#[allow(missing_docs)]\n#[repr(u8)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub enum GenericFontSizeAdjust<Factor> {\n    #[animation(error)]\n    None,\n    #[value_info(starts_with_keyword)]\n    ExHeight(Factor),\n    #[value_info(starts_with_keyword)]\n    CapHeight(Factor),\n    #[value_info(starts_with_keyword)]\n    ChWidth(Factor),\n    #[value_info(starts_with_keyword)]\n    IcWidth(Factor),\n    #[value_info(starts_with_keyword)]\n    IcHeight(Factor),\n}\n\nimpl<Factor: ToCss> ToCss for GenericFontSizeAdjust<Factor> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let (prefix, value) = match self {\n            Self::None => return dest.write_str(\"none\"),\n            Self::ExHeight(v) => (\"\", v),\n            Self::CapHeight(v) => (\"cap-height \", v),\n            Self::ChWidth(v) => (\"ch-width \", v),\n            Self::IcWidth(v) => (\"ic-width \", v),\n            Self::IcHeight(v) => (\"ic-height \", v),\n        };\n\n        dest.write_str(prefix)?;\n        value.to_css(dest)\n    }\n}\n\nimpl<Factor: ToTyped> ToTyped for GenericFontSizeAdjust<Factor> {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        match self {\n            Self::None => {\n                dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\"none\"))));\n                Ok(())\n            },\n            Self::ExHeight(v) => v.to_typed(dest),\n            _ => Err(()),\n        }\n    }\n}\n\n/// A generic value for the `line-height` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToCss,\n    ToShmem,\n    Parse,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericLineHeight<N, L> {\n    /// `normal`\n    Normal,\n    /// `-moz-block-height`\n    #[cfg(feature = \"gecko\")]\n    #[parse(condition = \"ParserContext::in_ua_sheet\")]\n    MozBlockHeight,\n    /// `<number>`\n    Number(N),\n    /// `<length-percentage>`\n    Length(L),\n}\n\npub use self::GenericLineHeight as LineHeight;\n\nimpl<N, L> ToAnimatedZero for LineHeight<N, L> {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\nimpl<N, L> LineHeight<N, L> {\n    /// Returns `normal`.\n    #[inline]\n    pub fn normal() -> Self {\n        LineHeight::Normal\n    }\n}\n"
  },
  {
    "path": "style/values/generics/grid.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for the handling of\n//! [grids](https://drafts.csswg.org/css-grid/).\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::specified;\nuse crate::values::{CSSFloat, CustomIdent};\nuse crate::{One, Zero};\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse std::{cmp, usize};\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// These are the limits that we choose to clamp grid line numbers to.\n/// http://drafts.csswg.org/css-grid/#overlarge-grids\n/// line_num is clamped to this range at parse time.\npub const MIN_GRID_LINE: i32 = -10000;\n/// See above.\npub const MAX_GRID_LINE: i32 = 10000;\n\n/// A `<grid-line>` type.\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-grid-row-start-grid-line>\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericGridLine<Integer> {\n    /// A custom identifier for named lines, or the empty atom otherwise.\n    ///\n    /// <https://drafts.csswg.org/css-grid/#grid-placement-slot>\n    pub ident: CustomIdent,\n    /// Denotes the nth grid line from grid item's placement.\n    ///\n    /// This is clamped by MIN_GRID_LINE and MAX_GRID_LINE.\n    ///\n    /// NOTE(emilio): If we ever allow animating these we need to either do\n    /// something more complicated for the clamping, or do this clamping at\n    /// used-value time.\n    pub line_num: Integer,\n    /// Flag to check whether it's a `span` keyword.\n    pub is_span: bool,\n}\n\npub use self::GenericGridLine as GridLine;\n\nimpl<Integer> GridLine<Integer>\nwhere\n    Integer: PartialEq + Zero,\n{\n    /// The `auto` value.\n    pub fn auto() -> Self {\n        Self {\n            is_span: false,\n            line_num: Zero::zero(),\n            ident: CustomIdent(atom!(\"\")),\n        }\n    }\n\n    /// Check whether this `<grid-line>` represents an `auto` value.\n    pub fn is_auto(&self) -> bool {\n        self.ident.0 == atom!(\"\") && self.line_num.is_zero() && !self.is_span\n    }\n\n    /// Check whether this `<grid-line>` represents a `<custom-ident>` value.\n    pub fn is_ident_only(&self) -> bool {\n        self.ident.0 != atom!(\"\") && self.line_num.is_zero() && !self.is_span\n    }\n\n    /// Check if `self` makes `other` omittable according to the rules at:\n    /// https://drafts.csswg.org/css-grid/#propdef-grid-column\n    /// https://drafts.csswg.org/css-grid/#propdef-grid-area\n    pub fn can_omit(&self, other: &Self) -> bool {\n        if self.is_ident_only() {\n            self == other\n        } else {\n            other.is_auto()\n        }\n    }\n}\n\nimpl<Integer> ToCss for GridLine<Integer>\nwhere\n    Integer: ToCss + PartialEq + Zero + One,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        // 1. `auto`\n        if self.is_auto() {\n            return dest.write_str(\"auto\");\n        }\n\n        // 2. `<custom-ident>`\n        if self.is_ident_only() {\n            return self.ident.to_css(dest);\n        }\n\n        // 3. `[ span && [ <integer [1,∞]> || <custom-ident> ] ]`\n        let has_ident = self.ident.0 != atom!(\"\");\n        if self.is_span {\n            dest.write_str(\"span\")?;\n            debug_assert!(!self.line_num.is_zero() || has_ident);\n\n            // We omit `line_num` if\n            // 1. we don't specify it, or\n            // 2. it is the default value, i.e. 1.0, and the ident is specified.\n            // https://drafts.csswg.org/css-grid/#grid-placement-span-int\n            if !self.line_num.is_zero() && !(self.line_num.is_one() && has_ident) {\n                dest.write_char(' ')?;\n                self.line_num.to_css(dest)?;\n            }\n\n            if has_ident {\n                dest.write_char(' ')?;\n                self.ident.to_css(dest)?;\n            }\n            return Ok(());\n        }\n\n        // 4. `[ <integer [-∞,-1]> | <integer [1,∞]> ] && <custom-ident>? ]`\n        debug_assert!(!self.line_num.is_zero());\n        self.line_num.to_css(dest)?;\n        if has_ident {\n            dest.write_char(' ')?;\n            self.ident.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl Parse for GridLine<specified::Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut grid_line = Self::auto();\n        if input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok() {\n            return Ok(grid_line);\n        }\n\n        // <custom-ident> | [ <integer> && <custom-ident>? ] | [ span && [ <integer> || <custom-ident> ] ]\n        // This <grid-line> horror is simply,\n        // [ span? && [ <custom-ident> || <integer> ] ]\n        // And, for some magical reason, \"span\" should be the first or last value and not in-between.\n        let mut val_before_span = false;\n\n        for _ in 0..3 {\n            // Maximum possible entities for <grid-line>\n            let location = input.current_source_location();\n            if input.try_parse(|i| i.expect_ident_matching(\"span\")).is_ok() {\n                if grid_line.is_span {\n                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n\n                if !grid_line.line_num.is_zero() || grid_line.ident.0 != atom!(\"\") {\n                    val_before_span = true;\n                }\n\n                grid_line.is_span = true;\n            } else if let Ok(i) = input.try_parse(|i| specified::Integer::parse(context, i)) {\n                // FIXME(emilio): Probably shouldn't reject if it's calc()...\n                let value = i.value();\n                if value == 0 || val_before_span || !grid_line.line_num.is_zero() {\n                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n\n                grid_line.line_num = specified::Integer::new(cmp::max(\n                    MIN_GRID_LINE,\n                    cmp::min(value, MAX_GRID_LINE),\n                ));\n            } else if let Ok(name) = input.try_parse(|i| CustomIdent::parse(i, &[\"auto\"])) {\n                if val_before_span || grid_line.ident.0 != atom!(\"\") {\n                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                // NOTE(emilio): `span` is consumed above, so we only need to\n                // reject `auto`.\n                grid_line.ident = name;\n            } else {\n                break;\n            }\n        }\n\n        if grid_line.is_auto() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        if grid_line.is_span {\n            if !grid_line.line_num.is_zero() {\n                if grid_line.line_num.value() <= 0 {\n                    // disallow negative integers for grid spans\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n            } else if grid_line.ident.0 == atom!(\"\") {\n                // integer could be omitted\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n        }\n\n        Ok(grid_line)\n    }\n}\n\n/// A track breadth for explicit grid track sizing. It's generic solely to\n/// avoid re-implementing it for the computed type.\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-track-breadth>\n#[derive(\n    Animate,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericTrackBreadth<L> {\n    /// The generic type is almost always a non-negative `<length-percentage>`\n    Breadth(L),\n    /// A flex fraction specified in `fr` units.\n    #[css(dimension)]\n    Fr(CSSFloat),\n    /// `auto`\n    Auto,\n    /// `min-content`\n    MinContent,\n    /// `max-content`\n    MaxContent,\n}\n\npub use self::GenericTrackBreadth as TrackBreadth;\n\nimpl<L> TrackBreadth<L> {\n    /// Check whether this is a `<fixed-breadth>` (i.e., it only has `<length-percentage>`)\n    ///\n    /// <https://drafts.csswg.org/css-grid/#typedef-fixed-breadth>\n    #[inline]\n    pub fn is_fixed(&self) -> bool {\n        matches!(*self, TrackBreadth::Breadth(..))\n    }\n}\n\n/// A `<track-size>` type for explicit grid track sizing. Like `<track-breadth>`, this is\n/// generic only to avoid code bloat. It only takes `<length-percentage>`\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-track-size>\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericTrackSize<L> {\n    /// A flexible `<track-breadth>`\n    Breadth(GenericTrackBreadth<L>),\n    /// A `minmax` function for a range over an inflexible `<track-breadth>`\n    /// and a flexible `<track-breadth>`\n    ///\n    /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-minmax>\n    #[css(function)]\n    Minmax(GenericTrackBreadth<L>, GenericTrackBreadth<L>),\n    /// A `fit-content` function.\n    ///\n    /// This stores a TrackBreadth<L> for convenience, but it can only be a\n    /// LengthPercentage.\n    ///\n    /// <https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-fit-content>\n    #[css(function)]\n    FitContent(GenericTrackBreadth<L>),\n}\n\npub use self::GenericTrackSize as TrackSize;\n\nimpl<L> TrackSize<L> {\n    /// The initial value.\n    const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);\n\n    /// Returns the initial value.\n    pub const fn initial_value() -> Self {\n        Self::INITIAL_VALUE\n    }\n\n    /// Returns true if `self` is the initial value.\n    pub fn is_initial(&self) -> bool {\n        matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585\n    }\n\n    /// Check whether this is a `<fixed-size>`\n    ///\n    /// <https://drafts.csswg.org/css-grid/#typedef-fixed-size>\n    pub fn is_fixed(&self) -> bool {\n        match *self {\n            TrackSize::Breadth(ref breadth) => breadth.is_fixed(),\n            // For minmax function, it could be either\n            // minmax(<fixed-breadth>, <track-breadth>) or minmax(<inflexible-breadth>, <fixed-breadth>),\n            // and since both variants are a subset of minmax(<inflexible-breadth>, <track-breadth>), we only\n            // need to make sure that they're fixed. So, we don't have to modify the parsing function.\n            TrackSize::Minmax(ref breadth_1, ref breadth_2) => {\n                if breadth_1.is_fixed() {\n                    return true; // the second value is always a <track-breadth>\n                }\n\n                match *breadth_1 {\n                    TrackBreadth::Fr(_) => false, // should be <inflexible-breadth> at this point\n                    _ => breadth_2.is_fixed(),\n                }\n            },\n            TrackSize::FitContent(_) => false,\n        }\n    }\n}\n\nimpl<L> Default for TrackSize<L> {\n    fn default() -> Self {\n        Self::initial_value()\n    }\n}\n\nimpl<L: ToCss> ToCss for TrackSize<L> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            TrackSize::Breadth(ref breadth) => breadth.to_css(dest),\n            TrackSize::Minmax(ref min, ref max) => {\n                // According to gecko minmax(auto, <flex>) is equivalent to <flex>,\n                // and both are serialized as <flex>.\n                if let TrackBreadth::Auto = *min {\n                    if let TrackBreadth::Fr(_) = *max {\n                        return max.to_css(dest);\n                    }\n                }\n\n                dest.write_str(\"minmax(\")?;\n                min.to_css(dest)?;\n                dest.write_str(\", \")?;\n                max.to_css(dest)?;\n                dest.write_char(')')\n            },\n            TrackSize::FitContent(ref lp) => {\n                dest.write_str(\"fit-content(\")?;\n                lp.to_css(dest)?;\n                dest.write_char(')')\n            },\n        }\n    }\n}\n\n/// A `<track-size>+`.\n/// We use the empty slice as `auto`, and always parse `auto` as an empty slice.\n/// This means it's impossible to have a slice containing only one auto item.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct GenericImplicitGridTracks<T>(\n    #[css(if_empty = \"auto\", iterable)] pub crate::OwnedSlice<T>,\n);\n\npub use self::GenericImplicitGridTracks as ImplicitGridTracks;\n\nimpl<T: fmt::Debug + Default + PartialEq> ImplicitGridTracks<T> {\n    /// Returns true if current value is same as its initial value (i.e. auto).\n    pub fn is_initial(&self) -> bool {\n        debug_assert_ne!(\n            *self,\n            ImplicitGridTracks(crate::OwnedSlice::from(vec![Default::default()]))\n        );\n        self.0.is_empty()\n    }\n}\n\n/// Helper function for serializing identifiers with a prefix and suffix, used\n/// for serializing <line-names> (in grid).\npub fn concat_serialize_idents<W>(\n    prefix: &str,\n    suffix: &str,\n    slice: &[CustomIdent],\n    sep: &str,\n    dest: &mut CssWriter<W>,\n) -> fmt::Result\nwhere\n    W: Write,\n{\n    if let Some((ref first, rest)) = slice.split_first() {\n        dest.write_str(prefix)?;\n        first.to_css(dest)?;\n        for thing in rest {\n            dest.write_str(sep)?;\n            thing.to_css(dest)?;\n        }\n\n        dest.write_str(suffix)?;\n    }\n\n    Ok(())\n}\n\n/// The initial argument of the `repeat` function.\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum RepeatCount<Integer> {\n    /// A positive integer. This is allowed only for `<track-repeat>` and `<fixed-repeat>`\n    Number(Integer),\n    /// An `<auto-fill>` keyword allowed only for `<auto-repeat>`\n    AutoFill,\n    /// An `<auto-fit>` keyword allowed only for `<auto-repeat>`\n    AutoFit,\n}\n\nimpl Parse for RepeatCount<specified::Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(mut i) = input.try_parse(|i| specified::Integer::parse_positive(context, i)) {\n            if i.value() > MAX_GRID_LINE {\n                i = specified::Integer::new(MAX_GRID_LINE);\n            }\n            return Ok(RepeatCount::Number(i));\n        }\n        try_match_ident_ignore_ascii_case! { input,\n            \"auto-fill\" => Ok(RepeatCount::AutoFill),\n            \"auto-fit\" => Ok(RepeatCount::AutoFit),\n        }\n    }\n}\n\n/// The structure containing `<line-names>` and `<track-size>` values.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function = \"repeat\")]\n#[repr(C)]\npub struct GenericTrackRepeat<L, I> {\n    /// The number of times for the value to be repeated (could also be `auto-fit` or `auto-fill`)\n    pub count: RepeatCount<I>,\n    /// `<line-names>` accompanying `<track_size>` values.\n    ///\n    /// If there's no `<line-names>`, then it's represented by an empty vector.\n    /// For N `<track-size>` values, there will be N+1 `<line-names>`, and so this vector's\n    /// length is always one value more than that of the `<track-size>`.\n    pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,\n    /// `<track-size>` values.\n    pub track_sizes: crate::OwnedSlice<GenericTrackSize<L>>,\n}\n\npub use self::GenericTrackRepeat as TrackRepeat;\n\nimpl<L: ToCss, I: ToCss> ToCss for TrackRepeat<L, I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"repeat(\")?;\n        self.count.to_css(dest)?;\n        dest.write_str(\", \")?;\n\n        let mut line_names_iter = self.line_names.iter();\n        for (i, (ref size, ref names)) in self\n            .track_sizes\n            .iter()\n            .zip(&mut line_names_iter)\n            .enumerate()\n        {\n            if i > 0 {\n                dest.write_char(' ')?;\n            }\n\n            concat_serialize_idents(\"[\", \"] \", names, \" \", dest)?;\n            size.to_css(dest)?;\n        }\n\n        if let Some(line_names_last) = line_names_iter.next() {\n            concat_serialize_idents(\" [\", \"]\", line_names_last, \" \", dest)?;\n        }\n\n        dest.write_char(')')?;\n\n        Ok(())\n    }\n}\n\n/// Track list values. Can be <track-size> or <track-repeat>\n#[derive(\n    Animate,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericTrackListValue<LengthPercentage, Integer> {\n    /// A <track-size> value.\n    TrackSize(#[animation(field_bound)] GenericTrackSize<LengthPercentage>),\n    /// A <track-repeat> value.\n    TrackRepeat(#[animation(field_bound)] GenericTrackRepeat<LengthPercentage, Integer>),\n}\n\npub use self::GenericTrackListValue as TrackListValue;\n\nimpl<L, I> TrackListValue<L, I> {\n    // FIXME: can't use TrackSize::initial_value() here b/c rustc error \"is not yet stable as a const fn\"\n    const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));\n\n    fn is_repeat(&self) -> bool {\n        matches!(*self, TrackListValue::TrackRepeat(..))\n    }\n\n    /// Returns true if `self` is the initial value.\n    pub fn is_initial(&self) -> bool {\n        matches!(\n            *self,\n            TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))\n        ) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585\n    }\n}\n\nimpl<L, I> Default for TrackListValue<L, I> {\n    #[inline]\n    fn default() -> Self {\n        Self::INITIAL_VALUE\n    }\n}\n\n/// A grid `<track-list>` type.\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-track-list>\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericTrackList<LengthPercentage, Integer> {\n    /// The index in `values` where our `<auto-repeat>` value is, if in bounds.\n    #[css(skip)]\n    pub auto_repeat_index: usize,\n    /// A vector of `<track-size> | <track-repeat>` values.\n    pub values: crate::OwnedSlice<GenericTrackListValue<LengthPercentage, Integer>>,\n    /// `<line-names>` accompanying `<track-size> | <track-repeat>` values.\n    ///\n    /// If there's no `<line-names>`, then it's represented by an empty vector.\n    /// For N values, there will be N+1 `<line-names>`, and so this vector's\n    /// length is always one value more than that of the `<track-size>`.\n    pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,\n}\n\npub use self::GenericTrackList as TrackList;\n\nimpl<L, I> TrackList<L, I> {\n    /// Whether this track list is an explicit track list (that is, doesn't have\n    /// any repeat values).\n    pub fn is_explicit(&self) -> bool {\n        !self.values.iter().any(|v| v.is_repeat())\n    }\n\n    /// Whether this track list has an `<auto-repeat>` value.\n    pub fn has_auto_repeat(&self) -> bool {\n        self.auto_repeat_index < self.values.len()\n    }\n}\n\nimpl<L: ToCss, I: ToCss> ToCss for TrackList<L, I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let mut values_iter = self.values.iter().peekable();\n        let mut line_names_iter = self.line_names.iter().peekable();\n\n        for idx in 0.. {\n            let names = line_names_iter.next().unwrap(); // This should exist!\n            concat_serialize_idents(\"[\", \"]\", names, \" \", dest)?;\n\n            match values_iter.next() {\n                Some(value) => {\n                    if !names.is_empty() {\n                        dest.write_char(' ')?;\n                    }\n\n                    value.to_css(dest)?;\n                },\n                None => break,\n            }\n\n            if values_iter.peek().is_some()\n                || line_names_iter.peek().map_or(false, |v| !v.is_empty())\n                || (idx + 1 == self.auto_repeat_index)\n            {\n                dest.write_char(' ')?;\n            }\n        }\n\n        Ok(())\n    }\n}\n\n/// The `<name-repeat>` for subgrids.\n///\n/// <name-repeat> = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+)\n///\n/// https://drafts.csswg.org/css-grid/#typedef-name-repeat\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericNameRepeat<I> {\n    /// The number of times for the value to be repeated (could also be `auto-fill`).\n    /// Note: `RepeatCount` accepts `auto-fit`, so we should reject it after parsing it.\n    pub count: RepeatCount<I>,\n    /// This represents `<line-names>+`. The length of the outer vector is at least one.\n    pub line_names: crate::OwnedSlice<crate::OwnedSlice<CustomIdent>>,\n}\n\npub use self::GenericNameRepeat as NameRepeat;\n\nimpl<I: ToCss> ToCss for NameRepeat<I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"repeat(\")?;\n        self.count.to_css(dest)?;\n        dest.write_char(',')?;\n\n        for ref names in self.line_names.iter() {\n            if names.is_empty() {\n                // Note: concat_serialize_idents() skip the empty list so we have to handle it\n                // manually for NameRepeat.\n                dest.write_str(\" []\")?;\n            } else {\n                concat_serialize_idents(\" [\", \"]\", names, \" \", dest)?;\n            }\n        }\n\n        dest.write_char(')')\n    }\n}\n\nimpl<I> NameRepeat<I> {\n    /// Returns true if it is auto-fill.\n    #[inline]\n    pub fn is_auto_fill(&self) -> bool {\n        matches!(self.count, RepeatCount::AutoFill)\n    }\n}\n\n/// A single value for `<line-names>` or `<name-repeat>`.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericLineNameListValue<I> {\n    /// `<line-names>`.\n    LineNames(crate::OwnedSlice<CustomIdent>),\n    /// `<name-repeat>`.\n    Repeat(GenericNameRepeat<I>),\n}\n\npub use self::GenericLineNameListValue as LineNameListValue;\n\nimpl<I: ToCss> ToCss for LineNameListValue<I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Self::Repeat(ref r) => r.to_css(dest),\n            Self::LineNames(ref names) => {\n                dest.write_char('[')?;\n\n                if let Some((ref first, rest)) = names.split_first() {\n                    first.to_css(dest)?;\n                    for name in rest {\n                        dest.write_char(' ')?;\n                        name.to_css(dest)?;\n                    }\n                }\n\n                dest.write_char(']')\n            },\n        }\n    }\n}\n\n/// The `<line-name-list>` for subgrids.\n///\n/// <line-name-list> = [ <line-names> | <name-repeat> ]+\n/// <name-repeat> = repeat( [ <integer [1,∞]> | auto-fill ], <line-names>+)\n///\n/// https://drafts.csswg.org/css-grid/#typedef-line-name-list\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericLineNameList<I> {\n    /// The pre-computed length of line_names, without the length of repeat(auto-fill, ...).\n    // We precomputed this at parsing time, so we can avoid an extra loop when expanding\n    // repeat(auto-fill).\n    pub expanded_line_names_length: usize,\n    /// The line name list.\n    pub line_names: crate::OwnedSlice<GenericLineNameListValue<I>>,\n}\n\npub use self::GenericLineNameList as LineNameList;\n\nimpl<I: ToCss> ToCss for LineNameList<I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"subgrid\")?;\n\n        for value in self.line_names.iter() {\n            dest.write_char(' ')?;\n            value.to_css(dest)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Variants for `<grid-template-rows> | <grid-template-columns>`\n#[derive(\n    Animate,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[value_info(other_values = \"subgrid\")]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericGridTemplateComponent<L, I> {\n    /// `none` value.\n    None,\n    /// The grid `<track-list>`\n    TrackList(\n        #[animation(field_bound)]\n        #[compute(field_bound)]\n        #[resolve(field_bound)]\n        #[shmem(field_bound)]\n        Box<GenericTrackList<L, I>>,\n    ),\n    /// A `subgrid <line-name-list>?`\n    /// TODO: Support animations for this after subgrid is addressed in [grid-2] spec.\n    #[animation(error)]\n    Subgrid(Box<GenericLineNameList<I>>),\n    /// `masonry` value.\n    /// https://github.com/w3c/csswg-drafts/issues/4650\n    Masonry,\n}\n\npub use self::GenericGridTemplateComponent as GridTemplateComponent;\n\nimpl<L, I> GridTemplateComponent<L, I> {\n    /// The initial value.\n    const INITIAL_VALUE: Self = Self::None;\n\n    /// Returns length of the <track-list>s <track-size>\n    pub fn track_list_len(&self) -> usize {\n        match *self {\n            GridTemplateComponent::TrackList(ref tracklist) => tracklist.values.len(),\n            _ => 0,\n        }\n    }\n\n    /// Returns true if `self` is the initial value.\n    pub fn is_initial(&self) -> bool {\n        matches!(*self, Self::None) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585\n    }\n}\n\nimpl<L, I> Default for GridTemplateComponent<L, I> {\n    #[inline]\n    fn default() -> Self {\n        Self::INITIAL_VALUE\n    }\n}\n"
  },
  {
    "path": "style/values/generics/image.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for the handling of [images].\n//!\n//! [images]: https://drafts.csswg.org/css-images/#image-values\n\nuse crate::color::mix::ColorInterpolationMethod;\nuse crate::custom_properties;\nuse crate::derives::*;\nuse crate::values::generics::NonNegative;\nuse crate::values::generics::{color::GenericLightDark, position::PositionComponent, Optional};\nuse crate::values::serialize_atom_identifier;\nuse crate::{Atom, Zero};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n/// An `<image> | none` value.\n///\n/// https://drafts.csswg.org/css-images/#image-values\n#[derive(Clone, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToResolvedValue, ToShmem, ToTyped)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {\n    /// `none` variant.\n    None,\n\n    /// A `<url()>` image.\n    Url(ImageUrl),\n\n    /// A `<gradient>` image.  Gradients are rather large, and not nearly as\n    /// common as urls, so we box them here to keep the size of this enum sane.\n    Gradient(Box<G>),\n\n    /// A `-moz-element(# <element-id>)`\n    #[cfg(feature = \"gecko\")]\n    #[css(function = \"-moz-element\")]\n    Element(Atom),\n\n    /// A `-moz-symbolic-icon(<icon-id>)`\n    /// NOTE(emilio): #[css(skip)] only really affects SpecifiedValueInfo, which we want because\n    /// this is chrome-only.\n    #[cfg(feature = \"gecko\")]\n    #[css(function, skip)]\n    MozSymbolicIcon(Atom),\n\n    /// A paint worklet image.\n    /// <https://drafts.css-houdini.org/css-paint-api/>\n    #[cfg(feature = \"servo\")]\n    PaintWorklet(Box<PaintWorklet>),\n\n    /// A `<cross-fade()>` image. Storing this directly inside of\n    /// GenericImage increases the size by 8 bytes so we box it here\n    /// and store images directly inside of cross-fade instead of\n    /// boxing them there.\n    CrossFade(Box<GenericCrossFade<Self, Color, Percentage>>),\n\n    /// An `image-set()` function.\n    ImageSet(Box<GenericImageSet<Self, Resolution>>),\n\n    /// A `light-dark()` function.\n    /// NOTE(emilio): #[css(skip)] only affects SpecifiedValueInfo. Remove or make conditional\n    /// if/when shipping light-dark() for content.\n    LightDark(#[css(skip)] Box<GenericLightDark<Self>>),\n}\n\npub use self::GenericImage as Image;\n\n/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>\n#[derive(\n    Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue, ToShmem, ToCss, ToComputedValue,\n)]\n#[css(comma, function = \"cross-fade\")]\n#[repr(C)]\npub struct GenericCrossFade<Image, Color, Percentage> {\n    /// All of the image percent pairings passed as arguments to\n    /// cross-fade.\n    #[css(iterable)]\n    pub elements: crate::OwnedSlice<GenericCrossFadeElement<Image, Color, Percentage>>,\n}\n\n/// An optional percent and a cross fade image.\n#[derive(\n    Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,\n)]\n#[repr(C)]\npub struct GenericCrossFadeElement<Image, Color, Percentage> {\n    /// The percent of the final image that `image` will be.\n    pub percent: Optional<Percentage>,\n    /// A color or image that will be blended when cross-fade is\n    /// evaluated.\n    pub image: GenericCrossFadeImage<Image, Color>,\n}\n\n/// An image or a color. `cross-fade` takes either when blending\n/// images together.\n#[derive(\n    Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, ToCss,\n)]\n#[repr(C, u8)]\npub enum GenericCrossFadeImage<I, C> {\n    /// A boxed image value. Boxing provides indirection so images can\n    /// be cross-fades and cross-fades can be images.\n    Image(I),\n    /// A color value.\n    Color(C),\n}\n\npub use self::GenericCrossFade as CrossFade;\npub use self::GenericCrossFadeElement as CrossFadeElement;\npub use self::GenericCrossFadeImage as CrossFadeImage;\n\n/// https://drafts.csswg.org/css-images-4/#image-set-notation\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)]\n#[css(comma, function = \"image-set\")]\n#[repr(C)]\npub struct GenericImageSet<Image, Resolution> {\n    /// The index of the selected candidate. usize::MAX for specified values or invalid images.\n    #[css(skip)]\n    pub selected_index: usize,\n\n    /// All of the image and resolution pairs.\n    #[css(iterable)]\n    pub items: crate::OwnedSlice<GenericImageSetItem<Image, Resolution>>,\n}\n\n/// An optional percent and a cross fade image.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\n#[repr(C)]\npub struct GenericImageSetItem<Image, Resolution> {\n    /// `<image>`. `<string>` is converted to `Image::Url` at parse time.\n    pub image: Image,\n    /// The `<resolution>`.\n    ///\n    /// TODO: Skip serialization if it is 1x.\n    pub resolution: Resolution,\n\n    /// The `type(<string>)`\n    /// (Optional) Specify the image's MIME type\n    pub mime_type: crate::OwnedStr,\n\n    /// True if mime_type has been specified\n    pub has_mime_type: bool,\n}\n\nimpl<I: style_traits::ToCss, R: style_traits::ToCss> ToCss for GenericImageSetItem<I, R> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.image.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.resolution.to_css(dest)?;\n\n        if self.has_mime_type {\n            dest.write_char(' ')?;\n            dest.write_str(\"type(\")?;\n            self.mime_type.to_css(dest)?;\n            dest.write_char(')')?;\n        }\n        Ok(())\n    }\n}\n\npub use self::GenericImageSet as ImageSet;\npub use self::GenericImageSetItem as ImageSetItem;\n\n/// State flags stored on each variant of a Gradient.\n#[derive(\n    Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[repr(C)]\npub struct GradientFlags(u8);\nbitflags! {\n    impl GradientFlags: u8 {\n        /// Set if this is a repeating gradient.\n        const REPEATING = 1 << 0;\n        /// Set if the color interpolation method matches the default for the items.\n        const HAS_DEFAULT_COLOR_INTERPOLATION_METHOD = 1 << 1;\n    }\n}\n\n/// A CSS gradient.\n/// <https://drafts.csswg.org/css-images/#gradients>\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\n#[repr(C)]\npub enum GenericGradient<\n    LineDirection,\n    Length,\n    LengthPercentage,\n    Position,\n    Angle,\n    AngleOrPercentage,\n    Color,\n> {\n    /// A linear gradient.\n    Linear {\n        /// Line direction\n        direction: LineDirection,\n        /// Method to use for color interpolation.\n        color_interpolation_method: ColorInterpolationMethod,\n        /// The color stops and interpolation hints.\n        items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,\n        /// State flags for the gradient.\n        flags: GradientFlags,\n        /// Compatibility mode.\n        compat_mode: GradientCompatMode,\n    },\n    /// A radial gradient.\n    Radial {\n        /// Shape of gradient\n        shape: GenericEndingShape<NonNegative<Length>, NonNegative<LengthPercentage>>,\n        /// Center of gradient\n        position: Position,\n        /// Method to use for color interpolation.\n        color_interpolation_method: ColorInterpolationMethod,\n        /// The color stops and interpolation hints.\n        items: crate::OwnedSlice<GenericGradientItem<Color, LengthPercentage>>,\n        /// State flags for the gradient.\n        flags: GradientFlags,\n        /// Compatibility mode.\n        compat_mode: GradientCompatMode,\n    },\n    /// A conic gradient.\n    Conic {\n        /// Start angle of gradient\n        angle: Angle,\n        /// Center of gradient\n        position: Position,\n        /// Method to use for color interpolation.\n        color_interpolation_method: ColorInterpolationMethod,\n        /// The color stops and interpolation hints.\n        items: crate::OwnedSlice<GenericGradientItem<Color, AngleOrPercentage>>,\n        /// State flags for the gradient.\n        flags: GradientFlags,\n    },\n}\n\npub use self::GenericGradient as Gradient;\n\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[repr(u8)]\n/// Whether we used the modern notation or the compatibility `-webkit`, `-moz` prefixes.\npub enum GradientCompatMode {\n    /// Modern syntax.\n    Modern,\n    /// `-webkit` prefix.\n    WebKit,\n    /// `-moz` prefix\n    Moz,\n}\n\n/// A radial gradient's ending shape.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericEndingShape<NonNegativeLength, NonNegativeLengthPercentage> {\n    /// A circular gradient.\n    Circle(GenericCircle<NonNegativeLength>),\n    /// An elliptic gradient.\n    Ellipse(GenericEllipse<NonNegativeLengthPercentage>),\n}\n\npub use self::GenericEndingShape as EndingShape;\n\n/// A circle shape.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericCircle<NonNegativeLength> {\n    /// A circle radius.\n    Radius(NonNegativeLength),\n    /// A circle extent.\n    Extent(ShapeExtent),\n}\n\npub use self::GenericCircle as Circle;\n\n/// An ellipse shape.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericEllipse<NonNegativeLengthPercentage> {\n    /// An ellipse pair of radii.\n    Radii(NonNegativeLengthPercentage, NonNegativeLengthPercentage),\n    /// An ellipse extent.\n    Extent(ShapeExtent),\n}\n\npub use self::GenericEllipse as Ellipse;\n\n/// <https://drafts.csswg.org/css-images/#typedef-extent-keyword>\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ShapeExtent {\n    ClosestSide,\n    FarthestSide,\n    ClosestCorner,\n    FarthestCorner,\n    Contain,\n    Cover,\n}\n\n/// A gradient item.\n/// <https://drafts.csswg.org/css-images-4/#color-stop-syntax>\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericGradientItem<Color, T> {\n    /// A simple color stop, without position.\n    SimpleColorStop(Color),\n    /// A complex color stop, with a position.\n    ComplexColorStop {\n        /// The color for the stop.\n        color: Color,\n        /// The position for the stop.\n        position: T,\n    },\n    /// An interpolation hint.\n    InterpolationHint(T),\n}\n\npub use self::GenericGradientItem as GradientItem;\n\n/// A color stop.\n/// <https://drafts.csswg.org/css-images/#typedef-color-stop-list>\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem,\n)]\npub struct ColorStop<Color, T> {\n    /// The color of this stop.\n    pub color: Color,\n    /// The position of this stop.\n    pub position: Option<T>,\n}\n\nimpl<Color, T> ColorStop<Color, T> {\n    /// Convert the color stop into an appropriate `GradientItem`.\n    #[inline]\n    pub fn into_item(self) -> GradientItem<Color, T> {\n        match self.position {\n            Some(position) => GradientItem::ComplexColorStop {\n                color: self.color,\n                position,\n            },\n            None => GradientItem::SimpleColorStop(self.color),\n        }\n    }\n}\n\n/// Specified values for a paint worklet.\n/// <https://drafts.css-houdini.org/css-paint-api/>\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\n#[derive(Clone, Debug, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\npub struct PaintWorklet {\n    /// The name the worklet was registered with.\n    pub name: Atom,\n    /// The arguments for the worklet.\n    /// TODO: store a parsed representation of the arguments.\n    #[cfg_attr(feature = \"servo\", ignore_malloc_size_of = \"Arc\")]\n    #[compute(no_field_bound)]\n    #[resolve(no_field_bound)]\n    pub arguments: Vec<Arc<custom_properties::SpecifiedValue>>,\n}\n\nimpl ::style_traits::SpecifiedValueInfo for PaintWorklet {}\n\nimpl ToCss for PaintWorklet {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"paint(\")?;\n        serialize_atom_identifier(&self.name, dest)?;\n        for argument in &self.arguments {\n            dest.write_str(\", \")?;\n            argument.to_css(dest)?;\n        }\n        dest.write_char(')')\n    }\n}\n\nimpl<G, U, C, P, Resolution> fmt::Debug for Image<G, U, C, P, Resolution>\nwhere\n    Image<G, U, C, P, Resolution>: ToCss,\n{\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        self.to_css(&mut CssWriter::new(f))\n    }\n}\n\nimpl<G, U, C, P, Resolution> ToCss for Image<G, U, C, P, Resolution>\nwhere\n    G: ToCss,\n    U: ToCss,\n    C: ToCss,\n    P: ToCss,\n    Resolution: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Image::None => dest.write_str(\"none\"),\n            Image::Url(ref url) => url.to_css(dest),\n            Image::Gradient(ref gradient) => gradient.to_css(dest),\n            #[cfg(feature = \"servo\")]\n            Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),\n            #[cfg(feature = \"gecko\")]\n            Image::Element(ref selector) => {\n                dest.write_str(\"-moz-element(#\")?;\n                serialize_atom_identifier(selector, dest)?;\n                dest.write_char(')')\n            },\n            #[cfg(feature = \"gecko\")]\n            Image::MozSymbolicIcon(ref id) => {\n                dest.write_str(\"-moz-symbolic-icon(\")?;\n                serialize_atom_identifier(id, dest)?;\n                dest.write_char(')')\n            },\n            Image::ImageSet(ref is) => is.to_css(dest),\n            Image::CrossFade(ref cf) => cf.to_css(dest),\n            Image::LightDark(ref ld) => ld.to_css(dest),\n        }\n    }\n}\n\nimpl<D, L, LP, P, A: Zero, AoP, C> ToCss for Gradient<D, L, LP, P, A, AoP, C>\nwhere\n    D: LineDirection,\n    L: ToCss,\n    LP: ToCss,\n    P: PositionComponent + ToCss,\n    A: ToCss,\n    AoP: ToCss,\n    C: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let (compat_mode, repeating, has_default_color_interpolation_method) = match *self {\n            Gradient::Linear {\n                compat_mode, flags, ..\n            }\n            | Gradient::Radial {\n                compat_mode, flags, ..\n            } => (\n                compat_mode,\n                flags.contains(GradientFlags::REPEATING),\n                flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),\n            ),\n            Gradient::Conic { flags, .. } => (\n                GradientCompatMode::Modern,\n                flags.contains(GradientFlags::REPEATING),\n                flags.contains(GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD),\n            ),\n        };\n\n        match compat_mode {\n            GradientCompatMode::WebKit => dest.write_str(\"-webkit-\")?,\n            GradientCompatMode::Moz => dest.write_str(\"-moz-\")?,\n            _ => {},\n        }\n\n        if repeating {\n            dest.write_str(\"repeating-\")?;\n        }\n\n        match *self {\n            Gradient::Linear {\n                ref direction,\n                ref color_interpolation_method,\n                ref items,\n                compat_mode,\n                ..\n            } => {\n                dest.write_str(\"linear-gradient(\")?;\n                let mut skip_comma = true;\n                if !direction.points_downwards(compat_mode) {\n                    direction.to_css(dest, compat_mode)?;\n                    skip_comma = false;\n                }\n                if !has_default_color_interpolation_method {\n                    if !skip_comma {\n                        dest.write_char(' ')?;\n                    }\n                    color_interpolation_method.to_css(dest)?;\n                    skip_comma = false;\n                }\n                for item in &**items {\n                    if !skip_comma {\n                        dest.write_str(\", \")?;\n                    }\n                    skip_comma = false;\n                    item.to_css(dest)?;\n                }\n            },\n            Gradient::Radial {\n                ref shape,\n                ref position,\n                ref color_interpolation_method,\n                ref items,\n                compat_mode,\n                ..\n            } => {\n                dest.write_str(\"radial-gradient(\")?;\n                let omit_shape = match *shape {\n                    EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::Cover))\n                    | EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner)) => true,\n                    _ => false,\n                };\n                let omit_position = position.is_center();\n                if compat_mode == GradientCompatMode::Modern {\n                    if !omit_shape {\n                        shape.to_css(dest)?;\n                        if !omit_position {\n                            dest.write_char(' ')?;\n                        }\n                    }\n                    if !omit_position {\n                        dest.write_str(\"at \")?;\n                        position.to_css(dest)?;\n                    }\n                } else {\n                    if !omit_position {\n                        position.to_css(dest)?;\n                        if !omit_shape {\n                            dest.write_str(\", \")?;\n                        }\n                    }\n                    if !omit_shape {\n                        shape.to_css(dest)?;\n                    }\n                }\n                if !has_default_color_interpolation_method {\n                    if !omit_shape || !omit_position {\n                        dest.write_char(' ')?;\n                    }\n                    color_interpolation_method.to_css(dest)?;\n                }\n\n                let mut skip_comma =\n                    omit_shape && omit_position && has_default_color_interpolation_method;\n                for item in &**items {\n                    if !skip_comma {\n                        dest.write_str(\", \")?;\n                    }\n                    skip_comma = false;\n                    item.to_css(dest)?;\n                }\n            },\n            Gradient::Conic {\n                ref angle,\n                ref position,\n                ref color_interpolation_method,\n                ref items,\n                ..\n            } => {\n                dest.write_str(\"conic-gradient(\")?;\n                let omit_angle = angle.is_zero();\n                let omit_position = position.is_center();\n                if !omit_angle {\n                    dest.write_str(\"from \")?;\n                    angle.to_css(dest)?;\n                    if !omit_position {\n                        dest.write_char(' ')?;\n                    }\n                }\n                if !omit_position {\n                    dest.write_str(\"at \")?;\n                    position.to_css(dest)?;\n                }\n                if !has_default_color_interpolation_method {\n                    if !omit_angle || !omit_position {\n                        dest.write_char(' ')?;\n                    }\n                    color_interpolation_method.to_css(dest)?;\n                }\n                let mut skip_comma =\n                    omit_angle && omit_position && has_default_color_interpolation_method;\n                for item in &**items {\n                    if !skip_comma {\n                        dest.write_str(\", \")?;\n                    }\n                    skip_comma = false;\n                    item.to_css(dest)?;\n                }\n            },\n        }\n        dest.write_char(')')\n    }\n}\n\n/// The direction of a linear gradient.\npub trait LineDirection {\n    /// Whether this direction points towards, and thus can be omitted.\n    fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool;\n\n    /// Serialises this direction according to the compatibility mode.\n    fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result\n    where\n        W: Write;\n}\n\nimpl<L> ToCss for Circle<L>\nwhere\n    L: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Circle::Extent(ShapeExtent::FarthestCorner) | Circle::Extent(ShapeExtent::Cover) => {\n                dest.write_str(\"circle\")\n            },\n            Circle::Extent(keyword) => {\n                dest.write_str(\"circle \")?;\n                keyword.to_css(dest)\n            },\n            Circle::Radius(ref length) => length.to_css(dest),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/generics/length.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to length.\n\nuse crate::derives::*;\nuse crate::logical_geometry::PhysicalSide;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::position::TryTacticAdjustment;\nuse crate::values::generics::box_::PositionProperty;\nuse crate::values::generics::position::TreeScoped;\nuse crate::values::generics::Optional;\nuse crate::values::DashedIdent;\nuse crate::Zero;\nuse cssparser::Parser;\nuse std::fmt::Write;\nuse style_derive::Animate;\nuse style_traits::ParseError;\nuse style_traits::StyleParseErrorKind;\nuse style_traits::ToCss;\nuse style_traits::{CssWriter, SpecifiedValueInfo};\n\n/// A `<length-percentage> | auto` value.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C, u8)]\npub enum GenericLengthPercentageOrAuto<LengthPercent> {\n    LengthPercentage(LengthPercent),\n    Auto,\n}\n\npub use self::GenericLengthPercentageOrAuto as LengthPercentageOrAuto;\n\nimpl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        LengthPercentageOrAuto::Auto\n    }\n\n    /// Whether this is the `auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, LengthPercentageOrAuto::Auto)\n    }\n\n    /// A helper function to parse this with quirks or not and so forth.\n    pub fn parse_with<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parser: impl FnOnce(\n            &ParserContext,\n            &mut Parser<'i, 't>,\n        ) -> Result<LengthPercentage, ParseError<'i>>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok() {\n            return Ok(LengthPercentageOrAuto::Auto);\n        }\n\n        Ok(LengthPercentageOrAuto::LengthPercentage(parser(\n            context, input,\n        )?))\n    }\n}\n\nimpl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage>\nwhere\n    LengthPercentage: Clone,\n{\n    /// Resolves `auto` values by calling `f`.\n    #[inline]\n    pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage {\n        match self {\n            LengthPercentageOrAuto::LengthPercentage(length) => length.clone(),\n            LengthPercentageOrAuto::Auto => f(),\n        }\n    }\n\n    /// Returns the non-`auto` value, if any.\n    #[inline]\n    pub fn non_auto(&self) -> Option<LengthPercentage> {\n        match self {\n            LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()),\n            LengthPercentageOrAuto::Auto => None,\n        }\n    }\n\n    /// Maps the length of this value.\n    pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> {\n        match self {\n            LengthPercentageOrAuto::LengthPercentage(l) => {\n                LengthPercentageOrAuto::LengthPercentage(f(l.clone()))\n            },\n            LengthPercentageOrAuto::Auto => LengthPercentageOrAuto::Auto,\n        }\n    }\n}\n\nimpl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {\n    fn zero() -> Self {\n        LengthPercentageOrAuto::LengthPercentage(Zero::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        match *self {\n            LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),\n            LengthPercentageOrAuto::Auto => false,\n        }\n    }\n}\n\nimpl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with(context, input, LengthPercentage::parse)\n    }\n}\n\n/// A generic value for the `width`, `height`, `min-width`, or `min-height` property.\n///\n/// Unlike `max-width` or `max-height` properties, a Size can be `auto`,\n/// and cannot be `none`.\n///\n/// Note that it only accepts non-negative values.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    ComputeSquaredDistance,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericSize<LengthPercent> {\n    LengthPercentage(LengthPercent),\n    Auto,\n    #[animation(error)]\n    MaxContent,\n    #[animation(error)]\n    MinContent,\n    #[animation(error)]\n    FitContent,\n    #[cfg(feature = \"gecko\")]\n    #[animation(error)]\n    MozAvailable,\n    #[animation(error)]\n    WebkitFillAvailable,\n    #[animation(error)]\n    Stretch,\n    #[animation(error)]\n    #[css(function = \"fit-content\")]\n    FitContentFunction(LengthPercent),\n    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),\n    AnchorContainingCalcFunction(LengthPercent),\n}\n\nimpl<LengthPercent> SpecifiedValueInfo for GenericSize<LengthPercent>\nwhere\n    LengthPercent: SpecifiedValueInfo,\n{\n    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {\n        LengthPercent::collect_completion_keywords(f);\n        f(&[\"auto\", \"fit-content\", \"max-content\", \"min-content\"]);\n        if cfg!(feature = \"gecko\") {\n            f(&[\"-moz-available\"]);\n        }\n        if static_prefs::pref!(\"layout.css.stretch-size-keyword.enabled\") {\n            f(&[\"stretch\"]);\n        }\n        if static_prefs::pref!(\"layout.css.webkit-fill-available.enabled\") {\n            f(&[\"-webkit-fill-available\"]);\n        }\n        if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            f(&[\"anchor-size\"]);\n        }\n    }\n}\n\npub use self::GenericSize as Size;\n\nimpl<LengthPercentage> Size<LengthPercentage> {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        Size::Auto\n    }\n\n    /// Returns whether we're the auto value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, Size::Auto)\n    }\n}\n\n/// A generic value for the `max-width` or `max-height` property.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericMaxSize<LengthPercent> {\n    LengthPercentage(LengthPercent),\n    None,\n    #[animation(error)]\n    MaxContent,\n    #[animation(error)]\n    MinContent,\n    #[animation(error)]\n    FitContent,\n    #[cfg(feature = \"gecko\")]\n    #[animation(error)]\n    MozAvailable,\n    #[animation(error)]\n    WebkitFillAvailable,\n    #[animation(error)]\n    Stretch,\n    #[animation(error)]\n    #[css(function = \"fit-content\")]\n    FitContentFunction(LengthPercent),\n    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),\n    AnchorContainingCalcFunction(LengthPercent),\n}\n\nimpl<LP> SpecifiedValueInfo for GenericMaxSize<LP>\nwhere\n    LP: SpecifiedValueInfo,\n{\n    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {\n        LP::collect_completion_keywords(f);\n        f(&[\"none\", \"fit-content\", \"max-content\", \"min-content\"]);\n        if cfg!(feature = \"gecko\") {\n            f(&[\"-moz-available\"]);\n        }\n        if static_prefs::pref!(\"layout.css.stretch-size-keyword.enabled\") {\n            f(&[\"stretch\"]);\n        }\n        if static_prefs::pref!(\"layout.css.webkit-fill-available.enabled\") {\n            f(&[\"-webkit-fill-available\"]);\n        }\n        if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            f(&[\"anchor-size\"]);\n        }\n    }\n}\n\npub use self::GenericMaxSize as MaxSize;\n\nimpl<LengthPercentage> MaxSize<LengthPercentage> {\n    /// `none` value.\n    #[inline]\n    pub fn none() -> Self {\n        MaxSize::None\n    }\n}\n\n/// A generic `<length>` | `<number>` value for the `tab-size` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericLengthOrNumber<L, N> {\n    /// A number.\n    ///\n    /// NOTE: Numbers need to be before lengths, in order to parse them\n    /// first, since `0` should be a number, not the `0px` length.\n    Number(N),\n    /// A length.\n    Length(L),\n}\n\npub use self::GenericLengthOrNumber as LengthOrNumber;\n\nimpl<L, N: Zero> Zero for LengthOrNumber<L, N> {\n    fn zero() -> Self {\n        LengthOrNumber::Number(Zero::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        match *self {\n            LengthOrNumber::Number(ref n) => n.is_zero(),\n            LengthOrNumber::Length(..) => false,\n        }\n    }\n}\n\n/// A generic `<length-percentage>` | normal` value.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[allow(missing_docs)]\npub enum GenericLengthPercentageOrNormal<LengthPercent> {\n    LengthPercentage(LengthPercent),\n    Normal,\n}\n\npub use self::GenericLengthPercentageOrNormal as LengthPercentageOrNormal;\n\nimpl<LengthPercent> LengthPercentageOrNormal<LengthPercent> {\n    /// Returns the normal value.\n    #[inline]\n    pub fn normal() -> Self {\n        LengthPercentageOrNormal::Normal\n    }\n}\n\n/// Anchor size function used by sizing, margin and inset properties.\n/// This resolves to the size of the anchor at computed time.\n///\n/// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    Serialize,\n    Deserialize,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericAnchorSizeFunction<Fallback> {\n    /// Anchor name of the element to anchor to.\n    /// If omitted (i.e. empty), selects the implicit anchor element.\n    #[animation(constant)]\n    pub target_element: TreeScoped<DashedIdent>,\n    /// Size of the positioned element, expressed in that of the anchor element.\n    /// If omitted, defaults to the axis of the property the function is used in.\n    pub size: AnchorSizeKeyword,\n    /// Value to use in case the anchor function is invalid.\n    pub fallback: Optional<Fallback>,\n}\n\nimpl<Fallback: TryTacticAdjustment> TryTacticAdjustment for GenericAnchorSizeFunction<Fallback> {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        self.size.try_tactic_adjustment(old_side, new_side);\n        if let Some(fallback) = self.fallback.as_mut() {\n            fallback.try_tactic_adjustment(old_side, new_side);\n        }\n    }\n}\n\nimpl<Fallback> ToCss for GenericAnchorSizeFunction<Fallback>\nwhere\n    Fallback: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"anchor-size(\")?;\n        let mut previous_entry_printed = false;\n        if !self.target_element.value.0.is_empty() {\n            previous_entry_printed = true;\n            self.target_element.to_css(dest)?;\n        }\n        if self.size != AnchorSizeKeyword::None {\n            if previous_entry_printed {\n                dest.write_str(\" \")?;\n            }\n            previous_entry_printed = true;\n            self.size.to_css(dest)?;\n        }\n        if let Some(f) = self.fallback.as_ref() {\n            if previous_entry_printed {\n                dest.write_str(\", \")?;\n            }\n            f.to_css(dest)?;\n        }\n        dest.write_str(\")\")\n    }\n}\n\nimpl<Fallback> Parse for GenericAnchorSizeFunction<Fallback>\nwhere\n    Fallback: Parse,\n{\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        input.expect_function_matching(\"anchor-size\")?;\n        Self::parse_inner(context, input, |i| Fallback::parse(context, i))\n    }\n}\nimpl<Fallback> GenericAnchorSizeFunction<Fallback> {\n    /// Is the anchor-size use valid for given property?\n    pub fn valid_for(&self, position_property: PositionProperty) -> bool {\n        position_property.is_absolutely_positioned()\n    }\n}\n\n/// Result of resolving an anchor function.\npub enum AnchorResolutionResult<'a, LengthPercentage> {\n    /// Function resolved to a valid anchor.\n    Resolved(LengthPercentage),\n    /// Referenced anchor is invalid, but fallback is used.\n    Fallback(&'a LengthPercentage),\n    /// Referenced anchor is invalid.\n    Invalid,\n}\n\nimpl<'a, LengthPercentage> AnchorResolutionResult<'a, LengthPercentage> {\n    /// Return result for an invalid anchor function, depending on if it has any fallback.\n    pub fn new_anchor_invalid(fallback: Option<&'a LengthPercentage>) -> Self {\n        if let Some(fb) = fallback {\n            return Self::Fallback(fb);\n        }\n        Self::Invalid\n    }\n}\n\nimpl<LengthPercentage> GenericAnchorSizeFunction<LengthPercentage> {\n    /// Parse the inner part of `anchor-size()`, after the parser has consumed \"anchor-size(\".\n    pub fn parse_inner<'i, 't, F>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        f: F,\n    ) -> Result<Self, ParseError<'i>>\n    where\n        F: FnOnce(&mut Parser<'i, '_>) -> Result<LengthPercentage, ParseError<'i>>,\n    {\n        input.parse_nested_block(|i| {\n            let mut target_element = i\n                .try_parse(|i| DashedIdent::parse(context, i))\n                .unwrap_or(DashedIdent::empty());\n            let size = i\n                .try_parse(AnchorSizeKeyword::parse)\n                .unwrap_or(AnchorSizeKeyword::None);\n            if target_element.is_empty() {\n                target_element = i\n                    .try_parse(|i| DashedIdent::parse(context, i))\n                    .unwrap_or(DashedIdent::empty());\n            }\n            let previous_parsed = !target_element.is_empty() || size != AnchorSizeKeyword::None;\n            let fallback = i\n                .try_parse(|i| {\n                    if previous_parsed {\n                        i.expect_comma()?;\n                    }\n                    f(i)\n                })\n                .ok();\n            Ok(GenericAnchorSizeFunction {\n                target_element: TreeScoped::with_default_level(target_element),\n                size: size.into(),\n                fallback: fallback.into(),\n            })\n        })\n    }\n}\n\n/// Keyword values for the anchor size function.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    Serialize,\n    Deserialize,\n)]\n#[repr(u8)]\npub enum AnchorSizeKeyword {\n    /// Magic value for nothing.\n    #[css(skip)]\n    None,\n    /// Width of the anchor element.\n    Width,\n    /// Height of the anchor element.\n    Height,\n    /// Block size of the anchor element.\n    Block,\n    /// Inline size of the anchor element.\n    Inline,\n    /// Same as `Block`, resolved against the positioned element's writing mode.\n    SelfBlock,\n    /// Same as `Inline`, resolved against the positioned element's writing mode.\n    SelfInline,\n}\n\nimpl TryTacticAdjustment for AnchorSizeKeyword {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        if old_side.parallel_to(new_side) {\n            return;\n        }\n        *self = match *self {\n            Self::None => Self::None,\n            Self::Width => Self::Height,\n            Self::Height => Self::Width,\n            Self::Block => Self::Inline,\n            Self::Inline => Self::Block,\n            Self::SelfBlock => Self::SelfInline,\n            Self::SelfInline => Self::SelfBlock,\n        }\n    }\n}\n\n/// Specified type for `margin` properties, which allows\n/// the use of the `anchor-size()` function.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToCss,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(C)]\npub enum GenericMargin<LP> {\n    /// A `<length-percentage>` value.\n    LengthPercentage(LP),\n    /// An `auto` value.\n    Auto,\n    /// Margin size defined by the anchor element.\n    ///\n    /// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor-size\n    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),\n    /// A `<length-percentage>` value, guaranteed to contain `calc()`,\n    /// which then is guaranteed to contain `anchor()` or `anchor-size()`.\n    AnchorContainingCalcFunction(LP),\n}\n\n#[cfg(feature = \"servo\")]\nimpl<LP> GenericMargin<LP> {\n    /// Return true if it is 'auto'.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(self, Self::Auto)\n    }\n}\n\nimpl<LP> SpecifiedValueInfo for GenericMargin<LP>\nwhere\n    LP: SpecifiedValueInfo,\n{\n    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {\n        LP::collect_completion_keywords(f);\n        f(&[\"auto\"]);\n        if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            f(&[\"anchor-size\"]);\n        }\n    }\n}\n\nimpl<LP> Zero for GenericMargin<LP>\nwhere\n    LP: Zero,\n{\n    fn is_zero(&self) -> bool {\n        match self {\n            Self::LengthPercentage(l) => l.is_zero(),\n            Self::Auto | Self::AnchorSizeFunction(_) | Self::AnchorContainingCalcFunction(_) => {\n                false\n            },\n        }\n    }\n\n    fn zero() -> Self {\n        Self::LengthPercentage(LP::zero())\n    }\n}\n"
  },
  {
    "path": "style/values/generics/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types that share their serialization implementations\n//! for both specified and computed values.\n\nuse crate::derives::*;\nuse crate::Zero;\nuse std::ops::Add;\n\npub mod animation;\npub mod background;\npub mod basic_shape;\npub mod border;\n#[path = \"box.rs\"]\npub mod box_;\npub mod calc;\npub mod color;\npub mod column;\npub mod counters;\npub mod easing;\npub mod effects;\npub mod flex;\npub mod font;\npub mod grid;\npub mod image;\npub mod length;\npub mod motion;\npub mod page;\npub mod position;\npub mod ratio;\npub mod rect;\npub mod size;\npub mod svg;\npub mod text;\npub mod transform;\npub mod ui;\npub mod url;\n\n/// A wrapper of Non-negative values.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    Serialize,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\npub struct NonNegative<T>(pub T);\n\n/// A trait to clamp a negative value to another.\npub trait ClampToNonNegative {\n    /// Clamps the value to be non-negative after an animation.\n    fn clamp_to_non_negative(self) -> Self;\n}\n\nimpl ClampToNonNegative for f32 {\n    fn clamp_to_non_negative(self) -> Self {\n        self.max(0.)\n    }\n}\n\nimpl<T: Add<Output = T>> Add<NonNegative<T>> for NonNegative<T> {\n    type Output = Self;\n\n    fn add(self, other: Self) -> Self {\n        NonNegative(self.0 + other.0)\n    }\n}\n\nimpl<T: Zero> Zero for NonNegative<T> {\n    fn is_zero(&self) -> bool {\n        self.0.is_zero()\n    }\n\n    fn zero() -> Self {\n        NonNegative(T::zero())\n    }\n}\n\n/// A wrapper of greater-than-or-equal-to-one values.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct GreaterThanOrEqualToOne<T>(pub T);\n\n/// A wrapper of values between zero and one.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct ZeroToOne<T>(pub T);\n\n/// A clip rect for clip and image-region\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function = \"rect\", comma)]\n#[repr(C)]\npub struct GenericClipRect<LengthOrAuto> {\n    pub top: LengthOrAuto,\n    pub right: LengthOrAuto,\n    pub bottom: LengthOrAuto,\n    pub left: LengthOrAuto,\n}\n\npub use self::GenericClipRect as ClipRect;\n\n/// Either a clip-rect or `auto`.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericClipRectOrAuto<R> {\n    Auto,\n    Rect(R),\n}\n\npub use self::GenericClipRectOrAuto as ClipRectOrAuto;\n\nimpl<L> ClipRectOrAuto<L> {\n    /// Returns the `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        ClipRectOrAuto::Auto\n    }\n\n    /// Returns whether this value is the `auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, ClipRectOrAuto::Auto)\n    }\n}\n\npub use page::PageSize;\n\npub use text::NumberOrAuto;\n\n/// An optional value, much like `Option<T>`, but with a defined struct layout\n/// to be able to use it from C++ as well.\n///\n/// Note that this is relatively inefficient, struct-layout-wise, as you have\n/// one byte for the tag, but padding to the alignment of T. If you have\n/// multiple optional values and care about struct compactness, you might be\n/// better off \"coalescing\" the combinations into a parent enum. But that\n/// shouldn't matter for most use cases.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    Serialize,\n    Deserialize,\n)]\n#[repr(C, u8)]\npub enum Optional<T> {\n    #[css(skip)]\n    None,\n    Some(T),\n}\n\nimpl<T> Optional<T> {\n    /// Returns whether this value is present.\n    pub fn is_some(&self) -> bool {\n        matches!(*self, Self::Some(..))\n    }\n\n    /// Returns whether this value is not present.\n    pub fn is_none(&self) -> bool {\n        matches!(*self, Self::None)\n    }\n\n    /// Turns this Optional<> into a regular rust Option<>.\n    pub fn into_rust(self) -> Option<T> {\n        match self {\n            Self::Some(v) => Some(v),\n            Self::None => None,\n        }\n    }\n\n    /// Return a reference to the containing value, if any, as a plain rust\n    /// Option<>.\n    pub fn as_ref(&self) -> Option<&T> {\n        match *self {\n            Self::Some(ref v) => Some(v),\n            Self::None => None,\n        }\n    }\n\n    /// Return a mutable reference to the containing value, if any, as a plain\n    /// rust Option<>.\n    pub fn as_mut(&mut self) -> Option<&mut T> {\n        match *self {\n            Self::Some(ref mut v) => Some(v),\n            Self::None => None,\n        }\n    }\n\n    /// See Option::map.\n    pub fn map<U>(self, map: impl FnOnce(T) -> U) -> Optional<U> {\n        match self {\n            Self::Some(v) => Optional::Some(map(v)),\n            Self::None => Optional::None,\n        }\n    }\n}\n\nimpl<T> From<Option<T>> for Optional<T> {\n    fn from(rust: Option<T>) -> Self {\n        match rust {\n            Some(t) => Self::Some(t),\n            None => Self::None,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/generics/motion.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS Motion Path.\n\nuse crate::derives::*;\nuse crate::values::animated::ToAnimatedZero;\nuse crate::values::generics::position::{GenericPosition, GenericPositionOrAuto};\nuse crate::values::specified::motion::CoordBox;\nuse serde::Deserializer;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// The <size> in ray() function.\n///\n/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-size\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum RaySize {\n    ClosestSide,\n    ClosestCorner,\n    FarthestSide,\n    FarthestCorner,\n    Sides,\n}\n\n/// The `ray()` function, `ray( [ <angle> && <size> && contain? && [at <position>]? ] )`\n///\n/// https://drafts.fxtf.org/motion-1/#valdef-offsetpath-ray\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct GenericRayFunction<Angle, Position> {\n    /// The bearing angle with `0deg` pointing up and positive angles\n    /// representing clockwise rotation.\n    pub angle: Angle,\n    /// Decide the path length used when `offset-distance` is expressed\n    /// as a percentage.\n    pub size: RaySize,\n    /// Clamp `offset-distance` so that the box is entirely contained\n    /// within the path.\n    #[animation(constant)]\n    pub contain: bool,\n    /// The \"at <position>\" part. If omitted, we use auto to represent it.\n    pub position: GenericPositionOrAuto<Position>,\n}\n\npub use self::GenericRayFunction as RayFunction;\n\nimpl<Angle, Position> ToCss for RayFunction<Angle, Position>\nwhere\n    Angle: ToCss,\n    Position: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.angle.to_css(dest)?;\n\n        if !matches!(self.size, RaySize::ClosestSide) {\n            dest.write_char(' ')?;\n            self.size.to_css(dest)?;\n        }\n\n        if self.contain {\n            dest.write_str(\" contain\")?;\n        }\n\n        if !matches!(self.position, GenericPositionOrAuto::Auto) {\n            dest.write_str(\" at \")?;\n            self.position.to_css(dest)?;\n        }\n\n        Ok(())\n    }\n}\n\n/// Return error if we try to deserialize the url, for Gecko IPC purposes.\n// Note: we cannot use #[serde(skip_deserializing)] variant attribute, which may cause the fatal\n// error when trying to read the parameters because it cannot deserialize the input byte buffer,\n// even if the type of OffsetPathFunction is not an url(), in our tests. This may be an issue of\n// #[serde(skip_deserializing)] on enum, at least in the version (1.0) we are using. So we have to\n// manually implement this deseriailzing function, but return error.\n// FIXME: Bug 1847620, fiure out this is a serde issue or a gecko bug.\nfn deserialize_url<'de, D, T>(_deserializer: D) -> Result<T, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    use crate::serde::de::Error;\n    // Return Err() so the IPC will catch it and assert this as a fetal error.\n    Err(<D as Deserializer>::Error::custom(\n        \"we don't support the deserializing for url\",\n    ))\n}\n\n/// The <offset-path> value.\n/// <offset-path> = <ray()> | <url> | <basic-shape>\n///\n/// https://drafts.fxtf.org/motion-1/#typedef-offset-path\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[animation(no_bound(U))]\n#[repr(C, u8)]\npub enum GenericOffsetPathFunction<Shapes, RayFunction, U> {\n    /// ray() function, which defines a path in the polar coordinate system.\n    /// Use Box<> to make sure the size of offset-path is not too large.\n    #[css(function)]\n    Ray(RayFunction),\n    /// A URL reference to an SVG shape element. If the URL does not reference a shape element,\n    /// this behaves as path(\"m 0 0\") instead.\n    #[animation(error)]\n    #[serde(deserialize_with = \"deserialize_url\")]\n    #[serde(skip_serializing)]\n    Url(U),\n    /// The <basic-shape> value.\n    Shape(Shapes),\n}\n\npub use self::GenericOffsetPathFunction as OffsetPathFunction;\n\n/// The offset-path property.\n/// offset-path: none | <offset-path> || <coord-box>\n///\n/// https://drafts.fxtf.org/motion-1/#offset-path-property\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericOffsetPath<Function> {\n    /// <offset-path> || <coord-box>.\n    OffsetPath {\n        /// <offset-path> part.\n        // Note: Use Box<> to make sure the size of this property doesn't go over the threshold.\n        path: Box<Function>,\n        /// <coord-box> part.\n        #[css(skip_if = \"CoordBox::is_default\")]\n        coord_box: CoordBox,\n    },\n    /// Only <coord-box>. This represents that <offset-path> is omitted, so we use the default\n    /// value, inset(0 round X), where X is the value of border-radius on the element that\n    /// establishes the containing block for this element.\n    CoordBox(CoordBox),\n    /// None value.\n    #[animation(error)]\n    None,\n}\n\npub use self::GenericOffsetPath as OffsetPath;\n\nimpl<Function> OffsetPath<Function> {\n    /// Return None.\n    #[inline]\n    pub fn none() -> Self {\n        OffsetPath::None\n    }\n}\n\nimpl<Function> ToAnimatedZero for OffsetPath<Function> {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\n/// The offset-position property, which specifies the offset starting position that is used by the\n/// <offset-path> functions if they don’t specify their own starting position.\n///\n/// https://drafts.fxtf.org/motion-1/#offset-position-property\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericOffsetPosition<H, V> {\n    /// The element does not have an offset starting position.\n    Normal,\n    /// The offset starting position is the top-left corner of the box.\n    Auto,\n    /// The offset starting position is the result of using the <position> to position a 0x0 object\n    /// area within the box’s containing block.\n    Position(\n        #[css(field_bound)]\n        #[parse(field_bound)]\n        GenericPosition<H, V>,\n    ),\n}\n\npub use self::GenericOffsetPosition as OffsetPosition;\n\nimpl<H, V> OffsetPosition<H, V> {\n    /// Returns the initial value, normal.\n    #[inline]\n    pub fn normal() -> Self {\n        Self::Normal\n    }\n}\n"
  },
  {
    "path": "style/values/generics/page.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! @page at-rule properties\n\nuse crate::derives::*;\nuse crate::values::generics::NonNegative;\nuse crate::values::specified::length::AbsoluteLength;\n\n/// Page size names.\n///\n/// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size\n#[derive(\n    Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,\n)]\n#[repr(u8)]\npub enum PaperSize {\n    /// ISO A5 media\n    A5,\n    /// ISO A4 media\n    A4,\n    /// ISO A3 media\n    A3,\n    /// ISO B5 media\n    B5,\n    /// ISO B4 media\n    B4,\n    /// JIS B5 media\n    JisB5,\n    /// JIS B4 media\n    JisB4,\n    /// North American Letter size\n    Letter,\n    /// North American Legal size\n    Legal,\n    /// North American Ledger size\n    Ledger,\n}\n\nimpl PaperSize {\n    /// Gets the long edge length of the paper size\n    pub fn long_edge(&self) -> NonNegative<AbsoluteLength> {\n        NonNegative(match *self {\n            PaperSize::A5 => AbsoluteLength::Mm(210.0),\n            PaperSize::A4 => AbsoluteLength::Mm(297.0),\n            PaperSize::A3 => AbsoluteLength::Mm(420.0),\n            PaperSize::B5 => AbsoluteLength::Mm(250.0),\n            PaperSize::B4 => AbsoluteLength::Mm(353.0),\n            PaperSize::JisB5 => AbsoluteLength::Mm(257.0),\n            PaperSize::JisB4 => AbsoluteLength::Mm(364.0),\n            PaperSize::Letter => AbsoluteLength::In(11.0),\n            PaperSize::Legal => AbsoluteLength::In(14.0),\n            PaperSize::Ledger => AbsoluteLength::In(17.0),\n        })\n    }\n    /// Gets the short edge length of the paper size\n    pub fn short_edge(&self) -> NonNegative<AbsoluteLength> {\n        NonNegative(match *self {\n            PaperSize::A5 => AbsoluteLength::Mm(148.0),\n            PaperSize::A4 => AbsoluteLength::Mm(210.0),\n            PaperSize::A3 => AbsoluteLength::Mm(297.0),\n            PaperSize::B5 => AbsoluteLength::Mm(176.0),\n            PaperSize::B4 => AbsoluteLength::Mm(250.0),\n            PaperSize::JisB5 => AbsoluteLength::Mm(182.0),\n            PaperSize::JisB4 => AbsoluteLength::Mm(257.0),\n            PaperSize::Letter => AbsoluteLength::In(8.5),\n            PaperSize::Legal => AbsoluteLength::In(8.5),\n            PaperSize::Ledger => AbsoluteLength::In(11.0),\n        })\n    }\n}\n\n/// Page orientation names.\n///\n/// https://drafts.csswg.org/css-page-3/#page-orientation-prop\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum PageOrientation {\n    /// upright\n    Upright,\n    /// rotate-left (counter-clockwise)\n    RotateLeft,\n    /// rotate-right (clockwise)\n    RotateRight,\n}\n\n/// Paper orientation\n///\n/// https://drafts.csswg.org/css-page-3/#page-size-prop\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum PageSizeOrientation {\n    /// Portrait orientation\n    Portrait,\n    /// Landscape orientation\n    Landscape,\n}\n\n#[inline]\nfn is_portrait(orientation: &PageSizeOrientation) -> bool {\n    *orientation == PageSizeOrientation::Portrait\n}\n\n/// Page size property\n///\n/// https://drafts.csswg.org/css-page-3/#page-size-prop\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericPageSize<S> {\n    /// `auto` value.\n    Auto,\n    /// Page dimensions.\n    Size(S),\n    /// An orientation with no size.\n    Orientation(PageSizeOrientation),\n    /// Paper size by name\n    PaperSize(\n        PaperSize,\n        #[css(skip_if = \"is_portrait\")] PageSizeOrientation,\n    ),\n}\n\npub use self::GenericPageSize as PageSize;\n\nimpl<S> PageSize<S> {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        PageSize::Auto\n    }\n\n    /// Whether this is the `auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, PageSize::Auto)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/position.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS handling of specified and computed values of\n//! [`position`](https://drafts.csswg.org/css-backgrounds-3/#position)\n\nuse cssparser::Parser;\nuse std::fmt::Write;\n\nuse style_derive::Animate;\nuse style_traits::CssWriter;\nuse style_traits::ParseError;\nuse style_traits::SpecifiedValueInfo;\nuse style_traits::ToCss;\n\nuse crate::derives::*;\nuse crate::logical_geometry::PhysicalSide;\nuse crate::parser::{Parse, ParserContext};\nuse crate::rule_tree::CascadeLevel;\nuse crate::values::animated::ToAnimatedZero;\nuse crate::values::computed::position::TryTacticAdjustment;\nuse crate::values::generics::box_::PositionProperty;\nuse crate::values::generics::length::GenericAnchorSizeFunction;\nuse crate::values::generics::ratio::Ratio;\nuse crate::values::generics::Optional;\nuse crate::values::DashedIdent;\n\nuse crate::values::computed::Context;\nuse crate::values::computed::ToComputedValue;\n\n/// Trait to check if the value of a potentially-tree-scoped type T\n/// is actually tree-scoped. e.g. `none` value of `anchor-scope` should\n/// not be tree-scoped.\npub trait IsTreeScoped {\n    /// Returns true if the current value should be considered tree-scoped.\n    /// Default implementation assumes that the value is always tree-scoped.\n    fn is_tree_scoped(&self) -> bool {\n        true\n    }\n}\n\n/// A generic type for representing a value scoped to a specific cascade level\n/// in the shadow tree hierarchy.\n#[repr(C)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n    Serialize,\n    Deserialize,\n)]\npub struct TreeScoped<T> {\n    /// The scoped value.\n    pub value: T,\n    /// The cascade level in the shadow tree hierarchy.\n    #[css(skip)]\n    pub scope: CascadeLevel,\n}\n\nimpl<T: IsTreeScoped + PartialEq> PartialEq for TreeScoped<T> {\n    fn eq(&self, other: &Self) -> bool {\n        let tree_scoped = self.value.is_tree_scoped();\n        if tree_scoped != other.value.is_tree_scoped() {\n            // Trivially different.\n            return false;\n        }\n        let scopes_equal = self.scope == other.scope;\n        if !scopes_equal && tree_scoped {\n            // Scope difference matters if the name is actually tree-scoped.\n            return false;\n        }\n        // Ok, do the actual value comparison.\n        self.value == other.value\n    }\n}\n\nimpl<T> TreeScoped<T> {\n    /// Creates a new `TreeScoped` value.\n    pub fn new(value: T, scope: CascadeLevel) -> Self {\n        Self { value, scope }\n    }\n\n    /// Creates a new `TreeScoped` value with the default cascade level\n    /// (same tree author normal).\n    pub fn with_default_level(value: T) -> Self {\n        Self {\n            value,\n            scope: CascadeLevel::same_tree_author_normal(),\n        }\n    }\n}\n\nimpl<T> Parse for TreeScoped<T>\nwhere\n    T: Parse,\n{\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(TreeScoped {\n            value: T::parse(context, input)?,\n            scope: CascadeLevel::same_tree_author_normal(),\n        })\n    }\n}\n\nimpl<T> ToComputedValue for TreeScoped<T>\nwhere\n    T: ToComputedValue + IsTreeScoped,\n    T::ComputedValue: IsTreeScoped,\n{\n    type ComputedValue = TreeScoped<T::ComputedValue>;\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        TreeScoped {\n            value: self.value.to_computed_value(context),\n            scope: if context.current_scope().is_tree() {\n                context.current_scope()\n            } else {\n                self.scope.clone()\n            },\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self {\n            value: ToComputedValue::from_computed_value(&computed.value),\n            scope: computed.scope.clone(),\n        }\n    }\n}\n\n/// A generic type for representing a CSS [position](https://drafts.csswg.org/css-values/#position).\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\npub struct GenericPosition<H, V> {\n    /// The horizontal component of position.\n    pub horizontal: H,\n    /// The vertical component of position.\n    pub vertical: V,\n}\n\nimpl<H, V> PositionComponent for Position<H, V>\nwhere\n    H: PositionComponent,\n    V: PositionComponent,\n{\n    #[inline]\n    fn is_center(&self) -> bool {\n        self.horizontal.is_center() && self.vertical.is_center()\n    }\n}\n\npub use self::GenericPosition as Position;\n\nimpl<H, V> Position<H, V> {\n    /// Returns a new position.\n    pub fn new(horizontal: H, vertical: V) -> Self {\n        Self {\n            horizontal,\n            vertical,\n        }\n    }\n}\n\n/// Implements a method that checks if the position is centered.\npub trait PositionComponent {\n    /// Returns if the position component is 50% or center.\n    /// For pixel lengths, it always returns false.\n    fn is_center(&self) -> bool;\n}\n\n/// A generic type for representing an `Auto | <position>`.\n/// This is used by <offset-anchor> for now.\n/// https://drafts.fxtf.org/motion-1/#offset-anchor-property\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedZero,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericPositionOrAuto<Pos> {\n    /// The <position> value.\n    Position(Pos),\n    /// The keyword `auto`.\n    Auto,\n}\n\npub use self::GenericPositionOrAuto as PositionOrAuto;\n\nimpl<Pos> PositionOrAuto<Pos> {\n    /// Return `auto`.\n    #[inline]\n    pub fn auto() -> Self {\n        PositionOrAuto::Auto\n    }\n\n    /// Return true if it is 'auto'.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(self, PositionOrAuto::Auto)\n    }\n}\n\n/// A generic value for the `z-index` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericZIndex<I> {\n    /// An integer value.\n    Integer(I),\n    /// The keyword `auto`.\n    Auto,\n}\n\npub use self::GenericZIndex as ZIndex;\n\nimpl<Integer> ZIndex<Integer> {\n    /// Returns `auto`\n    #[inline]\n    pub fn auto() -> Self {\n        ZIndex::Auto\n    }\n\n    /// Returns whether `self` is `auto`.\n    #[inline]\n    pub fn is_auto(self) -> bool {\n        matches!(self, ZIndex::Auto)\n    }\n\n    /// Returns the integer value if it is an integer, or `auto`.\n    #[inline]\n    pub fn integer_or(self, auto: Integer) -> Integer {\n        match self {\n            ZIndex::Integer(n) => n,\n            ZIndex::Auto => auto,\n        }\n    }\n}\n\n/// Ratio or None.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum PreferredRatio<N> {\n    /// Without specified ratio\n    #[css(skip)]\n    None,\n    /// With specified ratio\n    Ratio(\n        #[animation(field_bound)]\n        #[css(field_bound)]\n        #[distance(field_bound)]\n        Ratio<N>,\n    ),\n}\n\n/// A generic value for the `aspect-ratio` property, the value is `auto || <ratio>`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericAspectRatio<N> {\n    /// Specifiy auto or not.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub auto: bool,\n    /// The preferred aspect-ratio value.\n    #[animation(field_bound)]\n    #[css(field_bound)]\n    #[distance(field_bound)]\n    pub ratio: PreferredRatio<N>,\n}\n\npub use self::GenericAspectRatio as AspectRatio;\n\nimpl<N> AspectRatio<N> {\n    /// Returns `auto`\n    #[inline]\n    pub fn auto() -> Self {\n        AspectRatio {\n            auto: true,\n            ratio: PreferredRatio::None,\n        }\n    }\n}\n\nimpl<N> ToAnimatedZero for AspectRatio<N> {\n    #[inline]\n    fn to_animated_zero(&self) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\n/// Specified type for `inset` properties, which allows\n/// the use of the `anchor()` function.\n/// Note(dshin): `LengthPercentageOrAuto` is not used here because\n/// having `LengthPercentageOrAuto` and `AnchorFunction` in the enum\n/// pays the price of the discriminator for `LengthPercentage | Auto`\n/// as well as `LengthPercentageOrAuto | AnchorFunction`. This increases\n/// the size of the style struct, which would not be great.\n/// On the other hand, we trade for code duplication, so... :(\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToCss,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(C)]\npub enum GenericInset<P, LP> {\n    /// A `<length-percentage>` value.\n    LengthPercentage(LP),\n    /// An `auto` value.\n    Auto,\n    /// Inset defined by the anchor element.\n    ///\n    /// <https://drafts.csswg.org/css-anchor-position-1/#anchor-pos>\n    AnchorFunction(Box<GenericAnchorFunction<P, Self>>),\n    /// Inset defined by the size of the anchor element.\n    ///\n    /// <https://drafts.csswg.org/css-anchor-position-1/#anchor-pos>\n    AnchorSizeFunction(Box<GenericAnchorSizeFunction<Self>>),\n    /// A `<length-percentage>` value, guaranteed to contain `calc()`,\n    /// which then is guaranteed to contain `anchor()` or `anchor-size()`.\n    AnchorContainingCalcFunction(LP),\n}\n\nimpl<P, LP> SpecifiedValueInfo for GenericInset<P, LP>\nwhere\n    LP: SpecifiedValueInfo,\n{\n    fn collect_completion_keywords(f: style_traits::KeywordsCollectFn) {\n        LP::collect_completion_keywords(f);\n        f(&[\"auto\"]);\n        if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            f(&[\"anchor\", \"anchor-size\"]);\n        }\n    }\n}\n\nimpl<P, LP> GenericInset<P, LP> {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        Self::Auto\n    }\n\n    /// Return true if it is 'auto'.\n    #[inline]\n    #[cfg(feature = \"servo\")]\n    pub fn is_auto(&self) -> bool {\n        matches!(self, Self::Auto)\n    }\n}\n\npub use self::GenericInset as Inset;\n\n/// Anchor function used by inset properties. This resolves\n/// to length at computed time.\n///\n/// https://drafts.csswg.org/css-anchor-position-1/#funcdef-anchor\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    Serialize,\n    Deserialize,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericAnchorFunction<Percentage, Fallback> {\n    /// Anchor name of the element to anchor to.\n    /// If omitted, selects the implicit anchor element.\n    /// The shadow cascade order of the tree-scoped anchor name\n    /// associates the name with the host of the originating stylesheet.\n    #[animation(constant)]\n    pub target_element: TreeScoped<DashedIdent>,\n    /// Where relative to the target anchor element to position\n    /// the anchored element to.\n    pub side: GenericAnchorSide<Percentage>,\n    /// Value to use in case the anchor function is invalid.\n    pub fallback: Optional<Fallback>,\n}\n\nimpl<Percentage, Fallback> ToCss for GenericAnchorFunction<Percentage, Fallback>\nwhere\n    Percentage: ToCss,\n    Fallback: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> std::fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"anchor(\")?;\n        if !self.target_element.value.is_empty() {\n            self.target_element.to_css(dest)?;\n            dest.write_str(\" \")?;\n        }\n        self.side.to_css(dest)?;\n        if let Some(f) = self.fallback.as_ref() {\n            // This comma isn't really `derive()`-able, unfortunately.\n            dest.write_str(\", \")?;\n            f.to_css(dest)?;\n        }\n        dest.write_str(\")\")\n    }\n}\n\nimpl<Percentage, Fallback> GenericAnchorFunction<Percentage, Fallback> {\n    /// Is the anchor valid for given property?\n    pub fn valid_for(&self, side: PhysicalSide, position_property: PositionProperty) -> bool {\n        position_property.is_absolutely_positioned() && self.side.valid_for(side)\n    }\n}\n\n/// Keyword values for the anchor positioning function.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    Parse,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    Serialize,\n    Deserialize,\n)]\n#[repr(u8)]\npub enum AnchorSideKeyword {\n    /// Inside relative (i.e. Same side) to the inset property it's used in.\n    Inside,\n    /// Same as above, but outside (i.e. Opposite side).\n    Outside,\n    /// Top of the anchor element.\n    Top,\n    /// Left of the anchor element.\n    Left,\n    /// Right of the anchor element.\n    Right,\n    /// Bottom of the anchor element.\n    Bottom,\n    /// Refers to the start side of the anchor element for the same axis of the inset\n    /// property it's used in, resolved against the positioned element's containing\n    /// block's writing mode.\n    Start,\n    /// Same as above, but for the end side.\n    End,\n    /// Same as `start`, resolved against the positioned element's writing mode.\n    SelfStart,\n    /// Same as above, but for the end side.\n    SelfEnd,\n    /// Halfway between `start` and `end` sides.\n    Center,\n}\n\nimpl AnchorSideKeyword {\n    fn from_physical_side(side: PhysicalSide) -> Self {\n        match side {\n            PhysicalSide::Top => Self::Top,\n            PhysicalSide::Right => Self::Right,\n            PhysicalSide::Bottom => Self::Bottom,\n            PhysicalSide::Left => Self::Left,\n        }\n    }\n\n    fn physical_side(self) -> Option<PhysicalSide> {\n        Some(match self {\n            Self::Top => PhysicalSide::Top,\n            Self::Right => PhysicalSide::Right,\n            Self::Bottom => PhysicalSide::Bottom,\n            Self::Left => PhysicalSide::Left,\n            _ => return None,\n        })\n    }\n}\n\nimpl TryTacticAdjustment for AnchorSideKeyword {\n    fn try_tactic_adjustment(&mut self, old_side: PhysicalSide, new_side: PhysicalSide) {\n        if !old_side.parallel_to(new_side) {\n            let Some(s) = self.physical_side() else {\n                return;\n            };\n            *self = Self::from_physical_side(if s == new_side {\n                old_side\n            } else if s == old_side {\n                new_side\n            } else if s == new_side.opposite_side() {\n                old_side.opposite_side()\n            } else {\n                debug_assert_eq!(s, old_side.opposite_side());\n                new_side.opposite_side()\n            });\n            return;\n        }\n\n        *self = match self {\n            Self::Center | Self::Inside | Self::Outside => *self,\n            Self::SelfStart => Self::SelfEnd,\n            Self::SelfEnd => Self::SelfStart,\n            Self::Start => Self::End,\n            Self::End => Self::Start,\n            Self::Top => Self::Bottom,\n            Self::Bottom => Self::Top,\n            Self::Left => Self::Right,\n            Self::Right => Self::Left,\n        }\n    }\n}\n\nimpl AnchorSideKeyword {\n    fn valid_for(&self, side: PhysicalSide) -> bool {\n        match self {\n            Self::Left | Self::Right => matches!(side, PhysicalSide::Left | PhysicalSide::Right),\n            Self::Top | Self::Bottom => matches!(side, PhysicalSide::Top | PhysicalSide::Bottom),\n            Self::Inside\n            | Self::Outside\n            | Self::Start\n            | Self::End\n            | Self::SelfStart\n            | Self::SelfEnd\n            | Self::Center => true,\n        }\n    }\n}\n\n/// Anchor side for the anchor positioning function.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    Serialize,\n    Deserialize,\n)]\n#[repr(C)]\npub enum GenericAnchorSide<P> {\n    /// A keyword value for the anchor side.\n    Keyword(AnchorSideKeyword),\n    /// Percentage value between the `start` and `end` sides.\n    Percentage(P),\n}\n\nimpl<P> GenericAnchorSide<P> {\n    /// Is this anchor side valid for a given side?\n    pub fn valid_for(&self, side: PhysicalSide) -> bool {\n        match self {\n            Self::Keyword(k) => k.valid_for(side),\n            Self::Percentage(_) => true,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/generics/ratio.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values related to <ratio>.\n//! https://drafts.csswg.org/css-values/#ratios\n\nuse crate::derives::*;\nuse crate::{One, Zero};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A generic value for the `<ratio>` value.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct Ratio<N>(pub N, pub N);\n\nimpl<N> ToCss for Ratio<N>\nwhere\n    N: ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.0.to_css(dest)?;\n        // Even though 1 could be omitted, we don't per\n        // https://drafts.csswg.org/css-values-4/#ratio-value:\n        //\n        //     The second <number> is optional, defaulting to 1. However,\n        //     <ratio> is always serialized with both components.\n        //\n        // And for compat reasons, see bug 1669742.\n        //\n        // We serialize with spaces for consistency with all other\n        // slash-delimited things, see\n        // https://github.com/w3c/csswg-drafts/issues/4282\n        dest.write_str(\" / \")?;\n        self.1.to_css(dest)?;\n        Ok(())\n    }\n}\n\nimpl<N> Ratio<N>\nwhere\n    N: Zero + One,\n{\n    /// Returns true if this is a degenerate ratio.\n    /// https://drafts.csswg.org/css-values/#degenerate-ratio\n    #[inline]\n    pub fn is_degenerate(&self) -> bool {\n        self.0.is_zero() || self.1.is_zero()\n    }\n\n    /// Returns the used value. A ratio of 0/0 behaves as the ratio 1/0.\n    /// https://drafts.csswg.org/css-values-4/#ratios\n    pub fn used_value(self) -> Self {\n        if self.0.is_zero() && self.1.is_zero() {\n            Self(One::one(), Zero::zero())\n        } else {\n            self\n        }\n    }\n}\n\nimpl<N> Zero for Ratio<N>\nwhere\n    N: Zero + One,\n{\n    fn zero() -> Self {\n        Self(Zero::zero(), One::one())\n    }\n\n    fn is_zero(&self) -> bool {\n        self.0.is_zero()\n    }\n}\n"
  },
  {
    "path": "style/values/generics/rect.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values that are composed of four sides.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// A CSS value made of four components, where its `ToCss` impl will try to\n/// serialize as few components as possible, like for example in `border-width`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    Serialize,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct Rect<T>(pub T, pub T, pub T, pub T);\n\nimpl<T> Rect<T> {\n    /// Returns a new `Rect<T>` value.\n    pub fn new(first: T, second: T, third: T, fourth: T) -> Self {\n        Rect(first, second, third, fourth)\n    }\n}\n\nimpl<T> Rect<T>\nwhere\n    T: Clone,\n{\n    /// Returns a rect with all the values equal to `v`.\n    pub fn all(v: T) -> Self {\n        Rect::new(v.clone(), v.clone(), v.clone(), v)\n    }\n\n    /// Parses a new `Rect<T>` value with the given parse function.\n    pub fn parse_with<'i, 't, Parse>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parse: Parse,\n    ) -> Result<Self, ParseError<'i>>\n    where\n        Parse: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<T, ParseError<'i>>,\n    {\n        let first = parse(context, input)?;\n        let second = if let Ok(second) = input.try_parse(|i| parse(context, i)) {\n            second\n        } else {\n            // <first>\n            return Ok(Self::new(\n                first.clone(),\n                first.clone(),\n                first.clone(),\n                first,\n            ));\n        };\n        let third = if let Ok(third) = input.try_parse(|i| parse(context, i)) {\n            third\n        } else {\n            // <first> <second>\n            return Ok(Self::new(first.clone(), second.clone(), first, second));\n        };\n        let fourth = if let Ok(fourth) = input.try_parse(|i| parse(context, i)) {\n            fourth\n        } else {\n            // <first> <second> <third>\n            return Ok(Self::new(first, second.clone(), third, second));\n        };\n        // <first> <second> <third> <fourth>\n        Ok(Self::new(first, second, third, fourth))\n    }\n\n    /// Parses a new `Rect<T>` value which all components must be specified, with the given parse\n    /// function.\n    pub fn parse_all_components_with<'i, 't, Parse>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parse: Parse,\n    ) -> Result<Self, ParseError<'i>>\n    where\n        Parse: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<T, ParseError<'i>>,\n    {\n        let first = parse(context, input)?;\n        let second = parse(context, input)?;\n        let third = parse(context, input)?;\n        let fourth = parse(context, input)?;\n        // <first> <second> <third> <fourth>\n        Ok(Self::new(first, second, third, fourth))\n    }\n}\n\nimpl<T> Parse for Rect<T>\nwhere\n    T: Clone + Parse,\n{\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with(context, input, T::parse)\n    }\n}\n\nimpl<T> ToCss for Rect<T>\nwhere\n    T: PartialEq + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.0.to_css(dest)?;\n        let same_vertical = self.0 == self.2;\n        let same_horizontal = self.1 == self.3;\n        if same_vertical && same_horizontal && self.0 == self.1 {\n            return Ok(());\n        }\n        dest.write_char(' ')?;\n        self.1.to_css(dest)?;\n        if same_vertical && same_horizontal {\n            return Ok(());\n        }\n        dest.write_char(' ')?;\n        self.2.to_css(dest)?;\n        if same_horizontal {\n            return Ok(());\n        }\n        dest.write_char(' ')?;\n        self.3.to_css(dest)\n    }\n}\n"
  },
  {
    "path": "style/values/generics/size.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic type for CSS properties that are composed by two dimensions.\n\nuse crate::derives::*;\nuse crate::parser::ParserContext;\nuse crate::Zero;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// A generic size, for `border-*-radius` longhand properties, or\n/// `border-spacing`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    Serialize,\n    ToAnimatedZero,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(C)]\npub struct Size2D<L> {\n    pub width: L,\n    pub height: L,\n}\n\nimpl<L> Size2D<L> {\n    #[inline]\n    /// Create a new `Size2D` for an area of given width and height.\n    pub fn new(width: L, height: L) -> Self {\n        Self { width, height }\n    }\n\n    /// Returns the width component.\n    pub fn width(&self) -> &L {\n        &self.width\n    }\n\n    /// Returns the height component.\n    pub fn height(&self) -> &L {\n        &self.height\n    }\n\n    /// Parse a `Size2D` with a given parsing function.\n    pub fn parse_with<'i, 't, F>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parse_one: F,\n    ) -> Result<Self, ParseError<'i>>\n    where\n        L: Clone,\n        F: Fn(&ParserContext, &mut Parser<'i, 't>) -> Result<L, ParseError<'i>>,\n    {\n        let first = parse_one(context, input)?;\n        let second = input\n            .try_parse(|i| parse_one(context, i))\n            .unwrap_or_else(|_| first.clone());\n        Ok(Self::new(first, second))\n    }\n}\n\nimpl<L> ToCss for Size2D<L>\nwhere\n    L: ToCss + PartialEq,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.width.to_css(dest)?;\n\n        if self.height != self.width {\n            dest.write_char(' ')?;\n            self.height.to_css(dest)?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<L: Zero> Zero for Size2D<L> {\n    fn zero() -> Self {\n        Self::new(L::zero(), L::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        self.width.is_zero() && self.height.is_zero()\n    }\n}\n"
  },
  {
    "path": "style/values/generics/svg.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values in SVG\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse cssparser::Parser;\nuse style_traits::ParseError;\n\n/// The fallback of an SVG paint server value.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericSVGPaintFallback<C> {\n    /// The `none` keyword.\n    None,\n    /// A magic value that represents no fallback specified and serializes to\n    /// the empty string.\n    #[css(skip)]\n    Unset,\n    /// A color.\n    Color(C),\n}\n\npub use self::GenericSVGPaintFallback as SVGPaintFallback;\n\n/// An SVG paint value\n///\n/// <https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint>\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[animation(no_bound(Url))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericSVGPaint<Color, Url> {\n    /// The paint source.\n    pub kind: GenericSVGPaintKind<Color, Url>,\n    /// The fallback color.\n    pub fallback: GenericSVGPaintFallback<Color>,\n}\n\npub use self::GenericSVGPaint as SVGPaint;\n\nimpl<C, U> Default for SVGPaint<C, U> {\n    fn default() -> Self {\n        Self {\n            kind: SVGPaintKind::None,\n            fallback: SVGPaintFallback::Unset,\n        }\n    }\n}\n\n/// An SVG paint value without the fallback.\n///\n/// Whereas the spec only allows PaintServer to have a fallback, Gecko lets the\n/// context properties have a fallback as well.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[animation(no_bound(U))]\n#[repr(C, u8)]\npub enum GenericSVGPaintKind<C, U> {\n    /// `none`\n    #[animation(error)]\n    None,\n    /// `<color>`\n    Color(C),\n    /// `url(...)`\n    #[animation(error)]\n    PaintServer(U),\n    /// `context-fill`\n    ContextFill,\n    /// `context-stroke`\n    ContextStroke,\n}\n\npub use self::GenericSVGPaintKind as SVGPaintKind;\n\nimpl<C: Parse, U: Parse> Parse for SVGPaint<C, U> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let kind = SVGPaintKind::parse(context, input)?;\n        if matches!(kind, SVGPaintKind::None | SVGPaintKind::Color(..)) {\n            return Ok(SVGPaint {\n                kind,\n                fallback: SVGPaintFallback::Unset,\n            });\n        }\n        let fallback = input\n            .try_parse(|i| SVGPaintFallback::parse(context, i))\n            .unwrap_or(SVGPaintFallback::Unset);\n        Ok(SVGPaint { kind, fallback })\n    }\n}\n\n/// An SVG length value supports `context-value` in addition to length.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericSVGLength<L> {\n    /// `<length> | <percentage> | <number>`\n    LengthPercentage(L),\n    /// `context-value`\n    #[animation(error)]\n    ContextValue,\n}\n\npub use self::GenericSVGLength as SVGLength;\n\n/// Generic value for stroke-dasharray.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericSVGStrokeDashArray<L> {\n    /// `[ <length> | <percentage> | <number> ]#`\n    #[css(comma)]\n    Values(#[css(if_empty = \"none\", iterable)] crate::OwnedSlice<L>),\n    /// `context-value`\n    ContextValue,\n}\n\npub use self::GenericSVGStrokeDashArray as SVGStrokeDashArray;\n\n/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in\n/// addition to opacity value.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericSVGOpacity<OpacityType> {\n    /// `<opacity-value>`\n    Opacity(OpacityType),\n    /// `context-fill-opacity`\n    #[animation(error)]\n    ContextFillOpacity,\n    /// `context-stroke-opacity`\n    #[animation(error)]\n    ContextStrokeOpacity,\n}\n\npub use self::GenericSVGOpacity as SVGOpacity;\n"
  },
  {
    "path": "style/values/generics/text.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for text properties.\n\nuse crate::derives::*;\nuse crate::Zero;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A generic value that is either a number or `auto`.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum NumberOrAuto<N> {\n    /// `auto`\n    Auto,\n    /// `<number>`\n    Number(N),\n}\n\n/// A generic value for the `hyphenate-limit-chars` property.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericHyphenateLimitChars<Integer> {\n    /// Required minimum number of characters in a hyphenated word.\n    pub total_word_length: NumberOrAuto<Integer>,\n    /// Required minumum number of characters before the hyphen.\n    pub pre_hyphen_length: NumberOrAuto<Integer>,\n    /// Required minumum number of characters after the hyphen.\n    pub post_hyphen_length: NumberOrAuto<Integer>,\n}\n\nimpl<Integer: ToCss + PartialEq> ToCss for GenericHyphenateLimitChars<Integer> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.total_word_length.to_css(dest)?;\n\n        if self.pre_hyphen_length != NumberOrAuto::Auto\n            || self.post_hyphen_length != self.pre_hyphen_length\n        {\n            dest.write_char(' ')?;\n            self.pre_hyphen_length.to_css(dest)?;\n            if self.post_hyphen_length != self.pre_hyphen_length {\n                dest.write_char(' ')?;\n                self.post_hyphen_length.to_css(dest)?;\n            }\n        }\n\n        Ok(())\n    }\n}\n\n/// A generic value for the `initial-letter` property.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\npub struct GenericInitialLetter<Number, Integer> {\n    /// The size, >=1, or 0 if `normal`.\n    pub size: Number,\n    /// The sink, >=1, if specified, 0 otherwise.\n    pub sink: Integer,\n}\n\npub use self::GenericInitialLetter as InitialLetter;\nimpl<N: Zero, I: Zero> InitialLetter<N, I> {\n    /// Returns `normal`.\n    #[inline]\n    pub fn normal() -> Self {\n        InitialLetter {\n            size: N::zero(),\n            sink: I::zero(),\n        }\n    }\n}\n\nimpl<N: ToCss + Zero, I: ToCss + Zero> ToCss for InitialLetter<N, I> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.size.is_zero() {\n            return dest.write_str(\"normal\");\n        }\n        self.size.to_css(dest)?;\n        if !self.sink.is_zero() {\n            dest.write_char(' ')?;\n            self.sink.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// Implements type for text-decoration-thickness\n/// which takes the grammar of auto | from-font | <length> | <percentage>\n///\n/// https://drafts.csswg.org/css-text-decor-4/\n#[repr(C, u8)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Animate,\n    Clone,\n    Copy,\n    ComputeSquaredDistance,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum GenericTextDecorationLength<L> {\n    LengthPercentage(L),\n    Auto,\n    FromFont,\n}\n\n/// Text decoration inset values.\n///\n/// https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-inset-property\n#[repr(C, u8)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\npub enum GenericTextDecorationInset<L> {\n    /// `auto` value\n    Auto,\n    /// Start and end length values.\n    #[allow(missing_docs)]\n    Length { start: L, end: L },\n}\n\nimpl<L: Zero> GenericTextDecorationInset<L> {\n    /// Gets the initial value (zero)\n    #[inline]\n    pub fn get_initial_value() -> Self {\n        GenericTextDecorationInset::Length {\n            start: L::zero(),\n            end: L::zero(),\n        }\n    }\n}\n\nimpl<L: ToCss + PartialEq> ToCss for GenericTextDecorationInset<L> {\n    fn to_css<W>(&self, dst: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match self {\n            GenericTextDecorationInset::Auto => dst.write_str(\"auto\"),\n            GenericTextDecorationInset::Length { start, end } => {\n                start.to_css(dst)?;\n                if start != end {\n                    dst.write_char(' ')?;\n                    end.to_css(dst)?;\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\n/// Implements type for text-indent\n/// which takes the grammar of [<length-percentage>] && hanging? && each-line?\n///\n/// https://drafts.csswg.org/css-text/#propdef-text-indent\n#[repr(C)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[typed(todo_derive_fields)]\npub struct GenericTextIndent<LengthPercentage> {\n    /// The amount of indent to be applied to the inline-start of the first line.\n    pub length: LengthPercentage,\n    /// Apply indent to non-first lines instead of first.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub hanging: bool,\n    /// Apply to each line after a hard break, not only first in block.\n    #[animation(constant)]\n    #[css(represents_keyword)]\n    pub each_line: bool,\n}\n\nimpl<LengthPercentage: Zero> GenericTextIndent<LengthPercentage> {\n    /// Return the initial zero value.\n    pub fn zero() -> Self {\n        Self {\n            length: LengthPercentage::zero(),\n            hanging: false,\n            each_line: false,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/generics/transform.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for CSS values that are related to transformations.\n\nuse crate::derives::*;\nuse crate::values::computed::length::Length as ComputedLength;\nuse crate::values::computed::length::LengthPercentage as ComputedLengthPercentage;\nuse crate::values::specified::angle::Angle as SpecifiedAngle;\nuse crate::values::specified::length::Length as SpecifiedLength;\nuse crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;\nuse crate::values::{computed, CSSFloat};\nuse crate::{Zero, ZeroNoPercent};\nuse euclid::default::{Rect, Transform3D};\nuse std::fmt::{self, Write};\nuse std::ops::Neg;\nuse style_traits::{CssWriter, ToCss};\n\n/// A generic 2D transformation matrix.\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(comma, function = \"matrix\")]\n#[repr(C)]\npub struct GenericMatrix<T> {\n    pub a: T,\n    pub b: T,\n    pub c: T,\n    pub d: T,\n    pub e: T,\n    pub f: T,\n}\n\npub use self::GenericMatrix as Matrix;\n\n#[allow(missing_docs)]\n#[cfg_attr(rustfmt, rustfmt_skip)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(comma, function = \"matrix3d\")]\n#[repr(C)]\npub struct GenericMatrix3D<T> {\n    pub m11: T, pub m12: T, pub m13: T, pub m14: T,\n    pub m21: T, pub m22: T, pub m23: T, pub m24: T,\n    pub m31: T, pub m32: T, pub m33: T, pub m34: T,\n    pub m41: T, pub m42: T, pub m43: T, pub m44: T,\n}\n\npub use self::GenericMatrix3D as Matrix3D;\n\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl<T: Into<f64>> From<Matrix<T>> for Transform3D<f64> {\n    #[inline]\n    fn from(m: Matrix<T>) -> Self {\n        Transform3D::new(\n            m.a.into(), m.b.into(), 0.0, 0.0,\n            m.c.into(), m.d.into(), 0.0, 0.0,\n            0.0,        0.0,        1.0, 0.0,\n            m.e.into(), m.f.into(), 0.0, 1.0,\n        )\n    }\n}\n\n#[cfg_attr(rustfmt, rustfmt_skip)]\nimpl<T: Into<f64>> From<Matrix3D<T>> for Transform3D<f64> {\n    #[inline]\n    fn from(m: Matrix3D<T>) -> Self {\n        Transform3D::new(\n            m.m11.into(), m.m12.into(), m.m13.into(), m.m14.into(),\n            m.m21.into(), m.m22.into(), m.m23.into(), m.m24.into(),\n            m.m31.into(), m.m32.into(), m.m33.into(), m.m34.into(),\n            m.m41.into(), m.m42.into(), m.m43.into(), m.m44.into(),\n        )\n    }\n}\n\n/// A generic transform origin.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericTransformOrigin<H, V, Depth> {\n    /// The horizontal origin.\n    pub horizontal: H,\n    /// The vertical origin.\n    pub vertical: V,\n    /// The depth.\n    pub depth: Depth,\n}\n\npub use self::GenericTransformOrigin as TransformOrigin;\n\nimpl<H, V, D> TransformOrigin<H, V, D> {\n    /// Returns a new transform origin.\n    pub fn new(horizontal: H, vertical: V, depth: D) -> Self {\n        Self {\n            horizontal,\n            vertical,\n            depth,\n        }\n    }\n}\n\nfn is_same<N: PartialEq>(x: &N, y: &N) -> bool {\n    x == y\n}\n\n/// A value for the `perspective()` transform function, which is either a\n/// non-negative `<length>` or `none`.\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum GenericPerspectiveFunction<L> {\n    /// `none`\n    None,\n    /// A `<length>`.\n    Length(L),\n}\n\nimpl<L> GenericPerspectiveFunction<L> {\n    /// Returns `f32::INFINITY` or the result of a function on the length value.\n    pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 {\n        match *self {\n            Self::None => f32::INFINITY,\n            Self::Length(ref l) => f(l),\n        }\n    }\n}\n\npub use self::GenericPerspectiveFunction as PerspectiveFunction;\n\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\n/// A single operation in the list of a `transform` value\npub enum GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>\nwhere\n    Angle: Zero,\n    LengthPercentage: Zero + ZeroNoPercent,\n    Number: PartialEq,\n{\n    /// Represents a 2D 2x3 matrix.\n    Matrix(GenericMatrix<Number>),\n    /// Represents a 3D 4x4 matrix.\n    Matrix3D(GenericMatrix3D<Number>),\n    /// A 2D skew.\n    ///\n    /// If the second angle is not provided it is assumed zero.\n    ///\n    /// Syntax can be skew(angle) or skew(angle, angle)\n    #[css(comma, function)]\n    Skew(Angle, #[css(skip_if = \"Zero::is_zero\")] Angle),\n    /// skewX(angle)\n    #[css(function = \"skewX\")]\n    SkewX(Angle),\n    /// skewY(angle)\n    #[css(function = \"skewY\")]\n    SkewY(Angle),\n    /// translate(x, y) or translate(x)\n    #[css(comma, function)]\n    Translate(\n        LengthPercentage,\n        #[css(skip_if = \"ZeroNoPercent::is_zero_no_percent\")] LengthPercentage,\n    ),\n    /// translateX(x)\n    #[css(function = \"translateX\")]\n    TranslateX(LengthPercentage),\n    /// translateY(y)\n    #[css(function = \"translateY\")]\n    TranslateY(LengthPercentage),\n    /// translateZ(z)\n    #[css(function = \"translateZ\")]\n    TranslateZ(Length),\n    /// translate3d(x, y, z)\n    #[css(comma, function = \"translate3d\")]\n    Translate3D(LengthPercentage, LengthPercentage, Length),\n    /// A 2D scaling factor.\n    ///\n    /// Syntax can be scale(factor) or scale(factor, factor)\n    #[css(comma, function)]\n    Scale(Number, #[css(contextual_skip_if = \"is_same\")] Number),\n    /// scaleX(factor)\n    #[css(function = \"scaleX\")]\n    ScaleX(Number),\n    /// scaleY(factor)\n    #[css(function = \"scaleY\")]\n    ScaleY(Number),\n    /// scaleZ(factor)\n    #[css(function = \"scaleZ\")]\n    ScaleZ(Number),\n    /// scale3D(factorX, factorY, factorZ)\n    #[css(comma, function = \"scale3d\")]\n    Scale3D(Number, Number, Number),\n    /// Describes a 2D Rotation.\n    ///\n    /// In a 3D scene `rotate(angle)` is equivalent to `rotateZ(angle)`.\n    #[css(function)]\n    Rotate(Angle),\n    /// Rotation in 3D space around the x-axis.\n    #[css(function = \"rotateX\")]\n    RotateX(Angle),\n    /// Rotation in 3D space around the y-axis.\n    #[css(function = \"rotateY\")]\n    RotateY(Angle),\n    /// Rotation in 3D space around the z-axis.\n    #[css(function = \"rotateZ\")]\n    RotateZ(Angle),\n    /// Rotation in 3D space.\n    ///\n    /// Generalization of rotateX, rotateY and rotateZ.\n    #[css(comma, function = \"rotate3d\")]\n    Rotate3D(Number, Number, Number, Angle),\n    /// Specifies a perspective projection matrix.\n    ///\n    /// Part of CSS Transform Module Level 2 and defined at\n    /// [§ 13.1. 3D Transform Function](https://drafts.csswg.org/css-transforms-2/#funcdef-perspective).\n    ///\n    /// The value must be greater than or equal to zero.\n    #[css(function)]\n    Perspective(GenericPerspectiveFunction<Length>),\n    /// A intermediate type for interpolation of mismatched transform lists.\n    #[allow(missing_docs)]\n    #[css(comma, function = \"interpolatematrix\")]\n    InterpolateMatrix {\n        from_list: GenericTransform<\n            GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,\n        >,\n        to_list: GenericTransform<\n            GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,\n        >,\n        progress: computed::Percentage,\n    },\n    /// A intermediate type for accumulation of mismatched transform lists.\n    #[allow(missing_docs)]\n    #[css(comma, function = \"accumulatematrix\")]\n    AccumulateMatrix {\n        from_list: GenericTransform<\n            GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,\n        >,\n        to_list: GenericTransform<\n            GenericTransformOperation<Angle, Number, Length, Integer, LengthPercentage>,\n        >,\n        count: Integer,\n    },\n}\n\npub use self::GenericTransformOperation as TransformOperation;\n\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// A value of the `transform` property\npub struct GenericTransform<T>(#[css(if_empty = \"none\", iterable)] pub crate::OwnedSlice<T>);\n\npub use self::GenericTransform as Transform;\n\nimpl<Angle, Number, Length, Integer, LengthPercentage>\n    TransformOperation<Angle, Number, Length, Integer, LengthPercentage>\nwhere\n    Angle: Zero,\n    LengthPercentage: Zero + ZeroNoPercent,\n    Number: PartialEq,\n{\n    /// Check if it is any rotate function.\n    pub fn is_rotate(&self) -> bool {\n        use self::TransformOperation::*;\n        matches!(\n            *self,\n            Rotate(..) | Rotate3D(..) | RotateX(..) | RotateY(..) | RotateZ(..)\n        )\n    }\n\n    /// Check if it is any translate function\n    pub fn is_translate(&self) -> bool {\n        use self::TransformOperation::*;\n        match *self {\n            Translate(..) | Translate3D(..) | TranslateX(..) | TranslateY(..) | TranslateZ(..) => {\n                true\n            },\n            _ => false,\n        }\n    }\n\n    /// Check if it is any scale function\n    pub fn is_scale(&self) -> bool {\n        use self::TransformOperation::*;\n        match *self {\n            Scale(..) | Scale3D(..) | ScaleX(..) | ScaleY(..) | ScaleZ(..) => true,\n            _ => false,\n        }\n    }\n}\n\n/// Convert a length type into the absolute lengths.\npub trait ToAbsoluteLength {\n    /// Returns the absolute length as pixel value.\n    fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()>;\n}\n\nimpl ToAbsoluteLength for SpecifiedLength {\n    // This returns Err(()) if there is any relative length or percentage. We use this when\n    // parsing a transform list of DOMMatrix because we want to return a DOM Exception\n    // if there is relative length.\n    #[inline]\n    fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {\n        match *self {\n            SpecifiedLength::NoCalc(len) => len.to_computed_pixel_length_without_context(),\n            SpecifiedLength::Calc(ref calc) => calc.to_computed_pixel_length_without_context(),\n        }\n    }\n}\n\nimpl ToAbsoluteLength for SpecifiedLengthPercentage {\n    // This returns Err(()) if there is any relative length or percentage. We use this when\n    // parsing a transform list of DOMMatrix because we want to return a DOM Exception\n    // if there is relative length.\n    #[inline]\n    fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {\n        use self::SpecifiedLengthPercentage::*;\n        match *self {\n            Length(len) => len.to_computed_pixel_length_without_context(),\n            Calc(ref calc) => calc.to_computed_pixel_length_without_context(),\n            Percentage(..) => Err(()),\n        }\n    }\n}\n\nimpl ToAbsoluteLength for ComputedLength {\n    #[inline]\n    fn to_pixel_length(&self, _containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {\n        Ok(self.px())\n    }\n}\n\nimpl ToAbsoluteLength for ComputedLengthPercentage {\n    #[inline]\n    fn to_pixel_length(&self, containing_len: Option<ComputedLength>) -> Result<CSSFloat, ()> {\n        Ok(self\n            .maybe_percentage_relative_to(containing_len)\n            .ok_or(())?\n            .px())\n    }\n}\n\n/// Support the conversion to a 3d matrix.\npub trait ToMatrix {\n    /// Check if it is a 3d transform function.\n    fn is_3d(&self) -> bool;\n\n    /// Return the equivalent 3d matrix.\n    fn to_3d_matrix(\n        &self,\n        reference_box: Option<&Rect<ComputedLength>>,\n    ) -> Result<Transform3D<f64>, ()>;\n}\n\n/// A little helper to deal with both specified and computed angles.\npub trait ToRadians {\n    /// Return the radians value as a 64-bit floating point value.\n    fn radians64(&self) -> f64;\n}\n\nimpl ToRadians for computed::angle::Angle {\n    #[inline]\n    fn radians64(&self) -> f64 {\n        computed::angle::Angle::radians64(self)\n    }\n}\n\nimpl ToRadians for SpecifiedAngle {\n    #[inline]\n    fn radians64(&self) -> f64 {\n        computed::angle::Angle::from_degrees(self.degrees()).radians64()\n    }\n}\n\nimpl<Angle, Number, Length, Integer, LoP> ToMatrix\n    for TransformOperation<Angle, Number, Length, Integer, LoP>\nwhere\n    Angle: Zero + ToRadians + Copy,\n    Number: PartialEq + Copy + Into<f32> + Into<f64>,\n    Length: ToAbsoluteLength,\n    LoP: Zero + ToAbsoluteLength + ZeroNoPercent,\n{\n    #[inline]\n    fn is_3d(&self) -> bool {\n        use self::TransformOperation::*;\n        match *self {\n            Translate3D(..) | TranslateZ(..) | Rotate3D(..) | RotateX(..) | RotateY(..)\n            | RotateZ(..) | Scale3D(..) | ScaleZ(..) | Perspective(..) | Matrix3D(..) => true,\n            _ => false,\n        }\n    }\n\n    /// If |reference_box| is None, we will drop the percent part from translate because\n    /// we cannot resolve it without the layout info, for computed TransformOperation.\n    /// However, for specified TransformOperation, we will return Err(()) if there is any relative\n    /// lengths because the only caller, DOMMatrix, doesn't accept relative lengths.\n    #[inline]\n    fn to_3d_matrix(\n        &self,\n        reference_box: Option<&Rect<ComputedLength>>,\n    ) -> Result<Transform3D<f64>, ()> {\n        use self::TransformOperation::*;\n\n        let reference_width = reference_box.map(|v| v.size.width);\n        let reference_height = reference_box.map(|v| v.size.height);\n        let matrix = match *self {\n            Rotate3D(ax, ay, az, theta) => {\n                let theta = theta.radians64();\n                let (ax, ay, az, theta) =\n                    get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta);\n                Transform3D::rotation(\n                    ax as f64,\n                    ay as f64,\n                    az as f64,\n                    euclid::Angle::radians(theta),\n                )\n            },\n            RotateX(theta) => {\n                let theta = euclid::Angle::radians(theta.radians64());\n                Transform3D::rotation(1., 0., 0., theta)\n            },\n            RotateY(theta) => {\n                let theta = euclid::Angle::radians(theta.radians64());\n                Transform3D::rotation(0., 1., 0., theta)\n            },\n            RotateZ(theta) | Rotate(theta) => {\n                let theta = euclid::Angle::radians(theta.radians64());\n                Transform3D::rotation(0., 0., 1., theta)\n            },\n            Perspective(ref p) => {\n                let px = match p {\n                    PerspectiveFunction::None => f32::INFINITY,\n                    PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?,\n                };\n                create_perspective_matrix(px).cast()\n            },\n            Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()),\n            Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.),\n            ScaleX(s) => Transform3D::scale(s.into(), 1., 1.),\n            ScaleY(s) => Transform3D::scale(1., s.into(), 1.),\n            ScaleZ(s) => Transform3D::scale(1., 1., s.into()),\n            Translate3D(ref tx, ref ty, ref tz) => {\n                let tx = tx.to_pixel_length(reference_width)? as f64;\n                let ty = ty.to_pixel_length(reference_height)? as f64;\n                Transform3D::translation(tx, ty, tz.to_pixel_length(None)? as f64)\n            },\n            Translate(ref tx, ref ty) => {\n                let tx = tx.to_pixel_length(reference_width)? as f64;\n                let ty = ty.to_pixel_length(reference_height)? as f64;\n                Transform3D::translation(tx, ty, 0.)\n            },\n            TranslateX(ref t) => {\n                let t = t.to_pixel_length(reference_width)? as f64;\n                Transform3D::translation(t, 0., 0.)\n            },\n            TranslateY(ref t) => {\n                let t = t.to_pixel_length(reference_height)? as f64;\n                Transform3D::translation(0., t, 0.)\n            },\n            TranslateZ(ref z) => Transform3D::translation(0., 0., z.to_pixel_length(None)? as f64),\n            Skew(theta_x, theta_y) => Transform3D::skew(\n                euclid::Angle::radians(theta_x.radians64()),\n                euclid::Angle::radians(theta_y.radians64()),\n            ),\n            SkewX(theta) => Transform3D::skew(\n                euclid::Angle::radians(theta.radians64()),\n                euclid::Angle::radians(0.),\n            ),\n            SkewY(theta) => Transform3D::skew(\n                euclid::Angle::radians(0.),\n                euclid::Angle::radians(theta.radians64()),\n            ),\n            Matrix3D(m) => m.into(),\n            Matrix(m) => m.into(),\n            InterpolateMatrix { .. } | AccumulateMatrix { .. } => {\n                // TODO: Convert InterpolateMatrix/AccumulateMatrix into a valid Transform3D by\n                // the reference box and do interpolation on these two Transform3D matrices.\n                // Both Gecko and Servo don't support this for computing distance, and Servo\n                // doesn't support animations on InterpolateMatrix/AccumulateMatrix, so\n                // return an identity matrix.\n                // Note: DOMMatrix doesn't go into this arm.\n                Transform3D::identity()\n            },\n        };\n        Ok(matrix)\n    }\n}\n\nimpl<T> Transform<T> {\n    /// `none`\n    pub fn none() -> Self {\n        Transform(Default::default())\n    }\n}\n\nimpl<T: ToMatrix> Transform<T> {\n    /// Return the equivalent 3d matrix of this transform list.\n    ///\n    /// We return a pair: the first one is the transform matrix, and the second one\n    /// indicates if there is any 3d transform function in this transform list.\n    #[cfg_attr(rustfmt, rustfmt_skip)]\n    pub fn to_transform_3d_matrix(\n        &self,\n        reference_box: Option<&Rect<ComputedLength>>\n    ) -> Result<(Transform3D<CSSFloat>, bool), ()> {\n        Self::components_to_transform_3d_matrix(&self.0, reference_box)\n    }\n\n    /// Converts a series of components to a 3d matrix.\n    #[cfg_attr(rustfmt, rustfmt_skip)]\n    pub fn components_to_transform_3d_matrix(\n        ops: &[T],\n        reference_box: Option<&Rect<ComputedLength>>,\n    ) -> Result<(Transform3D<CSSFloat>, bool), ()> {\n        let cast_3d_transform = |m: Transform3D<f64>| -> Transform3D<CSSFloat> {\n            use std::{f32, f64};\n            let cast = |v: f64| v.min(f32::MAX as f64).max(f32::MIN as f64) as f32;\n            Transform3D::new(\n                cast(m.m11), cast(m.m12), cast(m.m13), cast(m.m14),\n                cast(m.m21), cast(m.m22), cast(m.m23), cast(m.m24),\n                cast(m.m31), cast(m.m32), cast(m.m33), cast(m.m34),\n                cast(m.m41), cast(m.m42), cast(m.m43), cast(m.m44),\n            )\n        };\n\n        let (m, is_3d) = Self::components_to_transform_3d_matrix_f64(ops, reference_box)?;\n        Ok((cast_3d_transform(m), is_3d))\n    }\n\n    /// Same as Transform::to_transform_3d_matrix but a f64 version.\n    pub fn to_transform_3d_matrix_f64(\n        &self,\n        reference_box: Option<&Rect<ComputedLength>>,\n    ) -> Result<(Transform3D<f64>, bool), ()> {\n        Self::components_to_transform_3d_matrix_f64(&self.0, reference_box)\n    }\n\n    /// Same as Transform::components_to_transform_3d_matrix but a f64 version.\n    fn components_to_transform_3d_matrix_f64(\n        ops: &[T],\n        reference_box: Option<&Rect<ComputedLength>>,\n    ) -> Result<(Transform3D<f64>, bool), ()> {\n        // We intentionally use Transform3D<f64> during computation to avoid\n        // error propagation because using f32 to compute triangle functions\n        // (e.g. in rotation()) is not accurate enough. In Gecko, we also use\n        // \"double\" to compute the triangle functions. Therefore, let's use\n        // Transform3D<f64> during matrix computation and cast it into f32 in\n        // the end.\n        let mut transform = Transform3D::<f64>::identity();\n        let mut contain_3d = false;\n\n        for operation in ops {\n            let matrix = operation.to_3d_matrix(reference_box)?;\n            contain_3d = contain_3d || operation.is_3d();\n            transform = matrix.then(&transform);\n        }\n\n        Ok((transform, contain_3d))\n    }\n}\n\n/// Return the transform matrix from a perspective length.\n#[inline]\npub fn create_perspective_matrix(d: CSSFloat) -> Transform3D<CSSFloat> {\n    if d.is_finite() {\n        Transform3D::perspective(d.max(1.))\n    } else {\n        Transform3D::identity()\n    }\n}\n\n/// Return the normalized direction vector and its angle for Rotate3D.\npub fn get_normalized_vector_and_angle<T: Zero>(\n    x: CSSFloat,\n    y: CSSFloat,\n    z: CSSFloat,\n    angle: T,\n) -> (CSSFloat, CSSFloat, CSSFloat, T) {\n    use crate::values::computed::transform::DirectionVector;\n    use euclid::approxeq::ApproxEq;\n    let vector = DirectionVector::new(x, y, z);\n    if vector.square_length().approx_eq(&f32::zero()) {\n        // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d\n        // A direction vector that cannot be normalized, such as [0, 0, 0], will cause the\n        // rotation to not be applied, so we use identity matrix (i.e. rotate3d(0, 0, 1, 0)).\n        (0., 0., 1., T::zero())\n    } else {\n        let vector = vector.robust_normalize();\n        (vector.x, vector.y, vector.z, angle)\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\n/// A value of the `Rotate` property\n///\n/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>\npub enum GenericRotate<Number, Angle> {\n    /// 'none'\n    None,\n    /// '<angle>'\n    Rotate(Angle),\n    /// '<number>{3} <angle>'\n    Rotate3D(Number, Number, Number, Angle),\n}\n\npub use self::GenericRotate as Rotate;\n\n/// A trait to check if the current 3D vector is parallel to the DirectionVector.\n/// This is especially for serialization on Rotate.\npub trait IsParallelTo {\n    /// Returns true if this is parallel to the vector.\n    fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;\n}\n\nimpl<Number, Angle> ToCss for Rotate<Number, Angle>\nwhere\n    Number: Copy + PartialOrd + ToCss + Zero,\n    Angle: Copy + Neg<Output = Angle> + ToCss + Zero,\n    (Number, Number, Number): IsParallelTo,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use crate::values::computed::transform::DirectionVector;\n        match *self {\n            Rotate::None => dest.write_str(\"none\"),\n            Rotate::Rotate(ref angle) => angle.to_css(dest),\n            Rotate::Rotate3D(x, y, z, angle) => {\n                // If the axis is parallel with the x or y axes, it must serialize as the\n                // appropriate keyword. If a rotation about the z axis (that is, in 2D) is\n                // specified, the property must serialize as just an <angle>.\n                //\n                // Note that if the axis is parallel to x/y/z but pointing in the opposite\n                // direction, we need to negate the angle to maintain the correct meaning.\n                //\n                // https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization\n                let v = (x, y, z);\n                let (axis, angle) = if x.is_zero() && y.is_zero() && z.is_zero() {\n                    // The zero length vector is parallel to every other vector, so\n                    // is_parallel_to() returns true for it. However, it is definitely different\n                    // from x axis, y axis, or z axis, and it's meaningless to perform a rotation\n                    // using that direction vector. So we *have* to serialize it using that same\n                    // vector - we can't simplify to some theoretically parallel axis-aligned\n                    // vector.\n                    (None, angle)\n                } else if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {\n                    (\n                        Some(\"x \"),\n                        if v.0 < Number::zero() { -angle } else { angle },\n                    )\n                } else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {\n                    (\n                        Some(\"y \"),\n                        if v.1 < Number::zero() { -angle } else { angle },\n                    )\n                } else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {\n                    // When we're parallel to the z-axis, we can just serialize the angle.\n                    let angle = if v.2 < Number::zero() { -angle } else { angle };\n                    return angle.to_css(dest);\n                } else {\n                    (None, angle)\n                };\n                match axis {\n                    Some(a) => dest.write_str(a)?,\n                    None => {\n                        x.to_css(dest)?;\n                        dest.write_char(' ')?;\n                        y.to_css(dest)?;\n                        dest.write_char(' ')?;\n                        z.to_css(dest)?;\n                        dest.write_char(' ')?;\n                    },\n                }\n                angle.to_css(dest)\n            },\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n/// A value of the `Scale` property\n///\n/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>\npub enum GenericScale<Number> {\n    /// 'none'\n    None,\n    /// '<number>{1,3}'\n    Scale(Number, Number, Number),\n}\n\npub use self::GenericScale as Scale;\n\nimpl<Number> ToCss for Scale<Number>\nwhere\n    Number: ToCss + PartialEq + Copy,\n    f32: From<Number>,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n        f32: From<Number>,\n    {\n        match *self {\n            Scale::None => dest.write_str(\"none\"),\n            Scale::Scale(ref x, ref y, ref z) => {\n                x.to_css(dest)?;\n\n                let is_3d = f32::from(*z) != 1.0;\n                if is_3d || x != y {\n                    dest.write_char(' ')?;\n                    y.to_css(dest)?;\n                }\n\n                if is_3d {\n                    dest.write_char(' ')?;\n                    z.to_css(dest)?;\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\n#[inline]\nfn y_axis_and_z_axis_are_zero<LengthPercentage: Zero + ZeroNoPercent, Length: Zero>(\n    _: &LengthPercentage,\n    y: &LengthPercentage,\n    z: &Length,\n) -> bool {\n    y.is_zero_no_percent() && z.is_zero()\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n/// A value of the `translate` property\n///\n/// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization:\n///\n/// If a 2d translation is specified, the property must serialize with only one\n/// or two values (per usual, if the second value is 0px, the default, it must\n/// be omitted when serializing; however if 0% is the second value, it is included).\n///\n/// If a 3d translation is specified and the value can be expressed as 2d, we treat as 2d and\n/// serialize accoringly. Otherwise, we serialize all three values.\n/// https://github.com/w3c/csswg-drafts/issues/3305\n///\n/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>\npub enum GenericTranslate<LengthPercentage, Length>\nwhere\n    LengthPercentage: Zero + ZeroNoPercent,\n    Length: Zero,\n{\n    /// 'none'\n    None,\n    /// <length-percentage> [ <length-percentage> <length>? ]?\n    Translate(\n        LengthPercentage,\n        #[css(contextual_skip_if = \"y_axis_and_z_axis_are_zero\")] LengthPercentage,\n        #[css(skip_if = \"Zero::is_zero\")] Length,\n    ),\n}\n\npub use self::GenericTranslate as Translate;\n\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum TransformStyle {\n    Flat,\n    #[css(keyword = \"preserve-3d\")]\n    Preserve3d,\n}\n"
  },
  {
    "path": "style/values/generics/ui.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic values for UI properties.\n\nuse crate::derives::*;\nuse crate::values::specified::ui::CursorKind;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ToCss};\n\n/// A generic value for the `cursor` property.\n///\n/// https://drafts.csswg.org/css-ui/#cursor\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct GenericCursor<Image> {\n    /// The parsed images for the cursor.\n    pub images: crate::OwnedSlice<Image>,\n    /// The kind of the cursor [default | help | ...].\n    pub keyword: CursorKind,\n}\n\npub use self::GenericCursor as Cursor;\n\nimpl<Image> Cursor<Image> {\n    /// Set `cursor` to `auto`\n    #[inline]\n    pub fn auto() -> Self {\n        Self {\n            images: Default::default(),\n            keyword: CursorKind::Auto,\n        }\n    }\n}\n\nimpl<Image: ToCss> ToCss for Cursor<Image> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        for image in &*self.images {\n            image.to_css(dest)?;\n            dest.write_str(\", \")?;\n        }\n        self.keyword.to_css(dest)\n    }\n}\n\n/// A generic value for item of `image cursors`.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem)]\n#[repr(C)]\npub struct GenericCursorImage<Image, Number> {\n    /// The url to parse images from.\n    pub image: Image,\n    /// Whether the image has a hotspot or not.\n    pub has_hotspot: bool,\n    /// The x coordinate.\n    pub hotspot_x: Number,\n    /// The y coordinate.\n    pub hotspot_y: Number,\n}\n\npub use self::GenericCursorImage as CursorImage;\n\nimpl<Image: ToCss, Number: ToCss> ToCss for CursorImage<Image, Number> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.image.to_css(dest)?;\n        if self.has_hotspot {\n            dest.write_char(' ')?;\n            self.hotspot_x.to_css(dest)?;\n            dest.write_char(' ')?;\n            self.hotspot_y.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// A generic value for `scrollbar-color` property.\n///\n/// https://drafts.csswg.org/css-scrollbars-1/#scrollbar-color\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\npub enum GenericScrollbarColor<Color> {\n    /// `auto`\n    Auto,\n    /// `<color>{2}`\n    Colors {\n        /// First `<color>`, for color of the scrollbar thumb.\n        thumb: Color,\n        /// Second `<color>`, for color of the scrollbar track.\n        track: Color,\n    },\n}\n\npub use self::GenericScrollbarColor as ScrollbarColor;\n\nimpl<Color> Default for ScrollbarColor<Color> {\n    #[inline]\n    fn default() -> Self {\n        ScrollbarColor::Auto\n    }\n}\n"
  },
  {
    "path": "style/values/generics/url.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Generic types for url properties.\n\nuse crate::derives::*;\n\n/// An image url or none, used for example in list-style-image\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum GenericUrlOrNone<U> {\n    /// `none`\n    None,\n    /// A URL.\n    Url(U),\n}\n\npub use self::GenericUrlOrNone as UrlOrNone;\n\nimpl<Url> UrlOrNone<Url> {\n    /// Initial \"none\" value for properties such as `list-style-image`\n    pub fn none() -> Self {\n        UrlOrNone::None\n    }\n\n    /// Returns whether the value is `none`.\n    pub fn is_none(&self) -> bool {\n        match *self {\n            UrlOrNone::None => true,\n            UrlOrNone::Url(..) => false,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common [values][values] used in CSS.\n//!\n//! [values]: https://drafts.csswg.org/css-values/\n\n#![deny(missing_docs)]\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::position::IsTreeScoped;\nuse crate::Atom;\npub use cssparser::{serialize_identifier, serialize_name, CowRcStr, Parser};\npub use cssparser::{SourceLocation, Token};\nuse precomputed_hash::PrecomputedHash;\nuse selectors::parser::SelectorParseErrorKind;\nuse std::fmt::{self, Debug, Write};\nuse style_traits::{\n    CssString, CssWriter, KeywordValue, MathSum, NumericValue, ParseError, StyleParseErrorKind,\n    ToCss, ToTyped, TypedValue, UnitValue,\n};\nuse thin_vec::ThinVec;\nuse to_shmem::impl_trivial_to_shmem;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::url::CssUrl;\n#[cfg(feature = \"servo\")]\npub use crate::servo::url::CssUrl;\n\npub mod animated;\npub mod computed;\npub mod distance;\npub mod generics;\npub mod resolved;\npub mod specified;\n\n/// A CSS float value.\npub type CSSFloat = f32;\n\n/// Normalizes a float value to zero after a set of operations that might turn\n/// it into NaN.\n#[inline]\npub fn normalize(v: CSSFloat) -> CSSFloat {\n    if v.is_nan() {\n        0.0\n    } else {\n        v\n    }\n}\n\n/// A CSS integer value.\npub type CSSInteger = i32;\n\n/// Serialize an identifier which is represented as an atom.\n#[cfg(feature = \"gecko\")]\npub fn serialize_atom_identifier<W>(ident: &Atom, dest: &mut W) -> fmt::Result\nwhere\n    W: Write,\n{\n    ident.with_str(|s| serialize_identifier(s, dest))\n}\n\n/// Serialize an identifier which is represented as an atom.\n#[cfg(feature = \"servo\")]\npub fn serialize_atom_identifier<Static, W>(\n    ident: &::string_cache::Atom<Static>,\n    dest: &mut W,\n) -> fmt::Result\nwhere\n    Static: string_cache::StaticAtomSet,\n    W: Write,\n{\n    serialize_identifier(&ident, dest)\n}\n\n/// Serialize a name which is represented as an Atom.\n#[cfg(feature = \"gecko\")]\npub fn serialize_atom_name<W>(ident: &Atom, dest: &mut W) -> fmt::Result\nwhere\n    W: Write,\n{\n    ident.with_str(|s| serialize_name(s, dest))\n}\n\n/// Serialize a name which is represented as an Atom.\n#[cfg(feature = \"servo\")]\npub fn serialize_atom_name<Static, W>(\n    ident: &::string_cache::Atom<Static>,\n    dest: &mut W,\n) -> fmt::Result\nwhere\n    Static: string_cache::StaticAtomSet,\n    W: Write,\n{\n    serialize_name(&ident, dest)\n}\n\n/// Serialize a number with calc, and NaN/infinity handling (if enabled)\npub fn serialize_number<W>(v: f32, was_calc: bool, dest: &mut CssWriter<W>) -> fmt::Result\nwhere\n    W: Write,\n{\n    serialize_specified_dimension(v, \"\", was_calc, dest)\n}\n\n/// Reify a number with calc.\npub fn reify_number(v: f32, was_calc: bool, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n    let numeric_value = NumericValue::Unit(UnitValue {\n        value: v,\n        unit: CssString::from(\"number\"),\n    });\n\n    // https://drafts.css-houdini.org/css-typed-om-1/#reify-a-math-expression\n    if was_calc {\n        dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {\n            values: ThinVec::from([numeric_value]),\n        })));\n    } else {\n        dest.push(TypedValue::Numeric(numeric_value));\n    }\n\n    Ok(())\n}\n\n/// Serialize a specified dimension with unit, calc, and NaN/infinity handling (if enabled)\npub fn serialize_specified_dimension<W>(\n    v: f32,\n    unit: &str,\n    was_calc: bool,\n    dest: &mut CssWriter<W>,\n) -> fmt::Result\nwhere\n    W: Write,\n{\n    if was_calc {\n        dest.write_str(\"calc(\")?;\n    }\n\n    if !v.is_finite() {\n        // https://drafts.csswg.org/css-values/#calc-error-constants:\n        // \"While not technically numbers, these keywords act as numeric values,\n        // similar to e and pi. Thus to get an infinite length, for example,\n        // requires an expression like calc(infinity * 1px).\"\n\n        if v.is_nan() {\n            dest.write_str(\"NaN\")?;\n        } else if v == f32::INFINITY {\n            dest.write_str(\"infinity\")?;\n        } else if v == f32::NEG_INFINITY {\n            dest.write_str(\"-infinity\")?;\n        }\n\n        if !unit.is_empty() {\n            dest.write_str(\" * 1\")?;\n        }\n    } else {\n        v.to_css(dest)?;\n    }\n\n    dest.write_str(unit)?;\n\n    if was_calc {\n        dest.write_char(')')?;\n    }\n    Ok(())\n}\n\n/// A CSS string stored as an `Atom`.\n#[repr(transparent)]\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    Deref,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct AtomString(pub Atom);\n\n#[cfg(feature = \"servo\")]\nimpl AsRef<str> for AtomString {\n    fn as_ref(&self) -> &str {\n        &*self.0\n    }\n}\n\nimpl Parse for AtomString {\n    fn parse<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {\n        Ok(Self(Atom::from(input.expect_string()?.as_ref())))\n    }\n}\n\nimpl cssparser::ToCss for AtomString {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: Write,\n    {\n        // Wrap in quotes to form a string literal\n        dest.write_char('\"')?;\n        #[cfg(feature = \"servo\")]\n        {\n            cssparser::CssStringWriter::new(dest).write_str(self.as_ref())?;\n        }\n        #[cfg(feature = \"gecko\")]\n        {\n            self.0\n                .with_str(|s| cssparser::CssStringWriter::new(dest).write_str(s))?;\n        }\n        dest.write_char('\"')\n    }\n}\n\nimpl style_traits::ToCss for AtomString {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        cssparser::ToCss::to_css(self, dest)\n    }\n}\n\nimpl PrecomputedHash for AtomString {\n    #[inline]\n    fn precomputed_hash(&self) -> u32 {\n        self.0.precomputed_hash()\n    }\n}\n\nimpl<'a> From<&'a str> for AtomString {\n    #[inline]\n    fn from(string: &str) -> Self {\n        Self(Atom::from(string))\n    }\n}\n\n/// A generic CSS `<ident>` stored as an `Atom`.\n#[cfg(feature = \"servo\")]\n#[repr(transparent)]\n#[derive(Deref)]\npub struct GenericAtomIdent<Set>(pub string_cache::Atom<Set>)\nwhere\n    Set: string_cache::StaticAtomSet;\n\n/// A generic CSS `<ident>` stored as an `Atom`, for the default atom set.\n#[cfg(feature = \"servo\")]\npub type AtomIdent = GenericAtomIdent<stylo_atoms::AtomStaticSet>;\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> style_traits::SpecifiedValueInfo for GenericAtomIdent<Set> {}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> Default for GenericAtomIdent<Set> {\n    fn default() -> Self {\n        Self(string_cache::Atom::default())\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> std::fmt::Debug for GenericAtomIdent<Set> {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> std::hash::Hash for GenericAtomIdent<Set> {\n    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {\n        self.0.hash(state)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> Eq for GenericAtomIdent<Set> {}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> PartialEq for GenericAtomIdent<Set> {\n    fn eq(&self, other: &Self) -> bool {\n        self.0 == other.0\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> Clone for GenericAtomIdent<Set> {\n    fn clone(&self) -> Self {\n        Self(self.0.clone())\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> to_shmem::ToShmem for GenericAtomIdent<Set> {\n    fn to_shmem(&self, builder: &mut to_shmem::SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        use std::mem::ManuallyDrop;\n\n        let atom = self.0.to_shmem(builder)?;\n        Ok(ManuallyDrop::new(Self(ManuallyDrop::into_inner(atom))))\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> malloc_size_of::MallocSizeOf for GenericAtomIdent<Set> {\n    fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {\n        self.0.size_of(ops)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> cssparser::ToCss for GenericAtomIdent<Set> {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> style_traits::ToCss for GenericAtomIdent<Set> {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> PrecomputedHash for GenericAtomIdent<Set> {\n    #[inline]\n    fn precomputed_hash(&self) -> u32 {\n        self.0.precomputed_hash()\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<'a, Set: string_cache::StaticAtomSet> From<&'a str> for GenericAtomIdent<Set> {\n    #[inline]\n    fn from(string: &str) -> Self {\n        Self(string_cache::Atom::from(string))\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> std::borrow::Borrow<string_cache::Atom<Set>>\n    for GenericAtomIdent<Set>\n{\n    #[inline]\n    fn borrow(&self) -> &string_cache::Atom<Set> {\n        &self.0\n    }\n}\n\n#[cfg(feature = \"servo\")]\nimpl<Set: string_cache::StaticAtomSet> GenericAtomIdent<Set> {\n    /// Constructs a new GenericAtomIdent.\n    #[inline]\n    pub fn new(atom: string_cache::Atom<Set>) -> Self {\n        Self(atom)\n    }\n\n    /// Cast an atom ref to an AtomIdent ref.\n    #[inline]\n    pub fn cast<'a>(atom: &'a string_cache::Atom<Set>) -> &'a Self {\n        let ptr = atom as *const _ as *const Self;\n        // safety: repr(transparent)\n        unsafe { &*ptr }\n    }\n}\n\n/// A CSS `<ident>` stored as an `Atom`.\n#[cfg(feature = \"gecko\")]\n#[repr(transparent)]\n#[derive(\n    Clone, Debug, Default, Deref, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem,\n)]\npub struct AtomIdent(pub Atom);\n\n#[cfg(feature = \"gecko\")]\nimpl cssparser::ToCss for AtomIdent {\n    fn to_css<W>(&self, dest: &mut W) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl style_traits::ToCss for AtomIdent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        cssparser::ToCss::to_css(self, dest)\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl PrecomputedHash for AtomIdent {\n    #[inline]\n    fn precomputed_hash(&self) -> u32 {\n        self.0.precomputed_hash()\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl<'a> From<&'a str> for AtomIdent {\n    #[inline]\n    fn from(string: &str) -> Self {\n        Self(Atom::from(string))\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl AtomIdent {\n    /// Constructs a new AtomIdent.\n    #[inline]\n    pub fn new(atom: Atom) -> Self {\n        Self(atom)\n    }\n\n    /// Like `Atom::with` but for `AtomIdent`.\n    pub unsafe fn with<F, R>(ptr: *const crate::gecko_bindings::structs::nsAtom, callback: F) -> R\n    where\n        F: FnOnce(&Self) -> R,\n    {\n        Atom::with(ptr, |atom: &Atom| {\n            // safety: repr(transparent)\n            let atom = atom as *const Atom as *const AtomIdent;\n            callback(&*atom)\n        })\n    }\n\n    /// Cast an atom ref to an AtomIdent ref.\n    #[inline]\n    pub fn cast<'a>(atom: &'a Atom) -> &'a Self {\n        let ptr = atom as *const _ as *const Self;\n        // safety: repr(transparent)\n        unsafe { &*ptr }\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl std::borrow::Borrow<crate::gecko_string_cache::WeakAtom> for AtomIdent {\n    #[inline]\n    fn borrow(&self) -> &crate::gecko_string_cache::WeakAtom {\n        self.0.borrow()\n    }\n}\n\n/// Serialize a value into percentage.\npub fn serialize_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result\nwhere\n    W: Write,\n{\n    serialize_specified_dimension(value * 100., \"%\", /* was_calc = */ false, dest)\n}\n\n/// Serialize a value into normalized (no NaN/inf serialization) percentage.\npub fn serialize_normalized_percentage<W>(value: CSSFloat, dest: &mut CssWriter<W>) -> fmt::Result\nwhere\n    W: Write,\n{\n    (value * 100.).to_css(dest)?;\n    dest.write_char('%')\n}\n\n/// Reify a percentage with calc.\npub fn reify_percentage(\n    value: CSSFloat,\n    was_calc: bool,\n    dest: &mut ThinVec<TypedValue>,\n) -> Result<(), ()> {\n    let numeric_value = NumericValue::Unit(UnitValue {\n        value: value * 100.,\n        unit: CssString::from(\"percent\"),\n    });\n\n    // https://drafts.css-houdini.org/css-typed-om-1/#reify-a-math-expression\n    if was_calc {\n        dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {\n            values: ThinVec::from([numeric_value]),\n        })));\n    } else {\n        dest.push(TypedValue::Numeric(numeric_value));\n    }\n\n    Ok(())\n}\n\n/// Convenience void type to disable some properties and values through types.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, MallocSizeOf, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n)]\npub enum Impossible {}\n\n// FIXME(nox): This should be derived but the derive code cannot cope\n// with uninhabited enums.\nimpl ComputeSquaredDistance for Impossible {\n    #[inline]\n    fn compute_squared_distance(&self, _other: &Self) -> Result<SquaredDistance, ()> {\n        match *self {}\n    }\n}\n\nimpl_trivial_to_shmem!(Impossible);\n\nimpl Parse for Impossible {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n/// A struct representing one of two kinds of values.\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\npub enum Either<A, B> {\n    /// The first value.\n    First(A),\n    /// The second kind of value.\n    Second(B),\n}\n\nimpl<A: Debug, B: Debug> Debug for Either<A, B> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match *self {\n            Either::First(ref v) => v.fmt(f),\n            Either::Second(ref v) => v.fmt(f),\n        }\n    }\n}\n\n/// <https://drafts.csswg.org/css-values-4/#custom-idents>\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct CustomIdent(pub Atom);\n\nimpl CustomIdent {\n    /// Parse a <custom-ident>\n    ///\n    /// TODO(zrhoffman, bug 1844501): Use CustomIdent::parse in more places instead of\n    /// CustomIdent::from_ident.\n    pub fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        invalid: &[&str],\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n        CustomIdent::from_ident(location, ident, invalid)\n    }\n\n    /// Parse an already-tokenizer identifier\n    pub fn from_ident<'i>(\n        location: SourceLocation,\n        ident: &CowRcStr<'i>,\n        excluding: &[&str],\n    ) -> Result<Self, ParseError<'i>> {\n        if !Self::is_valid(ident, excluding) {\n            return Err(\n                location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))\n            );\n        }\n        if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {\n            Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        } else {\n            Ok(CustomIdent(Atom::from(ident.as_ref())))\n        }\n    }\n\n    fn is_valid(ident: &str, excluding: &[&str]) -> bool {\n        use crate::properties::CSSWideKeyword;\n        // https://drafts.csswg.org/css-values-4/#custom-idents:\n        //\n        //     The CSS-wide keywords are not valid <custom-ident>s. The default\n        //     keyword is reserved and is also not a valid <custom-ident>.\n        if CSSWideKeyword::from_ident(ident).is_ok() || ident.eq_ignore_ascii_case(\"default\") {\n            return false;\n        }\n\n        // https://drafts.csswg.org/css-values-4/#custom-idents:\n        //\n        //     Excluded keywords are excluded in all ASCII case permutations.\n        !excluding.iter().any(|s| ident.eq_ignore_ascii_case(s))\n    }\n}\n\nimpl ToCss for CustomIdent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\nimpl ToTyped for CustomIdent {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        // This shouldn't escape identifiers. See bug 2023533.\n        let s = ToCss::to_css_cssstring(self);\n        dest.push(TypedValue::Keyword(KeywordValue(s)));\n        Ok(())\n    }\n}\n\n/// <https://www.w3.org/TR/css-values-4/#dashed-idents>\n/// This is simply an Atom, but will only parse if the identifier starts with \"--\".\n#[repr(transparent)]\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    Serialize,\n    Deserialize,\n)]\npub struct DashedIdent(pub Atom);\n\nimpl DashedIdent {\n    /// Parse an already-tokenizer identifier\n    pub fn from_ident<'i>(\n        location: SourceLocation,\n        ident: &CowRcStr<'i>,\n    ) -> Result<Self, ParseError<'i>> {\n        if !ident.starts_with(\"--\") {\n            return Err(\n                location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))\n            );\n        }\n        Ok(Self(Atom::from(ident.as_ref())))\n    }\n\n    /// Special value for internal use. Useful where we can't use Option<>.\n    pub fn empty() -> Self {\n        Self(atom!(\"\"))\n    }\n\n    /// Check for special internal value.\n    pub fn is_empty(&self) -> bool {\n        self.0 == atom!(\"\")\n    }\n\n    /// Returns an atom with the same value, but without the starting \"--\".\n    ///\n    /// # Panics\n    ///\n    /// Panics when used on the special `DashedIdent::empty()`.\n    pub(crate) fn undashed(&self) -> Atom {\n        assert!(!self.is_empty(), \"Can't undash the empty DashedIdent\");\n        #[cfg(feature = \"gecko\")]\n        let name = &self.0.as_slice()[2..];\n        #[cfg(feature = \"servo\")]\n        let name = &self.0[2..];\n        Atom::from(name)\n    }\n}\n\nimpl IsTreeScoped for DashedIdent {\n    fn is_tree_scoped(&self) -> bool {\n        !self.is_empty()\n    }\n}\n\nimpl Parse for DashedIdent {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n        Self::from_ident(location, ident)\n    }\n}\n\nimpl ToCss for DashedIdent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n/// The <keyframes-name>.\n///\n/// <https://drafts.csswg.org/css-animations/#typedef-keyframes-name>\n///\n/// We use a single atom for this. Empty atom represents `none` animation.\n#[repr(transparent)]\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    PartialEq,\n    MallocSizeOf,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct KeyframesName(Atom);\n\nimpl KeyframesName {\n    /// <https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name>\n    pub fn from_ident(value: &str) -> Self {\n        Self(Atom::from(value))\n    }\n\n    /// Returns the `none` value.\n    pub fn none() -> Self {\n        Self(atom!(\"\"))\n    }\n\n    /// Returns whether this is the special `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0 == atom!(\"\")\n    }\n\n    /// Create a new KeyframesName from Atom.\n    #[cfg(feature = \"gecko\")]\n    pub fn from_atom(atom: Atom) -> Self {\n        Self(atom)\n    }\n\n    /// The name as an Atom\n    pub fn as_atom(&self) -> &Atom {\n        &self.0\n    }\n}\n\nimpl Parse for KeyframesName {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        Ok(match *input.next()? {\n            Token::Ident(ref s) => Self(CustomIdent::from_ident(location, s, &[\"none\"])?.0),\n            // Note that empty <string> should be rejected.\n            Token::QuotedString(ref s) if !s.as_ref().is_empty() => Self(Atom::from(s.as_ref())),\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        })\n    }\n}\n\nimpl ToCss for KeyframesName {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_none() {\n            return dest.write_str(\"none\");\n        }\n\n        fn serialize<W: Write>(string: &str, dest: &mut CssWriter<W>) -> fmt::Result {\n            if CustomIdent::is_valid(string, &[\"none\"]) {\n                serialize_identifier(string, dest)\n            } else {\n                string.to_css(dest)\n            }\n        }\n\n        #[cfg(feature = \"gecko\")]\n        return self.0.with_str(|s| serialize(s, dest));\n\n        #[cfg(feature = \"servo\")]\n        return serialize(self.0.as_ref(), dest);\n    }\n}\n\nimpl ToTyped for KeyframesName {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        let s = ToCss::to_css_cssstring(self);\n        dest.push(TypedValue::Keyword(KeywordValue(s)));\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "style/values/resolved/animation.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolved animation values.\n\nuse super::{Context, ToResolvedValue};\n\nuse crate::values::computed::time::Time;\nuse crate::values::computed::AnimationDuration;\n\nimpl ToResolvedValue for AnimationDuration {\n    type ResolvedValue = Self;\n\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        match self {\n            // For backwards-compatibility with Level 1, when the computed value of\n            // animation-timeline is auto (i.e. only one list value, and that value being auto),\n            // the resolved value of auto for animation-duration is 0s whenever its used value\n            // would also be 0s.\n            // https://drafts.csswg.org/css-animations-2/#animation-duration\n            Self::Auto if context.style.get_ui().has_initial_animation_timeline() => {\n                Self::Time(Time::from_seconds(0.0f32))\n            },\n            _ => self,\n        }\n    }\n\n    #[inline]\n    fn from_resolved_value(value: Self::ResolvedValue) -> Self {\n        value\n    }\n}\n"
  },
  {
    "path": "style/values/resolved/color.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolved color values.\n\nuse super::{Context, ToResolvedValue};\n\nuse crate::properties::{PropertyId, ShorthandId};\nuse crate::values::computed::color as computed;\nuse crate::values::generics::color as generics;\n\nimpl ToResolvedValue for computed::Color {\n    // A resolved color value is (almost always) a rgba color, with currentcolor resolved.\n    type ResolvedValue = Self;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        if context.for_property == PropertyId::NonCustom(ShorthandId::TextDecoration.into())\n            && matches!(self, Self::CurrentColor)\n        {\n            return self;\n        }\n        generics::Color::Absolute(context.style.resolve_color(&self))\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        resolved\n    }\n}\n\nimpl ToResolvedValue for computed::CaretColor {\n    // A resolved caret-color value is an rgba color, with auto resolving to\n    // currentcolor.\n    type ResolvedValue = computed::Color;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        let color = match self.0 {\n            generics::ColorOrAuto::Color(color) => color,\n            generics::ColorOrAuto::Auto => generics::Color::currentcolor(),\n        };\n        color.to_resolved_value(context)\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        generics::CaretColor(generics::ColorOrAuto::Color(\n            computed::Color::from_resolved_value(resolved),\n        ))\n    }\n}\n"
  },
  {
    "path": "style/values/resolved/counters.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolved values for counter properties\n\nuse super::{Context, ToResolvedValue};\nuse crate::values::computed;\n#[cfg(feature = \"gecko\")]\nuse selectors::parser::PseudoElement;\n\n/// https://drafts.csswg.org/css-content/#content-property\n///\n/// We implement this at resolved value time because otherwise it causes us to allocate a bunch of\n/// useless initial structs for all ::before / ::after, which is a bit unfortunate.\n///\n/// Though these should be temporary, mostly, so if this causes complexity in\n/// other places, it should be fine to move to `StyleAdjuster`.\n///\n/// See https://github.com/w3c/csswg-drafts/issues/4632 for where some related\n/// issues are being discussed.\nimpl ToResolvedValue for computed::Content {\n    type ResolvedValue = Self;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self {\n        let (is_before_or_after, is_marker) = match context.style.pseudo() {\n            Some(ref pseudo) => (pseudo.is_before_or_after(), pseudo.is_marker()),\n            None => (false, false),\n        };\n        match self {\n            Self::Normal if is_before_or_after => Self::None,\n            // For now, make `content: none` resolve to `normal` for pseudos\n            // other than ::before, ::after and ::marker, as we don't respect it.\n            // https://github.com/w3c/csswg-drafts/issues/6124\n            Self::None if !is_before_or_after && !is_marker => Self::Normal,\n            other => other,\n        }\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self) -> Self {\n        resolved\n    }\n}\n"
  },
  {
    "path": "style/values/resolved/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolved values. These are almost always computed values, but in some cases\n//! there are used values.\n\n#[cfg(feature = \"gecko\")]\nuse crate::device::Device;\nuse crate::properties::{ComputedValues, LonghandId, PropertyId};\nuse crate::ArcSlice;\nuse app_units::Au;\nuse servo_arc::Arc;\nuse smallvec::SmallVec;\n\nmod animation;\nmod color;\nmod counters;\n\nuse crate::values::computed::{self, Length};\n\n/// Element-specific information needed to resolve property values.\n#[cfg(feature = \"gecko\")]\npub struct ResolvedElementInfo<'a> {\n    /// Element we're resolving line-height against.\n    pub element: crate::gecko::wrapper::GeckoElement<'a>,\n}\n\n/// Information needed to resolve a given value.\npub struct Context<'a> {\n    /// The style we're resolving for. This is useful to resolve currentColor.\n    pub style: &'a ComputedValues,\n    /// The device / document we're resolving style for. Useful to do font metrics stuff needed for\n    /// line-height.\n    #[cfg(feature = \"gecko\")]\n    pub device: &'a Device,\n    /// The element-specific information to resolve the value.\n    #[cfg(feature = \"gecko\")]\n    pub element_info: ResolvedElementInfo<'a>,\n    /// The property we're resolving the value for over-all (might be a shorthand, or a custom property).\n    pub for_property: PropertyId,\n    /// The current (physical) longhand we're resolving the value for. Guaranteed to be `Some()`\n    /// inside `ToResolvedValue` implementations.\n    pub current_longhand: Option<LonghandId>,\n}\n\n/// A trait to represent the conversion between resolved and resolved values.\n///\n/// This trait is derivable with `#[derive(ToResolvedValue)]`.\n///\n/// The deriving code assumes that if the type isn't generic, then the trait can\n/// be implemented as simple move. This means that a manual implementation with\n/// `ResolvedValue = Self` is bogus if it returns anything else than a clone.\npub trait ToResolvedValue {\n    /// The resolved value type we're going to be converted to.\n    type ResolvedValue;\n\n    /// Convert a resolved value to a resolved value.\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue;\n\n    /// Convert a resolved value to resolved value form.\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self;\n}\n\nmacro_rules! trivial_to_resolved_value {\n    ($ty:ty) => {\n        impl $crate::values::resolved::ToResolvedValue for $ty {\n            type ResolvedValue = Self;\n\n            #[inline]\n            fn to_resolved_value(self, _: &Context) -> Self {\n                self\n            }\n\n            #[inline]\n            fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n                resolved\n            }\n        }\n    };\n}\n\ntrivial_to_resolved_value!(());\ntrivial_to_resolved_value!(bool);\ntrivial_to_resolved_value!(f32);\ntrivial_to_resolved_value!(u8);\ntrivial_to_resolved_value!(i8);\ntrivial_to_resolved_value!(u16);\ntrivial_to_resolved_value!(i16);\ntrivial_to_resolved_value!(u32);\ntrivial_to_resolved_value!(i32);\ntrivial_to_resolved_value!(usize);\ntrivial_to_resolved_value!(String);\ntrivial_to_resolved_value!(Box<str>);\ntrivial_to_resolved_value!(crate::OwnedStr);\ntrivial_to_resolved_value!(crate::color::AbsoluteColor);\ntrivial_to_resolved_value!(crate::values::generics::color::ColorMixFlags);\ntrivial_to_resolved_value!(crate::Atom);\ntrivial_to_resolved_value!(crate::values::AtomIdent);\ntrivial_to_resolved_value!(crate::custom_properties::VariableValue);\ntrivial_to_resolved_value!(crate::stylesheets::UrlExtraData);\ntrivial_to_resolved_value!(computed::url::ComputedUrl);\n#[cfg(feature = \"servo\")]\ntrivial_to_resolved_value!(crate::Namespace);\n#[cfg(feature = \"servo\")]\ntrivial_to_resolved_value!(crate::Prefix);\ntrivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType);\ntrivial_to_resolved_value!(computed::TimingFunction);\n\nimpl ToResolvedValue for Au {\n    type ResolvedValue = Length;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        Length::new(self.to_f32_px()).to_resolved_value(context)\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        Au::from_f32_px(Length::from_resolved_value(resolved).px())\n    }\n}\n\nimpl<A, B> ToResolvedValue for (A, B)\nwhere\n    A: ToResolvedValue,\n    B: ToResolvedValue,\n{\n    type ResolvedValue = (\n        <A as ToResolvedValue>::ResolvedValue,\n        <B as ToResolvedValue>::ResolvedValue,\n    );\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        (\n            self.0.to_resolved_value(context),\n            self.1.to_resolved_value(context),\n        )\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        (\n            A::from_resolved_value(resolved.0),\n            B::from_resolved_value(resolved.1),\n        )\n    }\n}\n\nimpl<T> ToResolvedValue for Option<T>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = Option<<T as ToResolvedValue>::ResolvedValue>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        self.map(|item| item.to_resolved_value(context))\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        resolved.map(T::from_resolved_value)\n    }\n}\n\nimpl<T> ToResolvedValue for SmallVec<[T; 1]>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = SmallVec<[<T as ToResolvedValue>::ResolvedValue; 1]>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        self.into_iter()\n            .map(|item| item.to_resolved_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        resolved.into_iter().map(T::from_resolved_value).collect()\n    }\n}\n\nimpl<T> ToResolvedValue for Vec<T>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = Vec<<T as ToResolvedValue>::ResolvedValue>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        self.into_iter()\n            .map(|item| item.to_resolved_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        resolved.into_iter().map(T::from_resolved_value).collect()\n    }\n}\n\nimpl<T> ToResolvedValue for thin_vec::ThinVec<T>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = thin_vec::ThinVec<<T as ToResolvedValue>::ResolvedValue>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        self.into_iter()\n            .map(|item| item.to_resolved_value(context))\n            .collect()\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        resolved.into_iter().map(T::from_resolved_value).collect()\n    }\n}\n\nimpl<T> ToResolvedValue for Box<T>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = Box<<T as ToResolvedValue>::ResolvedValue>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        Box::new(T::to_resolved_value(*self, context))\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        Box::new(T::from_resolved_value(*resolved))\n    }\n}\n\nimpl<T> ToResolvedValue for Box<[T]>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = Box<[<T as ToResolvedValue>::ResolvedValue]>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        Vec::from(self)\n            .to_resolved_value(context)\n            .into_boxed_slice()\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        Vec::from_resolved_value(Vec::from(resolved)).into_boxed_slice()\n    }\n}\n\nimpl<T> ToResolvedValue for crate::OwnedSlice<T>\nwhere\n    T: ToResolvedValue,\n{\n    type ResolvedValue = crate::OwnedSlice<<T as ToResolvedValue>::ResolvedValue>;\n\n    #[inline]\n    fn to_resolved_value(self, context: &Context) -> Self::ResolvedValue {\n        self.into_box().to_resolved_value(context).into()\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self::ResolvedValue) -> Self {\n        Self::from(Box::from_resolved_value(resolved.into_box()))\n    }\n}\n\n// NOTE(emilio): This is implementable more generically, but it's unlikely what\n// you want there, as it forces you to have an extra allocation.\n//\n// We could do that if needed, ideally with specialization for the case where\n// ResolvedValue = T. But we don't need it for now.\nimpl<T> ToResolvedValue for Arc<T>\nwhere\n    T: ToResolvedValue<ResolvedValue = T>,\n{\n    type ResolvedValue = Self;\n\n    #[inline]\n    fn to_resolved_value(self, _: &Context) -> Self {\n        self\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self) -> Self {\n        resolved\n    }\n}\n\n// Same caveat as above applies.\nimpl<T> ToResolvedValue for ArcSlice<T>\nwhere\n    T: ToResolvedValue<ResolvedValue = T>,\n{\n    type ResolvedValue = Self;\n\n    #[inline]\n    fn to_resolved_value(self, _: &Context) -> Self {\n        self\n    }\n\n    #[inline]\n    fn from_resolved_value(resolved: Self) -> Self {\n        resolved\n    }\n}\n"
  },
  {
    "path": "style/values/specified/align.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Values for CSS Box Alignment properties\n//!\n//! https://drafts.csswg.org/css-align/\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, ToCss};\n\n/// Constants shared by multiple CSS Box Alignment properties\n#[derive(\n    Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct AlignFlags(u8);\nbitflags! {\n    impl AlignFlags: u8 {\n        // Enumeration stored in the lower 5 bits:\n        /// {align,justify}-{content,items,self}: 'auto'\n        const AUTO = 0;\n        /// 'normal'\n        const NORMAL = 1;\n        /// 'start'\n        const START = 2;\n        /// 'end'\n        const END = 3;\n        /// 'flex-start'\n        const FLEX_START = 4;\n        /// 'flex-end'\n        const FLEX_END = 5;\n        /// 'center'\n        const CENTER = 6;\n        /// 'left'\n        const LEFT = 7;\n        /// 'right'\n        const RIGHT = 8;\n        /// 'baseline'\n        const BASELINE = 9;\n        /// 'last-baseline'\n        const LAST_BASELINE = 10;\n        /// 'stretch'\n        const STRETCH = 11;\n        /// 'self-start'\n        const SELF_START = 12;\n        /// 'self-end'\n        const SELF_END = 13;\n        /// 'space-between'\n        const SPACE_BETWEEN = 14;\n        /// 'space-around'\n        const SPACE_AROUND = 15;\n        /// 'space-evenly'\n        const SPACE_EVENLY = 16;\n        /// `anchor-center`\n        const ANCHOR_CENTER = 17;\n\n        // Additional flags stored in the upper bits:\n        /// 'legacy' (mutually exclusive w. SAFE & UNSAFE)\n        const LEGACY = 1 << 5;\n        /// 'safe'\n        const SAFE = 1 << 6;\n        /// 'unsafe' (mutually exclusive w. SAFE)\n        const UNSAFE = 1 << 7;\n\n        /// Mask for the additional flags above.\n        const FLAG_BITS = 0b11100000;\n    }\n}\n\nimpl AlignFlags {\n    /// Returns the enumeration value stored in the lower 5 bits.\n    #[inline]\n    pub fn value(&self) -> Self {\n        *self & !AlignFlags::FLAG_BITS\n    }\n\n    /// Returns an updated value with the same flags.\n    #[inline]\n    pub fn with_value(&self, value: AlignFlags) -> Self {\n        debug_assert!(!value.intersects(Self::FLAG_BITS));\n        value | self.flags()\n    }\n\n    /// Returns the flags stored in the upper 3 bits.\n    #[inline]\n    pub fn flags(&self) -> Self {\n        *self & AlignFlags::FLAG_BITS\n    }\n}\n\nimpl ToCss for AlignFlags {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let flags = self.flags();\n        let value = self.value();\n        match flags {\n            AlignFlags::LEGACY => {\n                dest.write_str(\"legacy\")?;\n                if value.is_empty() {\n                    return Ok(());\n                }\n                dest.write_char(' ')?;\n            },\n            AlignFlags::SAFE => dest.write_str(\"safe \")?,\n            AlignFlags::UNSAFE => dest.write_str(\"unsafe \")?,\n            _ => {\n                debug_assert_eq!(flags, AlignFlags::empty());\n            },\n        }\n\n        dest.write_str(match value {\n            AlignFlags::AUTO => \"auto\",\n            AlignFlags::NORMAL => \"normal\",\n            AlignFlags::START => \"start\",\n            AlignFlags::END => \"end\",\n            AlignFlags::FLEX_START => \"flex-start\",\n            AlignFlags::FLEX_END => \"flex-end\",\n            AlignFlags::CENTER => \"center\",\n            AlignFlags::LEFT => \"left\",\n            AlignFlags::RIGHT => \"right\",\n            AlignFlags::BASELINE => \"baseline\",\n            AlignFlags::LAST_BASELINE => \"last baseline\",\n            AlignFlags::STRETCH => \"stretch\",\n            AlignFlags::SELF_START => \"self-start\",\n            AlignFlags::SELF_END => \"self-end\",\n            AlignFlags::SPACE_BETWEEN => \"space-between\",\n            AlignFlags::SPACE_AROUND => \"space-around\",\n            AlignFlags::SPACE_EVENLY => \"space-evenly\",\n            AlignFlags::ANCHOR_CENTER => \"anchor-center\",\n            _ => unreachable!(),\n        })\n    }\n}\n\n/// An axis direction, either inline (for the `justify` properties) or block,\n/// (for the `align` properties).\n#[derive(Clone, Copy, PartialEq)]\npub enum AxisDirection {\n    /// Block direction.\n    Block,\n    /// Inline direction.\n    Inline,\n}\n\n/// Shared value for the `align-content` and `justify-content` properties.\n///\n/// <https://drafts.csswg.org/css-align/#content-distribution>\n/// <https://drafts.csswg.org/css-align/#propdef-align-content>\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct ContentDistribution {\n    primary: AlignFlags,\n    // FIXME(https://github.com/w3c/csswg-drafts/issues/1002): This will need to\n    // accept fallback alignment, eventually.\n}\n\nimpl ContentDistribution {\n    /// The initial value 'normal'\n    #[inline]\n    pub fn normal() -> Self {\n        Self::new(AlignFlags::NORMAL)\n    }\n\n    /// `start`\n    #[inline]\n    pub fn start() -> Self {\n        Self::new(AlignFlags::START)\n    }\n\n    /// The initial value 'normal'\n    #[inline]\n    pub fn new(primary: AlignFlags) -> Self {\n        Self { primary }\n    }\n\n    /// Returns whether this value is a <baseline-position>.\n    pub fn is_baseline_position(&self) -> bool {\n        matches!(\n            self.primary.value(),\n            AlignFlags::BASELINE | AlignFlags::LAST_BASELINE\n        )\n    }\n\n    /// The primary alignment\n    #[inline]\n    pub fn primary(self) -> AlignFlags {\n        self.primary\n    }\n\n    /// Parse a value for align-content\n    pub fn parse_block<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Block)\n    }\n\n    /// Parse a value for justify-content\n    pub fn parse_inline<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Inline)\n    }\n\n    fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        axis: AxisDirection,\n    ) -> Result<Self, ParseError<'i>> {\n        // NOTE Please also update the `list_keywords` function below\n        //      when this function is updated.\n\n        // Try to parse normal first\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(ContentDistribution::normal());\n        }\n\n        // Parse <baseline-position>, but only on the block axis.\n        if axis == AxisDirection::Block {\n            if let Ok(value) = input.try_parse(parse_baseline) {\n                return Ok(ContentDistribution::new(value));\n            }\n        }\n\n        // <content-distribution>\n        if let Ok(value) = input.try_parse(parse_content_distribution) {\n            return Ok(ContentDistribution::new(value));\n        }\n\n        // <overflow-position>? <content-position>\n        let overflow_position = input\n            .try_parse(parse_overflow_position)\n            .unwrap_or(AlignFlags::empty());\n\n        let content_position = try_match_ident_ignore_ascii_case! { input,\n            \"start\" => AlignFlags::START,\n            \"end\" => AlignFlags::END,\n            \"flex-start\" => AlignFlags::FLEX_START,\n            \"flex-end\" => AlignFlags::FLEX_END,\n            \"center\" => AlignFlags::CENTER,\n            \"left\" if axis == AxisDirection::Inline => AlignFlags::LEFT,\n            \"right\" if axis == AxisDirection::Inline => AlignFlags::RIGHT,\n        };\n\n        Ok(ContentDistribution::new(\n            content_position | overflow_position,\n        ))\n    }\n}\n\nimpl SpecifiedValueInfo for ContentDistribution {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        f(&[\"normal\"]);\n        list_baseline_keywords(f); // block-axis only\n        list_content_distribution_keywords(f);\n        list_overflow_position_keywords(f);\n        f(&[\"start\", \"end\", \"flex-start\", \"flex-end\", \"center\"]);\n        f(&[\"left\", \"right\"]); // inline-axis only\n    }\n}\n\n/// The specified value of the {align,justify}-self properties.\n///\n/// <https://drafts.csswg.org/css-align/#self-alignment>\n/// <https://drafts.csswg.org/css-align/#propdef-align-self>\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deref,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct SelfAlignment(pub AlignFlags);\n\nimpl SelfAlignment {\n    /// The initial value 'auto'\n    #[inline]\n    pub fn auto() -> Self {\n        SelfAlignment(AlignFlags::AUTO)\n    }\n\n    /// Returns whether this value is valid for both axis directions.\n    pub fn is_valid_on_both_axes(&self) -> bool {\n        match self.0.value() {\n            // left | right are only allowed on the inline axis.\n            AlignFlags::LEFT | AlignFlags::RIGHT => false,\n\n            _ => true,\n        }\n    }\n\n    /// Parse self-alignment on the block axis (for align-self)\n    pub fn parse_block<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Block)\n    }\n\n    /// Parse self-alignment on the block axis (for align-self)\n    pub fn parse_inline<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Inline)\n    }\n\n    /// Parse a self-alignment value on one of the axes.\n    fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        axis: AxisDirection,\n    ) -> Result<Self, ParseError<'i>> {\n        // NOTE Please also update the `list_keywords` function below\n        //      when this function is updated.\n\n        // <baseline-position>\n        //\n        // It's weird that this accepts <baseline-position>, but not\n        // justify-content...\n        if let Ok(value) = input.try_parse(parse_baseline) {\n            return Ok(SelfAlignment(value));\n        }\n\n        // auto | normal | stretch\n        if let Ok(value) = input.try_parse(parse_auto_normal_stretch) {\n            return Ok(SelfAlignment(value));\n        }\n\n        // <overflow-position>? <self-position>\n        let overflow_position = input\n            .try_parse(parse_overflow_position)\n            .unwrap_or(AlignFlags::empty());\n        let self_position = parse_self_position(input, axis)?;\n        Ok(SelfAlignment(overflow_position | self_position))\n    }\n\n    fn list_keywords(f: KeywordsCollectFn, axis: AxisDirection) {\n        list_baseline_keywords(f);\n        list_auto_normal_stretch(f);\n        list_overflow_position_keywords(f);\n        list_self_position_keywords(f, axis);\n    }\n\n    /// Performs a flip of the position, that is, for self-start we return self-end, for left\n    /// we return right, etc.\n    pub fn flip_position(self) -> Self {\n        let flipped_value = match self.0.value() {\n            AlignFlags::START => AlignFlags::END,\n            AlignFlags::END => AlignFlags::START,\n            AlignFlags::FLEX_START => AlignFlags::FLEX_END,\n            AlignFlags::FLEX_END => AlignFlags::FLEX_START,\n            AlignFlags::LEFT => AlignFlags::RIGHT,\n            AlignFlags::RIGHT => AlignFlags::LEFT,\n            AlignFlags::SELF_START => AlignFlags::SELF_END,\n            AlignFlags::SELF_END => AlignFlags::SELF_START,\n\n            AlignFlags::AUTO\n            | AlignFlags::NORMAL\n            | AlignFlags::BASELINE\n            | AlignFlags::LAST_BASELINE\n            | AlignFlags::STRETCH\n            | AlignFlags::CENTER\n            | AlignFlags::SPACE_BETWEEN\n            | AlignFlags::SPACE_AROUND\n            | AlignFlags::SPACE_EVENLY\n            | AlignFlags::ANCHOR_CENTER => return self,\n            _ => {\n                debug_assert!(false, \"Unexpected alignment enumeration value\");\n                return self;\n            },\n        };\n        self.with_value(flipped_value)\n    }\n\n    /// Returns a fixed-up alignment value.\n    #[inline]\n    pub fn with_value(self, value: AlignFlags) -> Self {\n        Self(self.0.with_value(value))\n    }\n}\n\nimpl SpecifiedValueInfo for SelfAlignment {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        // TODO: This technically lists left/right for align-self. Not amazing but also not sure\n        // worth fixing here, could be special-cased on the caller.\n        Self::list_keywords(f, AxisDirection::Block);\n    }\n}\n\n/// Value of the `align-items` and `justify-items` properties\n///\n/// <https://drafts.csswg.org/css-align/#propdef-align-items>\n/// <https://drafts.csswg.org/css-align/#propdef-justify-items>\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Deref,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct ItemPlacement(pub AlignFlags);\n\nimpl ItemPlacement {\n    /// The value 'normal'\n    #[inline]\n    pub fn normal() -> Self {\n        Self(AlignFlags::NORMAL)\n    }\n}\n\nimpl ItemPlacement {\n    /// Parse a value for align-items\n    pub fn parse_block<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Block)\n    }\n\n    /// Parse a value for justify-items\n    pub fn parse_inline<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse(input, AxisDirection::Inline)\n    }\n\n    fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        axis: AxisDirection,\n    ) -> Result<Self, ParseError<'i>> {\n        // NOTE Please also update `impl SpecifiedValueInfo` below when\n        //      this function is updated.\n\n        // <baseline-position>\n        if let Ok(baseline) = input.try_parse(parse_baseline) {\n            return Ok(Self(baseline));\n        }\n\n        // normal | stretch\n        if let Ok(value) = input.try_parse(parse_normal_stretch) {\n            return Ok(Self(value));\n        }\n\n        if axis == AxisDirection::Inline {\n            // legacy | [ legacy && [ left | right | center ] ]\n            if let Ok(value) = input.try_parse(parse_legacy) {\n                return Ok(Self(value));\n            }\n        }\n\n        // <overflow-position>? <self-position>\n        let overflow = input\n            .try_parse(parse_overflow_position)\n            .unwrap_or(AlignFlags::empty());\n        let self_position = parse_self_position(input, axis)?;\n        Ok(ItemPlacement(self_position | overflow))\n    }\n}\n\nimpl SpecifiedValueInfo for ItemPlacement {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        list_baseline_keywords(f);\n        list_normal_stretch(f);\n        list_overflow_position_keywords(f);\n        list_self_position_keywords(f, AxisDirection::Block);\n    }\n}\n\n/// Value of the `justify-items` property\n///\n/// <https://drafts.csswg.org/css-align/#justify-items-property>\n#[derive(\n    Clone, Copy, Debug, Deref, Eq, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem, ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[repr(C)]\npub struct JustifyItems(pub ItemPlacement);\n\nimpl JustifyItems {\n    /// The initial value 'legacy'\n    #[inline]\n    pub fn legacy() -> Self {\n        Self(ItemPlacement(AlignFlags::LEGACY))\n    }\n\n    /// The value 'normal'\n    #[inline]\n    pub fn normal() -> Self {\n        Self(ItemPlacement::normal())\n    }\n}\n\nimpl Parse for JustifyItems {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        ItemPlacement::parse_inline(context, input).map(Self)\n    }\n}\n\nimpl SpecifiedValueInfo for JustifyItems {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        ItemPlacement::collect_completion_keywords(f);\n        list_legacy_keywords(f); // Inline axis only\n    }\n}\n\n// auto | normal | stretch\nfn parse_auto_normal_stretch<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_auto_normal_stretch` function\n    //      below when this function is updated.\n    try_match_ident_ignore_ascii_case! { input,\n        \"auto\" => Ok(AlignFlags::AUTO),\n        \"normal\" => Ok(AlignFlags::NORMAL),\n        \"stretch\" => Ok(AlignFlags::STRETCH),\n    }\n}\n\nfn list_auto_normal_stretch(f: KeywordsCollectFn) {\n    f(&[\"auto\", \"normal\", \"stretch\"]);\n}\n\n// normal | stretch\nfn parse_normal_stretch<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_normal_stretch` function below\n    //      when this function is updated.\n    try_match_ident_ignore_ascii_case! { input,\n        \"normal\" => Ok(AlignFlags::NORMAL),\n        \"stretch\" => Ok(AlignFlags::STRETCH),\n    }\n}\n\nfn list_normal_stretch(f: KeywordsCollectFn) {\n    f(&[\"normal\", \"stretch\"]);\n}\n\n// <baseline-position>\nfn parse_baseline<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_baseline_keywords` function\n    //      below when this function is updated.\n    try_match_ident_ignore_ascii_case! { input,\n        \"baseline\" => Ok(AlignFlags::BASELINE),\n        \"first\" => {\n            input.expect_ident_matching(\"baseline\")?;\n            Ok(AlignFlags::BASELINE)\n        },\n        \"last\" => {\n            input.expect_ident_matching(\"baseline\")?;\n            Ok(AlignFlags::LAST_BASELINE)\n        },\n    }\n}\n\nfn list_baseline_keywords(f: KeywordsCollectFn) {\n    f(&[\"baseline\", \"first baseline\", \"last baseline\"]);\n}\n\n// <content-distribution>\nfn parse_content_distribution<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_content_distribution_keywords`\n    //      function below when this function is updated.\n    try_match_ident_ignore_ascii_case! { input,\n        \"stretch\" => Ok(AlignFlags::STRETCH),\n        \"space-between\" => Ok(AlignFlags::SPACE_BETWEEN),\n        \"space-around\" => Ok(AlignFlags::SPACE_AROUND),\n        \"space-evenly\" => Ok(AlignFlags::SPACE_EVENLY),\n    }\n}\n\nfn list_content_distribution_keywords(f: KeywordsCollectFn) {\n    f(&[\"stretch\", \"space-between\", \"space-around\", \"space-evenly\"]);\n}\n\n// <overflow-position>\nfn parse_overflow_position<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_overflow_position_keywords`\n    //      function below when this function is updated.\n    try_match_ident_ignore_ascii_case! { input,\n        \"safe\" => Ok(AlignFlags::SAFE),\n        \"unsafe\" => Ok(AlignFlags::UNSAFE),\n    }\n}\n\nfn list_overflow_position_keywords(f: KeywordsCollectFn) {\n    f(&[\"safe\", \"unsafe\"]);\n}\n\n// <self-position> | left | right in the inline axis.\nfn parse_self_position<'i, 't>(\n    input: &mut Parser<'i, 't>,\n    axis: AxisDirection,\n) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_self_position_keywords`\n    //      function below when this function is updated.\n    Ok(try_match_ident_ignore_ascii_case! { input,\n        \"start\" => AlignFlags::START,\n        \"end\" => AlignFlags::END,\n        \"flex-start\" => AlignFlags::FLEX_START,\n        \"flex-end\" => AlignFlags::FLEX_END,\n        \"center\" => AlignFlags::CENTER,\n        \"self-start\" => AlignFlags::SELF_START,\n        \"self-end\" => AlignFlags::SELF_END,\n        \"left\" if axis == AxisDirection::Inline => AlignFlags::LEFT,\n        \"right\" if axis == AxisDirection::Inline => AlignFlags::RIGHT,\n        \"anchor-center\" if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") => AlignFlags::ANCHOR_CENTER,\n    })\n}\n\nfn list_self_position_keywords(f: KeywordsCollectFn, axis: AxisDirection) {\n    f(&[\n        \"start\",\n        \"end\",\n        \"flex-start\",\n        \"flex-end\",\n        \"center\",\n        \"self-start\",\n        \"self-end\",\n    ]);\n\n    if static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n        f(&[\"anchor-center\"]);\n    }\n\n    if axis == AxisDirection::Inline {\n        f(&[\"left\", \"right\"]);\n    }\n}\n\nfn parse_left_right_center<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_legacy_keywords` function below\n    //      when this function is updated.\n    Ok(try_match_ident_ignore_ascii_case! { input,\n        \"left\" => AlignFlags::LEFT,\n        \"right\" => AlignFlags::RIGHT,\n        \"center\" => AlignFlags::CENTER,\n    })\n}\n\n// legacy | [ legacy && [ left | right | center ] ]\nfn parse_legacy<'i, 't>(input: &mut Parser<'i, 't>) -> Result<AlignFlags, ParseError<'i>> {\n    // NOTE Please also update the `list_legacy_keywords` function below\n    //      when this function is updated.\n    let flags = try_match_ident_ignore_ascii_case! { input,\n        \"legacy\" => {\n            let flags = input.try_parse(parse_left_right_center)\n                .unwrap_or(AlignFlags::empty());\n\n            return Ok(AlignFlags::LEGACY | flags)\n        },\n        \"left\" => AlignFlags::LEFT,\n        \"right\" => AlignFlags::RIGHT,\n        \"center\" => AlignFlags::CENTER,\n    };\n\n    input.expect_ident_matching(\"legacy\")?;\n    Ok(AlignFlags::LEGACY | flags)\n}\n\nfn list_legacy_keywords(f: KeywordsCollectFn) {\n    f(&[\"legacy\", \"left\", \"right\", \"center\"]);\n}\n"
  },
  {
    "path": "style/values/specified/angle.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified angles.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::angle::Angle as ComputedAngle;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::specified::calc::CalcNode;\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse std::f32::consts::PI;\nuse std::fmt::{self, Write};\nuse std::ops::Neg;\nuse style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};\n\n/// A specified angle dimension.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToCss, ToShmem)]\npub enum AngleDimension {\n    /// An angle with degree unit.\n    #[css(dimension)]\n    Deg(CSSFloat),\n    /// An angle with gradian unit.\n    #[css(dimension)]\n    Grad(CSSFloat),\n    /// An angle with radian unit.\n    #[css(dimension)]\n    Rad(CSSFloat),\n    /// An angle with turn unit.\n    #[css(dimension)]\n    Turn(CSSFloat),\n}\n\nimpl Zero for AngleDimension {\n    fn zero() -> Self {\n        AngleDimension::Deg(0.)\n    }\n\n    fn is_zero(&self) -> bool {\n        self.unitless_value() == 0.0\n    }\n}\n\nimpl AngleDimension {\n    /// Returns the amount of degrees this angle represents.\n    #[inline]\n    fn degrees(&self) -> CSSFloat {\n        const DEG_PER_RAD: f32 = 180.0 / PI;\n        const DEG_PER_TURN: f32 = 360.0;\n        const DEG_PER_GRAD: f32 = 180.0 / 200.0;\n\n        match *self {\n            AngleDimension::Deg(d) => d,\n            AngleDimension::Rad(rad) => rad * DEG_PER_RAD,\n            AngleDimension::Turn(turns) => turns * DEG_PER_TURN,\n            AngleDimension::Grad(gradians) => gradians * DEG_PER_GRAD,\n        }\n    }\n\n    fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            AngleDimension::Deg(v)\n            | AngleDimension::Rad(v)\n            | AngleDimension::Turn(v)\n            | AngleDimension::Grad(v) => v,\n        }\n    }\n\n    fn unit(&self) -> &'static str {\n        match *self {\n            AngleDimension::Deg(_) => \"deg\",\n            AngleDimension::Rad(_) => \"rad\",\n            AngleDimension::Turn(_) => \"turn\",\n            AngleDimension::Grad(_) => \"grad\",\n        }\n    }\n}\n\n/// A specified Angle value, which is just the angle dimension, plus whether it\n/// was specified as `calc()` or not.\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct Angle {\n    value: AngleDimension,\n    was_calc: bool,\n}\n\nimpl Zero for Angle {\n    fn zero() -> Self {\n        Self {\n            value: Zero::zero(),\n            was_calc: false,\n        }\n    }\n\n    fn is_zero(&self) -> bool {\n        self.value.is_zero()\n    }\n}\n\nimpl ToCss for Angle {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        crate::values::serialize_specified_dimension(\n            self.value.unitless_value(),\n            self.value.unit(),\n            self.was_calc,\n            dest,\n        )\n    }\n}\n\nimpl ToComputedValue for Angle {\n    type ComputedValue = ComputedAngle;\n\n    #[inline]\n    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {\n        let degrees = self.degrees();\n\n        // NaN and +-infinity should degenerate to 0: https://github.com/w3c/csswg-drafts/issues/6105\n        ComputedAngle::from_degrees(if degrees.is_finite() { degrees } else { 0.0 })\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Angle {\n            value: AngleDimension::Deg(computed.degrees()),\n            was_calc: false,\n        }\n    }\n}\n\nimpl Angle {\n    /// Creates an angle with the given value in degrees.\n    #[inline]\n    pub fn from_degrees(value: CSSFloat, was_calc: bool) -> Self {\n        Angle {\n            value: AngleDimension::Deg(value),\n            was_calc,\n        }\n    }\n\n    /// Creates an angle with the given value in radians.\n    #[inline]\n    pub fn from_radians(value: CSSFloat) -> Self {\n        Angle {\n            value: AngleDimension::Rad(value),\n            was_calc: false,\n        }\n    }\n\n    /// Return `0deg`.\n    pub fn zero() -> Self {\n        Self::from_degrees(0.0, false)\n    }\n\n    /// Returns the value of the angle in degrees, mostly for `calc()`.\n    #[inline]\n    pub fn degrees(&self) -> CSSFloat {\n        self.value.degrees()\n    }\n\n    /// Returns the value of the angle in radians.\n    #[inline]\n    pub fn radians(&self) -> CSSFloat {\n        const RAD_PER_DEG: f32 = PI / 180.0;\n        self.value.degrees() * RAD_PER_DEG\n    }\n\n    /// Whether this specified angle came from a `calc()` expression.\n    #[inline]\n    pub fn was_calc(&self) -> bool {\n        self.was_calc\n    }\n\n    /// Returns an `Angle` parsed from a `calc()` expression.\n    pub fn from_calc(degrees: CSSFloat) -> Self {\n        Angle {\n            value: AngleDimension::Deg(degrees),\n            was_calc: true,\n        }\n    }\n\n    /// Returns the unit of the angle.\n    #[inline]\n    pub fn unit(&self) -> &'static str {\n        self.value.unit()\n    }\n}\n\n/// Whether to allow parsing an unitless zero as a valid angle.\n///\n/// This should always be `No`, except for exceptions like:\n///\n///   https://github.com/w3c/fxtf-drafts/issues/228\n///\n/// See also: https://github.com/w3c/csswg-drafts/issues/1162.\n#[allow(missing_docs)]\npub enum AllowUnitlessZeroAngle {\n    Yes,\n    No,\n}\n\nimpl Parse for Angle {\n    /// Parses an angle according to CSS-VALUES § 6.1.\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, AllowUnitlessZeroAngle::No)\n    }\n}\n\nimpl Angle {\n    /// Parse an `<angle>` value given a value and an unit.\n    pub fn parse_dimension(value: CSSFloat, unit: &str, was_calc: bool) -> Result<Angle, ()> {\n        let value = match_ignore_ascii_case! { unit,\n            \"deg\" => AngleDimension::Deg(value),\n            \"grad\" => AngleDimension::Grad(value),\n            \"turn\" => AngleDimension::Turn(value),\n            \"rad\" => AngleDimension::Rad(value),\n             _ => return Err(())\n        };\n\n        Ok(Self { value, was_calc })\n    }\n\n    /// Parse an `<angle>` allowing unitless zero to represent a zero angle.\n    ///\n    /// See the comment in `AllowUnitlessZeroAngle` for why.\n    #[inline]\n    pub fn parse_with_unitless<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)\n    }\n\n    pub(super) fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_unitless_zero: AllowUnitlessZeroAngle,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let t = input.next()?;\n        let allow_unitless_zero = matches!(allow_unitless_zero, AllowUnitlessZeroAngle::Yes);\n        match *t {\n            Token::Dimension {\n                value, ref unit, ..\n            } => {\n                match Angle::parse_dimension(value, unit, /* from_calc = */ false) {\n                    Ok(angle) => Ok(angle),\n                    Err(()) => {\n                        let t = t.clone();\n                        Err(input.new_unexpected_token_error(t))\n                    },\n                }\n            },\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                CalcNode::parse_angle(context, input, function)\n            },\n            Token::Number { value, .. } if value == 0. && allow_unitless_zero => Ok(Angle::zero()),\n            ref t => {\n                let t = t.clone();\n                Err(input.new_unexpected_token_error(t))\n            },\n        }\n    }\n}\n\nimpl SpecifiedValueInfo for Angle {}\n\nimpl Neg for Angle {\n    type Output = Angle;\n\n    #[inline]\n    fn neg(self) -> Angle {\n        let value = match self.value {\n            AngleDimension::Deg(v) => AngleDimension::Deg(-v),\n            AngleDimension::Rad(v) => AngleDimension::Rad(-v),\n            AngleDimension::Turn(v) => AngleDimension::Turn(-v),\n            AngleDimension::Grad(v) => AngleDimension::Grad(-v),\n        };\n        Angle {\n            value,\n            was_calc: self.was_calc,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/animation.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for properties related to animations and transitions.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::{NonCustomPropertyId, PropertyId, ShorthandId};\nuse crate::values::generics::animation as generics;\nuse crate::values::generics::position::{IsTreeScoped, TreeScoped};\nuse crate::values::specified::{LengthPercentage, NonNegativeNumber, Time};\nuse crate::values::{AtomIdent, CustomIdent, DashedIdent, KeyframesName};\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse std::fmt::{self, Write};\nuse style_traits::{\n    CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,\n    ToTyped,\n};\n\n/// A given transition property, that is either `All`, a longhand or shorthand\n/// property, or an unsupported or custom property.\n#[derive(\n    Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem,\n)]\n#[repr(u8)]\npub enum TransitionProperty {\n    /// A non-custom property.\n    NonCustom(NonCustomPropertyId),\n    /// A custom property.\n    Custom(Atom),\n    /// Unrecognized property which could be any non-transitionable, custom property, or\n    /// unknown property.\n    Unsupported(CustomIdent),\n}\n\nimpl ToCss for TransitionProperty {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            TransitionProperty::NonCustom(ref id) => id.to_css(dest),\n            TransitionProperty::Custom(ref name) => {\n                dest.write_str(\"--\")?;\n                crate::values::serialize_atom_name(name, dest)\n            },\n            TransitionProperty::Unsupported(ref i) => i.to_css(dest),\n        }\n    }\n}\n\nimpl ToTyped for TransitionProperty {}\n\nimpl Parse for TransitionProperty {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n\n        let id = match PropertyId::parse_ignoring_rule_type(&ident, context) {\n            Ok(id) => id,\n            Err(..) => {\n                // None is not acceptable as a single transition-property.\n                return Ok(TransitionProperty::Unsupported(CustomIdent::from_ident(\n                    location,\n                    ident,\n                    &[\"none\"],\n                )?));\n            },\n        };\n\n        Ok(match id {\n            PropertyId::NonCustom(id) => TransitionProperty::NonCustom(id.unaliased()),\n            PropertyId::Custom(name) => TransitionProperty::Custom(name),\n        })\n    }\n}\n\nimpl SpecifiedValueInfo for TransitionProperty {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        // `transition-property` can actually accept all properties and\n        // arbitrary identifiers, but `all` is a special one we'd like\n        // to list.\n        f(&[\"all\"]);\n    }\n}\n\nimpl TransitionProperty {\n    /// Returns the `none` value.\n    #[inline]\n    pub fn none() -> Self {\n        TransitionProperty::Unsupported(CustomIdent(atom!(\"none\")))\n    }\n\n    /// Returns whether we're the `none` value.\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        matches!(*self, TransitionProperty::Unsupported(ref ident) if ident.0 == atom!(\"none\"))\n    }\n\n    /// Returns `all`.\n    #[inline]\n    pub fn all() -> Self {\n        TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand(ShorthandId::All))\n    }\n\n    /// Returns true if it is `all`.\n    #[inline]\n    pub fn is_all(&self) -> bool {\n        self == &TransitionProperty::NonCustom(NonCustomPropertyId::from_shorthand(\n            ShorthandId::All,\n        ))\n    }\n}\n\n/// A specified value for <transition-behavior-value>.\n///\n/// https://drafts.csswg.org/css-transitions-2/#transition-behavior-property\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum TransitionBehavior {\n    /// Transitions will not be started for discrete properties, only for interpolable properties.\n    Normal,\n    /// Transitions will be started for discrete properties as well as interpolable properties.\n    AllowDiscrete,\n}\n\nimpl TransitionBehavior {\n    /// Return normal, the initial value.\n    #[inline]\n    pub fn normal() -> Self {\n        Self::Normal\n    }\n\n    /// Return true if it is normal.\n    #[inline]\n    pub fn is_normal(&self) -> bool {\n        matches!(*self, Self::Normal)\n    }\n}\n\n/// A specified value for the `animation-duration` property.\npub type AnimationDuration = generics::GenericAnimationDuration<Time>;\n\nimpl Parse for AnimationDuration {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if static_prefs::pref!(\"layout.css.scroll-driven-animations.enabled\")\n            && input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok()\n        {\n            return Ok(Self::auto());\n        }\n\n        Time::parse_non_negative(context, input).map(AnimationDuration::Time)\n    }\n}\n\n/// https://drafts.csswg.org/css-animations/#animation-iteration-count\n#[derive(\n    Copy, Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum AnimationIterationCount {\n    /// A `<number>` value.\n    Number(NonNegativeNumber),\n    /// The `infinite` keyword.\n    Infinite,\n}\n\nimpl AnimationIterationCount {\n    /// Returns the value `1.0`.\n    #[inline]\n    pub fn one() -> Self {\n        Self::Number(NonNegativeNumber::new(1.0))\n    }\n\n    /// Returns true if it's `1.0`.\n    #[inline]\n    pub fn is_one(&self) -> bool {\n        *self == Self::one()\n    }\n}\n\n/// A value for the `animation-name` property.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[value_info(other_values = \"none\")]\n#[repr(C)]\npub struct AnimationName(pub KeyframesName);\n\nimpl AnimationName {\n    /// Get the name of the animation as an `Atom`.\n    pub fn as_atom(&self) -> Option<&Atom> {\n        if self.is_none() {\n            return None;\n        }\n        Some(self.0.as_atom())\n    }\n\n    /// Returns the `none` value.\n    pub fn none() -> Self {\n        AnimationName(KeyframesName::none())\n    }\n\n    /// Returns whether this is the none value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_none()\n    }\n}\n\nimpl Parse for AnimationName {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(name) = input.try_parse(|input| KeyframesName::parse(context, input)) {\n            return Ok(AnimationName(name));\n        }\n\n        input.expect_ident_matching(\"none\")?;\n        Ok(AnimationName(KeyframesName::none()))\n    }\n}\n\n/// https://drafts.csswg.org/css-animations/#propdef-animation-direction\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum AnimationDirection {\n    Normal,\n    Reverse,\n    Alternate,\n    AlternateReverse,\n}\n\nimpl AnimationDirection {\n    /// Returns true if the name matches any animation-direction keyword.\n    #[inline]\n    pub fn match_keywords(name: &AnimationName) -> bool {\n        if let Some(name) = name.as_atom() {\n            #[cfg(feature = \"gecko\")]\n            return name.with_str(|n| Self::from_ident(n).is_ok());\n            #[cfg(feature = \"servo\")]\n            return Self::from_ident(name).is_ok();\n        }\n        false\n    }\n}\n\n/// https://drafts.csswg.org/css-animations/#animation-play-state\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum AnimationPlayState {\n    Running,\n    Paused,\n}\n\nimpl AnimationPlayState {\n    /// Returns true if the name matches any animation-play-state keyword.\n    #[inline]\n    pub fn match_keywords(name: &AnimationName) -> bool {\n        if let Some(name) = name.as_atom() {\n            #[cfg(feature = \"gecko\")]\n            return name.with_str(|n| Self::from_ident(n).is_ok());\n            #[cfg(feature = \"servo\")]\n            return Self::from_ident(name).is_ok();\n        }\n        false\n    }\n}\n\n/// https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum AnimationFillMode {\n    None,\n    Forwards,\n    Backwards,\n    Both,\n}\n\nimpl AnimationFillMode {\n    /// Returns true if the name matches any animation-fill-mode keyword.\n    /// Note: animation-name:none is its initial value, so we don't have to match none here.\n    #[inline]\n    pub fn match_keywords(name: &AnimationName) -> bool {\n        if let Some(name) = name.as_atom() {\n            #[cfg(feature = \"gecko\")]\n            return name.with_str(|n| Self::from_ident(n).is_ok());\n            #[cfg(feature = \"servo\")]\n            return Self::from_ident(name).is_ok();\n        }\n        false\n    }\n}\n\n/// https://drafts.csswg.org/css-animations-2/#animation-composition\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[allow(missing_docs)]\npub enum AnimationComposition {\n    Replace,\n    Add,\n    Accumulate,\n}\n\n/// A value for the <Scroller> used in scroll().\n///\n/// https://drafts.csswg.org/scroll-animations-1/rewrite#typedef-scroller\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum Scroller {\n    /// The nearest ancestor scroll container. (Default.)\n    Nearest,\n    /// The document viewport as the scroll container.\n    Root,\n    /// Specifies to use the element’s own principal box as the scroll container.\n    #[css(keyword = \"self\")]\n    SelfElement,\n}\n\nimpl Scroller {\n    /// Returns true if it is default.\n    #[inline]\n    fn is_default(&self) -> bool {\n        matches!(*self, Self::Nearest)\n    }\n}\n\nimpl Default for Scroller {\n    fn default() -> Self {\n        Self::Nearest\n    }\n}\n\n/// A value for the <Axis> used in scroll(), or a value for {scroll|view}-timeline-axis.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#typedef-axis\n/// https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-axis\n/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-axis\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ScrollAxis {\n    /// The block axis of the scroll container. (Default.)\n    Block = 0,\n    /// The inline axis of the scroll container.\n    Inline = 1,\n    /// The horizontal axis of the scroll container.\n    X = 2,\n    /// The vertical axis of the scroll container.\n    Y = 3,\n}\n\nimpl ScrollAxis {\n    /// Returns true if it is default.\n    #[inline]\n    pub fn is_default(&self) -> bool {\n        matches!(*self, Self::Block)\n    }\n}\n\nimpl Default for ScrollAxis {\n    fn default() -> Self {\n        Self::Block\n    }\n}\n\n/// The scroll() notation.\n/// https://drafts.csswg.org/scroll-animations-1/#scroll-notation\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function = \"scroll\")]\n#[repr(C)]\npub struct ScrollFunction {\n    /// The scroll container element whose scroll position drives the progress of the timeline.\n    #[css(skip_if = \"Scroller::is_default\")]\n    pub scroller: Scroller,\n    /// The axis of scrolling that drives the progress of the timeline.\n    #[css(skip_if = \"ScrollAxis::is_default\")]\n    pub axis: ScrollAxis,\n}\n\nimpl ScrollFunction {\n    /// Parse the inner function arguments of `scroll()`.\n    fn parse_arguments<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        // <scroll()> = scroll( [ <scroller> || <axis> ]? )\n        // https://drafts.csswg.org/scroll-animations-1/#funcdef-scroll\n        let mut scroller = None;\n        let mut axis = None;\n        loop {\n            if scroller.is_none() {\n                scroller = input.try_parse(Scroller::parse).ok();\n            }\n\n            if axis.is_none() {\n                axis = input.try_parse(ScrollAxis::parse).ok();\n                if axis.is_some() {\n                    continue;\n                }\n            }\n            break;\n        }\n\n        Ok(Self {\n            scroller: scroller.unwrap_or_default(),\n            axis: axis.unwrap_or_default(),\n        })\n    }\n}\n\nimpl generics::ViewFunction<LengthPercentage> {\n    /// Parse the inner function arguments of `view()`.\n    fn parse_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // <view()> = view( [ <axis> || <'view-timeline-inset'> ]? )\n        // https://drafts.csswg.org/scroll-animations-1/#funcdef-view\n        let mut axis = None;\n        let mut inset = None;\n        loop {\n            if axis.is_none() {\n                axis = input.try_parse(ScrollAxis::parse).ok();\n            }\n\n            if inset.is_none() {\n                inset = input\n                    .try_parse(|i| ViewTimelineInset::parse(context, i))\n                    .ok();\n                if inset.is_some() {\n                    continue;\n                }\n            }\n            break;\n        }\n\n        Ok(Self {\n            inset: inset.unwrap_or_default(),\n            axis: axis.unwrap_or_default(),\n        })\n    }\n}\n\n/// The typedef of scroll-timeline-name or view-timeline-name.\n///\n/// https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-name\n/// https://drafts.csswg.org/scroll-animations-1/#view-timeline-name\npub type TimelineName = TreeScoped<TimelineIdent>;\n\nimpl TimelineName {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self::with_default_level(TimelineIdent::none())\n    }\n}\n\n/// The identifier for a timeline name.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct TimelineIdent(DashedIdent);\n\nimpl TimelineIdent {\n    /// Returns the `none` value.\n    pub fn none() -> Self {\n        Self(DashedIdent::empty())\n    }\n\n    /// Check if this is `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\nimpl IsTreeScoped for TimelineIdent {\n    fn is_tree_scoped(&self) -> bool {\n        !self.is_none()\n    }\n}\n\nimpl Parse for TimelineIdent {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(Self::none());\n        }\n\n        DashedIdent::parse(context, input).map(TimelineIdent)\n    }\n}\n\nimpl ToCss for TimelineIdent {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_none() {\n            return dest.write_str(\"none\");\n        }\n\n        self.0.to_css(dest)\n    }\n}\n\nimpl ToTyped for TimelineName {}\n\n/// A specified value for the `animation-timeline` property.\npub type AnimationTimeline = generics::GenericAnimationTimeline<LengthPercentage>;\n\nimpl Parse for AnimationTimeline {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::generics::animation::ViewFunction;\n\n        // <single-animation-timeline> = auto | none | <dashed-ident> | <scroll()> | <view()>\n        // https://drafts.csswg.org/css-animations-2/#typedef-single-animation-timeline\n\n        if input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok() {\n            return Ok(Self::Auto);\n        }\n\n        // This parses none or <dashed-indent>.\n        if let Ok(name) = input.try_parse(|i| TimelineName::parse(context, i)) {\n            return Ok(AnimationTimeline::Timeline(name));\n        }\n\n        // Parse <scroll()> or <view()>.\n        let location = input.current_source_location();\n        let function = input.expect_function()?.clone();\n        input.parse_nested_block(move |i| {\n            match_ignore_ascii_case! { &function,\n                \"scroll\" => ScrollFunction::parse_arguments(i).map(Self::Scroll),\n                \"view\" => ViewFunction::parse_arguments(context, i).map(Self::View),\n                _ => {\n                    Err(location.new_custom_error(\n                        StyleParseErrorKind::UnexpectedFunction(function.clone())\n                    ))\n                },\n            }\n        })\n    }\n}\n\n/// A specified value for the `view-timeline-inset` property.\npub type ViewTimelineInset = generics::GenericViewTimelineInset<LengthPercentage>;\n\nimpl Parse for ViewTimelineInset {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::specified::LengthPercentageOrAuto;\n\n        let start = LengthPercentageOrAuto::parse(context, input)?;\n        let end = match input.try_parse(|input| LengthPercentageOrAuto::parse(context, input)) {\n            Ok(end) => end,\n            Err(_) => start.clone(),\n        };\n\n        Ok(Self { start, end })\n    }\n}\n\n/// The view-transition-name: `none | <custom-ident> | match-element`.\n///\n/// https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop\n/// https://drafts.csswg.org/css-view-transitions-2/#auto-vt-name\n// TODO: auto keyword.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    Hash,\n    PartialEq,\n    MallocSizeOf,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\n#[value_info(other_values = \"none, match-element\")]\npub struct ViewTransitionNameKeyword(AtomIdent);\n\nimpl ViewTransitionNameKeyword {\n    /// Returns the `none` value.\n    pub fn none() -> Self {\n        Self(AtomIdent::new(atom!(\"none\")))\n    }\n}\n\nimpl IsTreeScoped for ViewTransitionNameKeyword {\n    fn is_tree_scoped(&self) -> bool {\n        self.0 .0 != atom!(\"none\")\n    }\n}\n\nimpl Parse for ViewTransitionNameKeyword {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n        if ident.eq_ignore_ascii_case(\"none\") {\n            return Ok(Self::none());\n        }\n\n        if ident.eq_ignore_ascii_case(\"match-element\") {\n            return Ok(Self(AtomIdent::new(atom!(\"match-element\"))));\n        }\n\n        // We check none already, so don't need to exclude none here.\n        // Note: \"auto\" is not supported yet so we exclude it.\n        CustomIdent::from_ident(location, ident, &[\"auto\"]).map(|i| Self(AtomIdent::new(i.0)))\n    }\n}\n\n/// https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop\npub type ViewTransitionName = TreeScoped<ViewTransitionNameKeyword>;\n\nimpl ViewTransitionName {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self::with_default_level(ViewTransitionNameKeyword::none())\n    }\n}\n\n/// The view-transition-class: `none | <custom-ident>+`.\n///\n/// https://drafts.csswg.org/css-view-transitions-2/#view-transition-class-prop\n///\n/// Empty slice represents `none`.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    Eq,\n    Hash,\n    PartialEq,\n    MallocSizeOf,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[value_info(other_values = \"none\")]\npub struct ViewTransitionClassList(\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    crate::ArcSlice<CustomIdent>,\n);\n\nimpl IsTreeScoped for ViewTransitionClassList {\n    fn is_tree_scoped(&self) -> bool {\n        !self.is_none()\n    }\n}\n\nimpl ViewTransitionClassList {\n    /// Returns the default value, `none`. We use the default slice (i.e. empty) to represent it.\n    pub fn none() -> Self {\n        Self(Default::default())\n    }\n\n    /// Returns whether this is the `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Iterates over the contained custom idents.\n    pub fn iter(&self) -> impl Iterator<Item = &CustomIdent> {\n        self.0.iter()\n    }\n}\n\nimpl Parse for ViewTransitionClassList {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use style_traits::{Separator, Space};\n\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(Self::none());\n        }\n\n        Ok(Self(crate::ArcSlice::from_iter(\n            Space::parse(input, |i| CustomIdent::parse(i, &[\"none\"]))?.into_iter(),\n        )))\n    }\n}\n\n/// https://drafts.csswg.org/css-view-transitions-2/#view-transition-class-prop\npub type ViewTransitionClass = TreeScoped<ViewTransitionClassList>;\n\nimpl ViewTransitionClass {\n    /// Returns the default value, `none`.\n    pub fn none() -> Self {\n        Self::with_default_level(ViewTransitionClassList::none())\n    }\n}\n\n/// The <timeline-range-name> value type, which indicates a CSS identifier representing one of the\n/// predefined named timeline ranges.\n/// https://drafts.csswg.org/scroll-animations-1/#named-ranges\n///\n/// For now, only view timeline ranges use this type.\n/// https://drafts.csswg.org/scroll-animations-1/#view-timelines-ranges\n#[derive(\n    Copy,\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum TimelineRangeName {\n    /// The default normal value.\n    #[css(skip)]\n    Normal,\n    /// No timeline range name specified.\n    #[css(skip)]\n    None,\n    /// Represents the full range of the view progress timeline\n    Cover,\n    /// Represents the range during which the principal box is either fully contained by, or fully\n    /// covers, its view progress visibility range within the scrollport.\n    Contain,\n    /// Represents the range during which the principal box is entering the view progress\n    /// visibility range.\n    Entry,\n    /// Represents the range during which the principal box is exiting the view progress visibility\n    /// range.\n    Exit,\n    /// Represents the range during which the principal box crosses the end border edge.\n    EntryCrossing,\n    /// Represents the range during which the principal box crosses the start border edge.\n    ExitCrossing,\n    /// Represents the full range of the scroll container on which the view progress timeline is\n    /// defined.\n    Scroll,\n}\n\nimpl TimelineRangeName {\n    /// Returns true if it is `normal`.\n    #[inline]\n    pub fn is_normal(&self) -> bool {\n        matches!(*self, Self::Normal)\n    }\n\n    /// Returns true if it is `none`.\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        matches!(*self, Self::None)\n    }\n}\n\n/// The internal value for `animation-range-start` and `animation-range-end`.\npub type AnimationRangeValue = generics::GenericAnimationRangeValue<LengthPercentage>;\n\nfn parse_animation_range<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    default: LengthPercentage,\n) -> Result<AnimationRangeValue, ParseError<'i>> {\n    if input\n        .try_parse(|i| i.expect_ident_matching(\"normal\"))\n        .is_ok()\n    {\n        return Ok(AnimationRangeValue::normal(default));\n    }\n\n    if let Ok(lp) = input.try_parse(|i| LengthPercentage::parse(context, i)) {\n        return Ok(AnimationRangeValue::length_percentage(lp));\n    }\n\n    let name = TimelineRangeName::parse(input)?;\n    let lp = input\n        .try_parse(|i| LengthPercentage::parse(context, i))\n        .unwrap_or(default);\n    Ok(AnimationRangeValue::new(name, lp))\n}\n\n/// A specified value for the `animation-range-start`.\npub type AnimationRangeStart = generics::GenericAnimationRangeStart<LengthPercentage>;\n\nimpl Parse for AnimationRangeStart {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_animation_range(context, input, LengthPercentage::zero_percent()).map(Self)\n    }\n}\n\n/// A specified value for the `animation-range-end`.\npub type AnimationRangeEnd = generics::GenericAnimationRangeEnd<LengthPercentage>;\n\nimpl Parse for AnimationRangeEnd {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_animation_range(context, input, LengthPercentage::hundred_percent()).map(Self)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/background.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values related to backgrounds.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::background::BackgroundSize as GenericBackgroundSize;\nuse crate::values::specified::length::{\n    NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,\n};\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse selectors::parser::SelectorParseErrorKind;\nuse std::fmt::{self, Write};\nuse style_traits::{CssString, CssWriter, KeywordValue, ParseError, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\n/// A specified value for the `background-size` property.\npub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthPercentage>;\n\nimpl Parse for BackgroundSize {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(width) = input.try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))\n        {\n            let height = input\n                .try_parse(|i| NonNegativeLengthPercentageOrAuto::parse(context, i))\n                .unwrap_or(NonNegativeLengthPercentageOrAuto::auto());\n            return Ok(GenericBackgroundSize::ExplicitSize { width, height });\n        }\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"cover\" => GenericBackgroundSize::Cover,\n            \"contain\" => GenericBackgroundSize::Contain,\n        })\n    }\n}\n\n/// One of the keywords for `background-repeat`.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\n#[value_info(other_values = \"repeat-x,repeat-y\")]\npub enum BackgroundRepeatKeyword {\n    Repeat,\n    Space,\n    Round,\n    NoRepeat,\n}\n\n/// The value of the `background-repeat` property, with `repeat-x` / `repeat-y`\n/// represented as the combination of `no-repeat` and `repeat` in the opposite\n/// axes.\n///\n/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);\n\nimpl BackgroundRepeat {\n    /// Returns the `repeat repeat` value.\n    pub fn repeat() -> Self {\n        BackgroundRepeat(\n            BackgroundRepeatKeyword::Repeat,\n            BackgroundRepeatKeyword::Repeat,\n        )\n    }\n}\n\nimpl ToCss for BackgroundRepeat {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match (self.0, self.1) {\n            (BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {\n                dest.write_str(\"repeat-x\")\n            },\n            (BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {\n                dest.write_str(\"repeat-y\")\n            },\n            (horizontal, vertical) => {\n                horizontal.to_css(dest)?;\n                if horizontal != vertical {\n                    dest.write_char(' ')?;\n                    vertical.to_css(dest)?;\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\nimpl ToTyped for BackgroundRepeat {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        match (self.0, self.1) {\n            (BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {\n                dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\n                    \"repeat-x\",\n                ))));\n                Ok(())\n            },\n            (BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {\n                dest.push(TypedValue::Keyword(KeywordValue(CssString::from(\n                    \"repeat-y\",\n                ))));\n                Ok(())\n            },\n            (horizontal, vertical) if horizontal == vertical => {\n                ToTyped::to_typed(&horizontal, dest)\n            },\n            _ => Err(()),\n        }\n    }\n}\n\nimpl Parse for BackgroundRepeat {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let ident = input.expect_ident_cloned()?;\n\n        match_ignore_ascii_case! { &ident,\n            \"repeat-x\" => {\n                return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));\n            },\n            \"repeat-y\" => {\n                return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));\n            },\n            _ => {},\n        }\n\n        let horizontal = match BackgroundRepeatKeyword::from_ident(&ident) {\n            Ok(h) => h,\n            Err(()) => {\n                return Err(\n                    input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))\n                );\n            },\n        };\n\n        let vertical = input.try_parse(BackgroundRepeatKeyword::parse).ok();\n        Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/basic_shape.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the specified value of\n//! [`basic-shape`][basic-shape]s\n//!\n//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::basic_shape::InsetRect as ComputedInsetRect;\nuse crate::values::computed::{\n    Context, LengthPercentage as ComputedLengthPercentage, ToComputedValue,\n};\nuse crate::values::generics::basic_shape as generic;\nuse crate::values::generics::basic_shape::{Path, PolygonCoord};\nuse crate::values::generics::position::GenericPositionOrAuto;\nuse crate::values::generics::rect::Rect;\nuse crate::values::specified::angle::Angle;\nuse crate::values::specified::border::BorderRadius;\nuse crate::values::specified::image::Image;\nuse crate::values::specified::length::LengthPercentageOrAuto;\nuse crate::values::specified::position::Position;\nuse crate::values::specified::url::SpecifiedUrl;\nuse crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage, SVGPathData};\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// A specified alias for FillRule.\npub use crate::values::generics::basic_shape::FillRule;\n\n/// A specified `clip-path` value.\npub type ClipPath = generic::GenericClipPath<BasicShape, SpecifiedUrl>;\n\n/// A specified `shape-outside` value.\npub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;\n\n/// A specified basic shape.\npub type BasicShape = generic::GenericBasicShape<Angle, Position, LengthPercentage, BasicShapeRect>;\n\n/// The specified value of `inset()`.\npub type InsetRect = generic::GenericInsetRect<LengthPercentage>;\n\n/// A specified circle.\npub type Circle = generic::Circle<Position, LengthPercentage>;\n\n/// A specified ellipse.\npub type Ellipse = generic::Ellipse<Position, LengthPercentage>;\n\n/// The specified value of `ShapeRadius`.\npub type ShapeRadius = generic::ShapeRadius<LengthPercentage>;\n\n/// The specified value of `Polygon`.\npub type Polygon = generic::GenericPolygon<LengthPercentage>;\n\n/// The specified value of `PathOrShapeFunction`.\npub type PathOrShapeFunction =\n    generic::GenericPathOrShapeFunction<Angle, Position, LengthPercentage>;\n\n/// The specified value of `ShapeCommand`.\npub type ShapeCommand = generic::GenericShapeCommand<Angle, Position, LengthPercentage>;\n\n/// The specified value of `xywh()`.\n/// Defines a rectangle via offsets from the top and left edge of the reference box, and a\n/// specified width and height.\n///\n/// The four <length-percentage>s define, respectively, the inset from the left edge of the\n/// reference box, the inset from the top edge of the reference box, the width of the rectangle,\n/// and the height of the rectangle.\n///\n/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-xywh\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]\npub struct Xywh {\n    /// The left edge of the reference box.\n    pub x: LengthPercentage,\n    /// The top edge of the reference box.\n    pub y: LengthPercentage,\n    /// The specified width.\n    pub width: NonNegativeLengthPercentage,\n    /// The specified height.\n    pub height: NonNegativeLengthPercentage,\n    /// The optional <border-radius> argument(s) define rounded corners for the inset rectangle\n    /// using the border-radius shorthand syntax.\n    pub round: BorderRadius,\n}\n\n/// Defines a rectangle via insets from the top and left edges of the reference box.\n///\n/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]\n#[repr(C)]\npub struct ShapeRectFunction {\n    /// The four <length-percentage>s define the position of the top, right, bottom, and left edges\n    /// of a rectangle, respectively, as insets from the top edge of the reference box (for the\n    /// first and third values) or the left edge of the reference box (for the second and fourth\n    /// values).\n    ///\n    /// An auto value makes the edge of the box coincide with the corresponding edge of the\n    /// reference box: it’s equivalent to 0% as the first (top) or fourth (left) value, and\n    /// equivalent to 100% as the second (right) or third (bottom) value.\n    pub rect: Rect<LengthPercentageOrAuto>,\n    /// The optional <border-radius> argument(s) define rounded corners for the inset rectangle\n    /// using the border-radius shorthand syntax.\n    pub round: BorderRadius,\n}\n\n/// The specified value of <basic-shape-rect>.\n/// <basic-shape-rect> = <inset()> | <rect()> | <xywh()>\n///\n/// https://drafts.csswg.org/css-shapes-1/#supported-basic-shapes\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\npub enum BasicShapeRect {\n    /// Defines an inset rectangle via insets from each edge of the reference box.\n    Inset(InsetRect),\n    /// Defines a xywh function.\n    #[css(function)]\n    Xywh(Xywh),\n    /// Defines a rect function.\n    #[css(function)]\n    Rect(ShapeRectFunction),\n}\n\n/// For filled shapes, we use fill-rule, and store it for path() and polygon().\n/// For outline shapes, we should ignore fill-rule.\n///\n/// https://github.com/w3c/fxtf-drafts/issues/512\n/// https://github.com/w3c/csswg-drafts/issues/7390\n/// https://github.com/w3c/csswg-drafts/issues/3468\npub enum ShapeType {\n    /// The CSS property uses filled shapes. The default behavior.\n    Filled,\n    /// The CSS property uses outline shapes. This is especially useful for offset-path.\n    Outline,\n}\n\nbitflags! {\n    /// The flags to represent which basic shapes we would like to support.\n    ///\n    /// Different properties may use different subsets of <basic-shape>:\n    /// e.g.\n    /// clip-path: all basic shapes.\n    /// motion-path: all basic shapes (but ignore fill-rule).\n    /// shape-outside: inset(), circle(), ellipse(), polygon().\n    ///\n    /// Also there are some properties we don't support for now:\n    /// shape-inside: inset(), circle(), ellipse(), polygon().\n    /// SVG shape-inside and shape-subtract: circle(), ellipse(), polygon().\n    ///\n    /// The spec issue proposes some better ways to clarify the usage of basic shapes, so for now\n    /// we use the bitflags to choose the supported basic shapes for each property at the parse\n    /// time.\n    /// https://github.com/w3c/csswg-drafts/issues/7390\n    #[derive(Clone, Copy)]\n    #[repr(C)]\n    pub struct AllowedBasicShapes: u8 {\n        /// inset().\n        const INSET = 1 << 0;\n        /// xywh().\n        const XYWH = 1 << 1;\n        /// rect().\n        const RECT = 1 << 2;\n        /// circle().\n        const CIRCLE = 1 << 3;\n        /// ellipse().\n        const ELLIPSE = 1 << 4;\n        /// polygon().\n        const POLYGON = 1 << 5;\n        /// path().\n        const PATH = 1 << 6;\n        /// shape().\n        const SHAPE = 1 << 7;\n\n        /// All flags.\n        const ALL =\n            Self::INSET.bits() |\n            Self::XYWH.bits() |\n            Self::RECT.bits() |\n            Self::CIRCLE.bits() |\n            Self::ELLIPSE.bits() |\n            Self::POLYGON.bits() |\n            Self::PATH.bits() |\n            Self::SHAPE.bits();\n\n        /// For shape-outside.\n        const SHAPE_OUTSIDE =\n            Self::INSET.bits() |\n            Self::XYWH.bits() |\n            Self::RECT.bits() |\n            Self::CIRCLE.bits() |\n            Self::ELLIPSE.bits() |\n            Self::POLYGON.bits();\n    }\n}\n\n/// A helper for both clip-path and shape-outside parsing of shapes.\nfn parse_shape_or_box<'i, 't, R, ReferenceBox>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    to_shape: impl FnOnce(Box<BasicShape>, ReferenceBox) -> R,\n    to_reference_box: impl FnOnce(ReferenceBox) -> R,\n    flags: AllowedBasicShapes,\n) -> Result<R, ParseError<'i>>\nwhere\n    ReferenceBox: Default + Parse,\n{\n    let mut shape = None;\n    let mut ref_box = None;\n    loop {\n        if shape.is_none() {\n            shape = input\n                .try_parse(|i| BasicShape::parse(context, i, flags, ShapeType::Filled))\n                .ok();\n        }\n\n        if ref_box.is_none() {\n            ref_box = input.try_parse(|i| ReferenceBox::parse(context, i)).ok();\n            if ref_box.is_some() {\n                continue;\n            }\n        }\n        break;\n    }\n\n    if let Some(shp) = shape {\n        return Ok(to_shape(Box::new(shp), ref_box.unwrap_or_default()));\n    }\n\n    match ref_box {\n        Some(r) => Ok(to_reference_box(r)),\n        None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n    }\n}\n\nimpl Parse for ClipPath {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(ClipPath::None);\n        }\n\n        if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {\n            return Ok(ClipPath::Url(url));\n        }\n\n        parse_shape_or_box(\n            context,\n            input,\n            ClipPath::Shape,\n            ClipPath::Box,\n            AllowedBasicShapes::ALL,\n        )\n    }\n}\n\nimpl Parse for ShapeOutside {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Need to parse this here so that `Image::parse_with_cors_anonymous`\n        // doesn't parse it.\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(ShapeOutside::None);\n        }\n\n        if let Ok(image) = input.try_parse(|i| Image::parse_with_cors_anonymous(context, i)) {\n            debug_assert_ne!(image, Image::None);\n            return Ok(ShapeOutside::Image(image));\n        }\n\n        parse_shape_or_box(\n            context,\n            input,\n            ShapeOutside::Shape,\n            ShapeOutside::Box,\n            AllowedBasicShapes::SHAPE_OUTSIDE,\n        )\n    }\n}\n\nimpl BasicShape {\n    /// Parse with some parameters.\n    /// 1. The supported <basic-shape>.\n    /// 2. The type of shapes. Should we ignore fill-rule?\n    /// 3. The default value of `at <position>`.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        flags: AllowedBasicShapes,\n        shape_type: ShapeType,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let function = input.expect_function()?.clone();\n        input.parse_nested_block(move |i| {\n            match_ignore_ascii_case! { &function,\n                \"inset\" if flags.contains(AllowedBasicShapes::INSET) => {\n                    InsetRect::parse_function_arguments(context, i)\n                        .map(BasicShapeRect::Inset)\n                        .map(BasicShape::Rect)\n                },\n                \"xywh\" if flags.contains(AllowedBasicShapes::XYWH) => {\n                    Xywh::parse_function_arguments(context, i)\n                        .map(BasicShapeRect::Xywh)\n                        .map(BasicShape::Rect)\n                },\n                \"rect\" if flags.contains(AllowedBasicShapes::RECT) => {\n                    ShapeRectFunction::parse_function_arguments(context, i)\n                        .map(BasicShapeRect::Rect)\n                        .map(BasicShape::Rect)\n                },\n                \"circle\" if flags.contains(AllowedBasicShapes::CIRCLE) => {\n                    Circle::parse_function_arguments(context, i)\n                        .map(BasicShape::Circle)\n                },\n                \"ellipse\" if flags.contains(AllowedBasicShapes::ELLIPSE) => {\n                    Ellipse::parse_function_arguments(context, i)\n                        .map(BasicShape::Ellipse)\n                },\n                \"polygon\" if flags.contains(AllowedBasicShapes::POLYGON) => {\n                    Polygon::parse_function_arguments(context, i, shape_type)\n                        .map(BasicShape::Polygon)\n                },\n                \"path\" if flags.contains(AllowedBasicShapes::PATH) => {\n                    Path::parse_function_arguments(i, shape_type)\n                        .map(PathOrShapeFunction::Path)\n                        .map(BasicShape::PathOrShape)\n                },\n                \"shape\"\n                    if flags.contains(AllowedBasicShapes::SHAPE)\n                        && static_prefs::pref!(\"layout.css.basic-shape-shape.enabled\") =>\n                {\n                    generic::Shape::parse_function_arguments(context, i, shape_type)\n                        .map(PathOrShapeFunction::Shape)\n                        .map(BasicShape::PathOrShape)\n                },\n                _ => Err(location\n                    .new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),\n            }\n        })\n    }\n}\n\nimpl Parse for InsetRect {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"inset\")?;\n        input.parse_nested_block(|i| Self::parse_function_arguments(context, i))\n    }\n}\n\nfn parse_round<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<BorderRadius, ParseError<'i>> {\n    if input\n        .try_parse(|i| i.expect_ident_matching(\"round\"))\n        .is_ok()\n    {\n        return BorderRadius::parse(context, input);\n    }\n\n    Ok(BorderRadius::zero())\n}\n\nimpl InsetRect {\n    /// Parse the inner function arguments of `inset()`\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;\n        let round = parse_round(context, input)?;\n        Ok(generic::InsetRect { rect, round })\n    }\n}\n\nfn parse_at_position<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<GenericPositionOrAuto<Position>, ParseError<'i>> {\n    if input.try_parse(|i| i.expect_ident_matching(\"at\")).is_ok() {\n        Position::parse(context, input).map(GenericPositionOrAuto::Position)\n    } else {\n        Ok(GenericPositionOrAuto::Auto)\n    }\n}\n\nimpl Parse for Circle {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"circle\")?;\n        input.parse_nested_block(|i| Self::parse_function_arguments(context, i))\n    }\n}\n\nimpl Circle {\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let radius = input\n            .try_parse(|i| ShapeRadius::parse(context, i))\n            .unwrap_or_default();\n        let position = parse_at_position(context, input)?;\n\n        Ok(generic::Circle { radius, position })\n    }\n}\n\nimpl Parse for Ellipse {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"ellipse\")?;\n        input.parse_nested_block(|i| Self::parse_function_arguments(context, i))\n    }\n}\n\nimpl Ellipse {\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let (semiaxis_x, semiaxis_y) = input\n            .try_parse(|i| -> Result<_, ParseError> {\n                Ok((\n                    ShapeRadius::parse(context, i)?,\n                    ShapeRadius::parse(context, i)?,\n                ))\n            })\n            .unwrap_or_default();\n        let position = parse_at_position(context, input)?;\n\n        Ok(generic::Ellipse {\n            semiaxis_x,\n            semiaxis_y,\n            position,\n        })\n    }\n}\n\nfn parse_fill_rule<'i, 't>(\n    input: &mut Parser<'i, 't>,\n    shape_type: ShapeType,\n    expect_comma: bool,\n) -> FillRule {\n    match shape_type {\n        // Per [1] and [2], we ignore `<fill-rule>` for outline shapes, so always use a default\n        // value.\n        // [1] https://github.com/w3c/csswg-drafts/issues/3468\n        // [2] https://github.com/w3c/csswg-drafts/issues/7390\n        //\n        // Also, per [3] and [4], we would like the ignore `<file-rule>` from outline shapes, e.g.\n        // offset-path, which means we don't parse it when setting `ShapeType::Outline`.\n        // This should be web compatible because the shipped \"offset-path:path()\" doesn't have\n        // `<fill-rule>` and \"offset-path:polygon()\" is a new feature and still behind the\n        // preference.\n        // [3] https://github.com/w3c/fxtf-drafts/issues/512#issuecomment-1545393321\n        // [4] https://github.com/w3c/fxtf-drafts/issues/512#issuecomment-1555330929\n        ShapeType::Outline => Default::default(),\n        ShapeType::Filled => input\n            .try_parse(|i| -> Result<_, ParseError> {\n                let fill = FillRule::parse(i)?;\n                if expect_comma {\n                    i.expect_comma()?;\n                }\n                Ok(fill)\n            })\n            .unwrap_or_default(),\n    }\n}\n\nimpl Parse for Polygon {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"polygon\")?;\n        input.parse_nested_block(|i| Self::parse_function_arguments(context, i, ShapeType::Filled))\n    }\n}\n\nimpl Polygon {\n    /// Parse the inner arguments of a `polygon` function.\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        shape_type: ShapeType,\n    ) -> Result<Self, ParseError<'i>> {\n        let fill = parse_fill_rule(input, shape_type, true /* has comma */);\n        let coordinates = input\n            .parse_comma_separated(|i| {\n                Ok(PolygonCoord(\n                    LengthPercentage::parse(context, i)?,\n                    LengthPercentage::parse(context, i)?,\n                ))\n            })?\n            .into();\n\n        Ok(Polygon { fill, coordinates })\n    }\n}\n\nimpl Path {\n    /// Parse the inner arguments of a `path` function.\n    fn parse_function_arguments<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        shape_type: ShapeType,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::specified::svg_path::AllowEmpty;\n\n        let fill = parse_fill_rule(input, shape_type, true /* has comma */);\n        let path = SVGPathData::parse(input, AllowEmpty::No)?;\n        Ok(Path { fill, path })\n    }\n}\n\nfn round_to_css<W>(round: &BorderRadius, dest: &mut CssWriter<W>) -> fmt::Result\nwhere\n    W: Write,\n{\n    if !round.is_zero() {\n        dest.write_str(\" round \")?;\n        round.to_css(dest)?;\n    }\n    Ok(())\n}\n\nimpl ToCss for Xywh {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.x.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.y.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.width.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.height.to_css(dest)?;\n        round_to_css(&self.round, dest)\n    }\n}\n\nimpl Xywh {\n    /// Parse the inner function arguments of `xywh()`.\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let x = LengthPercentage::parse(context, input)?;\n        let y = LengthPercentage::parse(context, input)?;\n        let width = NonNegativeLengthPercentage::parse(context, input)?;\n        let height = NonNegativeLengthPercentage::parse(context, input)?;\n        let round = parse_round(context, input)?;\n        Ok(Xywh {\n            x,\n            y,\n            width,\n            height,\n            round,\n        })\n    }\n}\n\nimpl ToCss for ShapeRectFunction {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.rect.0.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.rect.1.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.rect.2.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.rect.3.to_css(dest)?;\n        round_to_css(&self.round, dest)\n    }\n}\n\nimpl ShapeRectFunction {\n    /// Parse the inner function arguments of `rect()`.\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let rect = Rect::parse_all_components_with(context, input, LengthPercentageOrAuto::parse)?;\n        let round = parse_round(context, input)?;\n        Ok(ShapeRectFunction { rect, round })\n    }\n}\n\nimpl ToComputedValue for BasicShapeRect {\n    type ComputedValue = ComputedInsetRect;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        use crate::values::computed::LengthPercentage;\n        use crate::values::computed::LengthPercentageOrAuto;\n        use style_traits::values::specified::AllowedNumericType;\n\n        match self {\n            Self::Inset(ref inset) => inset.to_computed_value(context),\n            Self::Xywh(ref xywh) => {\n                // Given `xywh(x y w h)`, construct the equivalent inset() function,\n                // `inset(y calc(100% - x - w) calc(100% - y - h) x)`.\n                //\n                // https://drafts.csswg.org/css-shapes-1/#basic-shape-computed-values\n                // https://github.com/w3c/csswg-drafts/issues/9053\n                let x = xywh.x.to_computed_value(context);\n                let y = xywh.y.to_computed_value(context);\n                let w = xywh.width.to_computed_value(context);\n                let h = xywh.height.to_computed_value(context);\n                // calc(100% - x - w).\n                let right = LengthPercentage::hundred_percent_minus_list(\n                    &[&x, &w.0],\n                    AllowedNumericType::All,\n                );\n                // calc(100% - y - h).\n                let bottom = LengthPercentage::hundred_percent_minus_list(\n                    &[&y, &h.0],\n                    AllowedNumericType::All,\n                );\n\n                ComputedInsetRect {\n                    rect: Rect::new(y, right, bottom, x),\n                    round: xywh.round.to_computed_value(context),\n                }\n            },\n            Self::Rect(ref rect) => {\n                // Given `rect(t r b l)`, the equivalent function is\n                // `inset(t calc(100% - r) calc(100% - b) l)`.\n                //\n                // https://drafts.csswg.org/css-shapes-1/#basic-shape-computed-values\n                fn compute_top_or_left(v: LengthPercentageOrAuto) -> LengthPercentage {\n                    match v {\n                        // it’s equivalent to 0% as the first (top) or fourth (left) value.\n                        // https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect\n                        LengthPercentageOrAuto::Auto => LengthPercentage::zero_percent(),\n                        LengthPercentageOrAuto::LengthPercentage(lp) => lp,\n                    }\n                }\n                fn compute_bottom_or_right(v: LengthPercentageOrAuto) -> LengthPercentage {\n                    match v {\n                        // It's equivalent to 100% as the second (right) or third (bottom) value.\n                        // So calc(100% - 100%) = 0%.\n                        // https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-rect\n                        LengthPercentageOrAuto::Auto => LengthPercentage::zero_percent(),\n                        LengthPercentageOrAuto::LengthPercentage(lp) => {\n                            LengthPercentage::hundred_percent_minus(lp, AllowedNumericType::All)\n                        },\n                    }\n                }\n\n                let round = rect.round.to_computed_value(context);\n                let rect = rect.rect.to_computed_value(context);\n                let rect = Rect::new(\n                    compute_top_or_left(rect.0),\n                    compute_bottom_or_right(rect.1),\n                    compute_bottom_or_right(rect.2),\n                    compute_top_or_left(rect.3),\n                );\n\n                ComputedInsetRect { rect, round }\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::Inset(ToComputedValue::from_computed_value(computed))\n    }\n}\n\nimpl generic::Shape<Angle, Position, LengthPercentage> {\n    /// Parse the inner arguments of a `shape` function.\n    /// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        shape_type: ShapeType,\n    ) -> Result<Self, ParseError<'i>> {\n        let fill = parse_fill_rule(input, shape_type, false /* no following comma */);\n\n        let mut first = true;\n        let commands = input.parse_comma_separated(|i| {\n            if first {\n                first = false;\n\n                // The starting point for the first shape-command. It adds an initial absolute\n                // moveto to the list of path data commands, with the <coordinate-pair> measured\n                // from the top-left corner of the reference\n                i.expect_ident_matching(\"from\")?;\n                Ok(ShapeCommand::Move {\n                    point: generic::CommandEndPoint::parse_endpoint_as_abs(context, i)?,\n                })\n            } else {\n                // The further path data commands.\n                ShapeCommand::parse(context, i)\n            }\n        })?;\n\n        // We must have one starting point and at least one following <shape-command>.\n        if commands.len() < 2 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(Self {\n            fill,\n            commands: commands.into(),\n        })\n    }\n}\n\nimpl Parse for ShapeCommand {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::generics::basic_shape::{\n            ArcRadii, ArcSize, ArcSweep, AxisEndPoint, CommandEndPoint, ControlPoint,\n        };\n\n        // <shape-command> = <move-command> | <line-command> | <hv-line-command> |\n        //                   <curve-command> | <smooth-command> | <arc-command> | close\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"close\" => Self::Close,\n            \"move\" => {\n                let point = CommandEndPoint::parse(context, input)?;\n                Self::Move { point }\n            },\n            \"line\" => {\n                let point = CommandEndPoint::parse(context, input)?;\n                Self::Line { point }\n            },\n            \"hline\" => {\n                let x = AxisEndPoint::parse_hline(context, input)?;\n                Self::HLine { x }\n            },\n            \"vline\" => {\n                let y = AxisEndPoint::parse_vline(context, input)?;\n                Self::VLine { y }\n            },\n            \"curve\" => {\n                let point = CommandEndPoint::parse(context, input)?;\n                input.expect_ident_matching(\"with\")?;\n                let control1 = ControlPoint::parse(context, input, point.is_abs())?;\n                if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n                    let control2 = ControlPoint::parse(context, input, point.is_abs())?;\n                    Self::CubicCurve {\n                        point,\n                        control1,\n                        control2,\n                    }\n                } else {\n                    Self::QuadCurve {\n                        point,\n                        control1,\n                    }\n                }\n            },\n            \"smooth\" => {\n                let point = CommandEndPoint::parse(context, input)?;\n                if input.try_parse(|i| i.expect_ident_matching(\"with\")).is_ok() {\n                    let control2 = ControlPoint::parse(context, input, point.is_abs())?;\n                    Self::SmoothCubic {\n                        point,\n                        control2,\n                    }\n                } else {\n                    Self::SmoothQuad { point }\n                }\n            },\n            \"arc\" => {\n                let point = CommandEndPoint::parse(context, input)?;\n                input.expect_ident_matching(\"of\")?;\n                let rx = LengthPercentage::parse(context, input)?;\n                let ry = input.try_parse(|i| LengthPercentage::parse(context, i)).ok();\n                let radii = ArcRadii { rx, ry: ry.into() };\n\n                // [<arc-sweep> || <arc-size> || rotate <angle>]?\n                let mut arc_sweep = None;\n                let mut arc_size = None;\n                let mut rotate = None;\n                loop {\n                    if arc_sweep.is_none() {\n                        arc_sweep = input.try_parse(ArcSweep::parse).ok();\n                    }\n\n                    if arc_size.is_none() {\n                        arc_size = input.try_parse(ArcSize::parse).ok();\n                        if arc_size.is_some() {\n                            continue;\n                        }\n                    }\n\n                    if rotate.is_none()\n                        && input\n                            .try_parse(|i| i.expect_ident_matching(\"rotate\"))\n                            .is_ok()\n                    {\n                        rotate = Some(Angle::parse(context, input)?);\n                        continue;\n                    }\n                    break;\n                }\n                Self::Arc {\n                    point,\n                    radii,\n                    arc_sweep: arc_sweep.unwrap_or(ArcSweep::Ccw),\n                    arc_size: arc_size.unwrap_or(ArcSize::Small),\n                    rotate: rotate.unwrap_or(Angle::zero()),\n                }\n            },\n        })\n    }\n}\n\nimpl Parse for generic::CoordinatePair<LengthPercentage> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let x = LengthPercentage::parse(context, input)?;\n        let y = LengthPercentage::parse(context, input)?;\n        Ok(Self::new(x, y))\n    }\n}\n\nimpl generic::ControlPoint<Position, LengthPercentage> {\n    /// Parse <control-point> = [ <position> | <relative-control-point> ]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        is_end_point_abs: bool,\n    ) -> Result<Self, ParseError<'i>> {\n        use generic::ControlReference;\n        let coord = input.try_parse(|i| generic::CoordinatePair::parse(context, i));\n\n        // Parse <position>\n        if is_end_point_abs && coord.is_err() {\n            let pos = Position::parse(context, input)?;\n            return Ok(Self::Absolute(pos));\n        }\n\n        // Parse <relative-control-point> = <coordinate-pair> [from [ start | end | origin ]]?\n        let coord = coord?;\n        let mut reference = if is_end_point_abs {\n            ControlReference::Origin\n        } else {\n            ControlReference::Start\n        };\n        if input.try_parse(|i| i.expect_ident_matching(\"from\")).is_ok() {\n            reference = ControlReference::parse(input)?;\n        }\n\n        Ok(Self::Relative(generic::RelativeControlPoint {\n            coord,\n            reference,\n        }))\n    }\n}\n\nimpl Parse for generic::CommandEndPoint<Position, LengthPercentage> {\n    /// Parse <command-end-point> = to <position> | by <coordinate-pair>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if ByTo::parse(input)?.is_abs() {\n            Self::parse_endpoint_as_abs(context, input)\n        } else {\n            let point = generic::CoordinatePair::parse(context, input)?;\n            Ok(Self::ByCoordinate(point))\n        }\n    }\n}\n\nimpl generic::CommandEndPoint<Position, LengthPercentage> {\n    /// Parse <command-end-point> = to <position>\n    fn parse_endpoint_as_abs<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let point = Position::parse(context, input)?;\n        Ok(generic::CommandEndPoint::ToPosition(point))\n    }\n}\n\nimpl generic::AxisEndPoint<LengthPercentage> {\n    /// Parse <horizontal-line-command>\n    pub fn parse_hline<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use cssparser::Token;\n        use generic::{AxisPosition, AxisPositionKeyword};\n\n        // If the command is relative, parse for <length-percentage> only.\n        if !ByTo::parse(input)?.is_abs() {\n            return Ok(Self::ByCoordinate(LengthPercentage::parse(context, input)?));\n        }\n\n        let x = AxisPosition::parse(context, input)?;\n        if let AxisPosition::Keyword(\n            _word @ (AxisPositionKeyword::Top\n            | AxisPositionKeyword::Bottom\n            | AxisPositionKeyword::YStart\n            | AxisPositionKeyword::YEnd),\n        ) = &x\n        {\n            let location = input.current_source_location();\n            let token = Token::Ident(x.to_css_string().into());\n            return Err(location.new_unexpected_token_error(token));\n        }\n        Ok(Self::ToPosition(x))\n    }\n\n    /// Parse <vertical-line-command>\n    pub fn parse_vline<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use cssparser::Token;\n        use generic::{AxisPosition, AxisPositionKeyword};\n\n        // If the command is relative, parse for <length-percentage> only.\n        if !ByTo::parse(input)?.is_abs() {\n            return Ok(Self::ByCoordinate(LengthPercentage::parse(context, input)?));\n        }\n\n        let y = AxisPosition::parse(context, input)?;\n        if let AxisPosition::Keyword(\n            _word @ (AxisPositionKeyword::Left\n            | AxisPositionKeyword::Right\n            | AxisPositionKeyword::XStart\n            | AxisPositionKeyword::XEnd),\n        ) = &y\n        {\n            // Return an error if we parsed a different keyword.\n            let location = input.current_source_location();\n            let token = Token::Ident(y.to_css_string().into());\n            return Err(location.new_unexpected_token_error(token));\n        }\n        Ok(Self::ToPosition(y))\n    }\n}\n\nimpl ToComputedValue for generic::AxisPosition<LengthPercentage> {\n    type ComputedValue = generic::AxisPosition<ComputedLengthPercentage>;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match self {\n            Self::LengthPercent(lp) => {\n                Self::ComputedValue::LengthPercent(lp.to_computed_value(context))\n            },\n            Self::Keyword(word) => {\n                let lp = LengthPercentage::Percentage(word.as_percentage());\n                Self::ComputedValue::LengthPercent(lp.to_computed_value(context))\n            },\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match computed {\n            Self::ComputedValue::LengthPercent(lp) => {\n                Self::LengthPercent(LengthPercentage::from_computed_value(lp))\n            },\n            _ => unreachable!(\"Invalid state: computed value cannot be a keyword.\"),\n        }\n    }\n}\n\nimpl ToComputedValue for generic::AxisPosition<CSSFloat> {\n    type ComputedValue = Self;\n\n    fn to_computed_value(&self, _context: &Context) -> Self {\n        *self\n    }\n\n    fn from_computed_value(computed: &Self) -> Self {\n        *computed\n    }\n}\n\n/// This determines whether the command is absolutely or relatively positioned.\n/// https://drafts.csswg.org/css-shapes-1/#typedef-shape-command-end-point\n#[derive(Clone, Copy, Debug, Parse, PartialEq)]\nenum ByTo {\n    /// Command is relative to the command’s starting point.\n    By,\n    /// Command is relative to the top-left corner of the reference box.\n    To,\n}\n\nimpl ByTo {\n    /// Return true if it is absolute, i.e. it is To.\n    #[inline]\n    pub fn is_abs(&self) -> bool {\n        matches!(self, ByTo::To)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/border.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values related to borders.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::border::BorderSideWidth as ComputedBorderSideWidth;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::border::{\n    GenericBorderCornerRadius, GenericBorderImageSideWidth, GenericBorderImageSlice,\n    GenericBorderRadius, GenericBorderSpacing,\n};\nuse crate::values::generics::rect::Rect;\nuse crate::values::generics::size::Size2D;\nuse crate::values::specified::length::{Length, NonNegativeLength, NonNegativeLengthPercentage};\nuse crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};\nuse crate::Zero;\nuse app_units::Au;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, ToCss};\n\n/// A specified value for a single side of a `border-style` property.\n///\n/// The order here corresponds to the integer values from the border conflict\n/// resolution rules in CSS 2.1 § 17.6.2.1. Higher values override lower values.\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Ord,\n    Parse,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum BorderStyle {\n    Hidden,\n    None,\n    Inset,\n    Groove,\n    Outset,\n    Ridge,\n    Dotted,\n    Dashed,\n    Solid,\n    Double,\n}\n\nimpl BorderStyle {\n    /// Whether this border style is either none or hidden.\n    #[inline]\n    pub fn none_or_hidden(&self) -> bool {\n        matches!(*self, BorderStyle::None | BorderStyle::Hidden)\n    }\n}\n\n/// A specified value for the `border-image-width` property.\npub type BorderImageWidth = Rect<BorderImageSideWidth>;\n\n/// A specified value for a single side of a `border-image-width` property.\npub type BorderImageSideWidth =\n    GenericBorderImageSideWidth<NonNegativeLengthPercentage, NonNegativeNumber>;\n\n/// A specified value for the `border-image-slice` property.\npub type BorderImageSlice = GenericBorderImageSlice<NonNegativeNumberOrPercentage>;\n\n/// A specified value for the `border-radius` property.\npub type BorderRadius = GenericBorderRadius<NonNegativeLengthPercentage>;\n\n/// A specified value for the `border-*-radius` longhand properties.\npub type BorderCornerRadius = GenericBorderCornerRadius<NonNegativeLengthPercentage>;\n\n/// A specified value for the `border-spacing` longhand properties.\npub type BorderSpacing = GenericBorderSpacing<NonNegativeLength>;\n\nimpl BorderImageSlice {\n    /// Returns the `100%` value.\n    #[inline]\n    pub fn hundred_percent() -> Self {\n        GenericBorderImageSlice {\n            offsets: Rect::all(NonNegativeNumberOrPercentage::hundred_percent()),\n            fill: false,\n        }\n    }\n}\n\n/// https://drafts.csswg.org/css-backgrounds-3/#typedef-line-width\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub enum LineWidth {\n    /// `thin`\n    Thin,\n    /// `medium`\n    Medium,\n    /// `thick`\n    Thick,\n    /// `<length>`\n    Length(NonNegativeLength),\n}\n\nimpl LineWidth {\n    /// Returns the `0px` value.\n    #[inline]\n    pub fn zero() -> Self {\n        Self::Length(NonNegativeLength::zero())\n    }\n\n    fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(length) =\n            input.try_parse(|i| NonNegativeLength::parse_quirky(context, i, allow_quirks))\n        {\n            return Ok(Self::Length(length));\n        }\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"thin\" => Self::Thin,\n            \"medium\" => Self::Medium,\n            \"thick\" => Self::Thick,\n        })\n    }\n}\n\nimpl Parse for LineWidth {\n    fn parse<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl ToComputedValue for LineWidth {\n    type ComputedValue = Au;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            // https://drafts.csswg.org/css-backgrounds-3/#line-width\n            Self::Thin => Au::from_px(1),\n            Self::Medium => Au::from_px(3),\n            Self::Thick => Au::from_px(5),\n            Self::Length(ref length) => Au::from_f32_px(length.to_computed_value(context).px()),\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::Length(NonNegativeLength::from_px(computed.to_f32_px()))\n    }\n}\n\n/// A specified value for a single side of the `border-width` property. The difference between this\n/// and LineWidth is whether we snap to device pixels or not.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub struct BorderSideWidth(LineWidth);\n\nimpl BorderSideWidth {\n    /// Returns the `medium` value.\n    pub fn medium() -> Self {\n        Self(LineWidth::Medium)\n    }\n\n    /// Returns a bare px value from the argument.\n    pub fn from_px(px: f32) -> Self {\n        Self(LineWidth::Length(Length::from_px(px).into()))\n    }\n\n    /// Parses, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self(LineWidth::parse_quirky(context, input, allow_quirks)?))\n    }\n}\n\nimpl Parse for BorderSideWidth {\n    fn parse<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\n// https://drafts.csswg.org/css-values-4/#snap-a-length-as-a-border-width\nfn snap_as_border_width(len: Au, context: &Context) -> Au {\n    debug_assert!(len >= Au(0));\n\n    // Round `width` down to the nearest device pixel, but any non-zero value that would round\n    // down to zero is clamped to 1 device pixel.\n    if len == Au(0) {\n        return len;\n    }\n\n    let au_per_dev_px = context.device().app_units_per_device_pixel();\n    std::cmp::max(Au(au_per_dev_px), Au(len.0 / au_per_dev_px * au_per_dev_px))\n}\n\nimpl ToComputedValue for BorderSideWidth {\n    type ComputedValue = ComputedBorderSideWidth;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        ComputedBorderSideWidth(snap_as_border_width(\n            self.0.to_computed_value(context),\n            context,\n        ))\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self(LineWidth::from_computed_value(&computed.0))\n    }\n}\n\n/// A specified value for outline-offset.\n#[derive(\n    Clone, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub struct BorderSideOffset(Length);\n\nimpl ToComputedValue for BorderSideOffset {\n    type ComputedValue = Au;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        let offset = Au::from_f32_px(self.0.to_computed_value(context).px());\n        let should_snap = match static_prefs::pref!(\"layout.css.outline-offset.snapping\") {\n            1 => true,\n            2 => context.device().chrome_rules_enabled_for_document(),\n            _ => false,\n        };\n        if !should_snap {\n            return offset;\n        }\n        if offset < Au(0) {\n            -snap_as_border_width(-offset, context)\n        } else {\n            snap_as_border_width(offset, context)\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Au) -> Self {\n        Self(Length::from_px(computed.to_f32_px()))\n    }\n}\n\nimpl BorderImageSideWidth {\n    /// Returns `1`.\n    #[inline]\n    pub fn one() -> Self {\n        GenericBorderImageSideWidth::Number(NonNegativeNumber::new(1.))\n    }\n}\n\nimpl Parse for BorderImageSlice {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut fill = input.try_parse(|i| i.expect_ident_matching(\"fill\")).is_ok();\n        let offsets = Rect::parse_with(context, input, NonNegativeNumberOrPercentage::parse)?;\n        if !fill {\n            fill = input.try_parse(|i| i.expect_ident_matching(\"fill\")).is_ok();\n        }\n        Ok(GenericBorderImageSlice { offsets, fill })\n    }\n}\n\nimpl Parse for BorderRadius {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let widths = Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?;\n        let heights = if input.try_parse(|i| i.expect_delim('/')).is_ok() {\n            Rect::parse_with(context, input, NonNegativeLengthPercentage::parse)?\n        } else {\n            widths.clone()\n        };\n\n        Ok(GenericBorderRadius {\n            top_left: BorderCornerRadius::new(widths.0, heights.0),\n            top_right: BorderCornerRadius::new(widths.1, heights.1),\n            bottom_right: BorderCornerRadius::new(widths.2, heights.2),\n            bottom_left: BorderCornerRadius::new(widths.3, heights.3),\n        })\n    }\n}\n\nimpl Parse for BorderCornerRadius {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)\n            .map(GenericBorderCornerRadius)\n    }\n}\n\nimpl Parse for BorderSpacing {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Size2D::parse_with(context, input, |context, input| {\n            NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes)\n        })\n        .map(GenericBorderSpacing)\n    }\n}\n\n/// A single border-image-repeat keyword.\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum BorderImageRepeatKeyword {\n    Stretch,\n    Repeat,\n    Round,\n    Space,\n}\n\n/// The specified value for the `border-image-repeat` property.\n///\n/// https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct BorderImageRepeat(pub BorderImageRepeatKeyword, pub BorderImageRepeatKeyword);\n\nimpl ToCss for BorderImageRepeat {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.0.to_css(dest)?;\n        if self.0 != self.1 {\n            dest.write_char(' ')?;\n            self.1.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl BorderImageRepeat {\n    /// Returns the `stretch` value.\n    #[inline]\n    pub fn stretch() -> Self {\n        BorderImageRepeat(\n            BorderImageRepeatKeyword::Stretch,\n            BorderImageRepeatKeyword::Stretch,\n        )\n    }\n}\n\nimpl Parse for BorderImageRepeat {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let horizontal = BorderImageRepeatKeyword::parse(input)?;\n        let vertical = input.try_parse(BorderImageRepeatKeyword::parse).ok();\n        Ok(BorderImageRepeat(\n            horizontal,\n            vertical.unwrap_or(horizontal),\n        ))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/box.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for box properties.\n\nuse crate::derives::*;\npub use crate::logical_geometry::WritingModeProperty;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::{LonghandId, PropertyDeclarationId, PropertyId};\nuse crate::values::generics::box_::{\n    BaselineShiftKeyword, GenericBaselineShift, GenericContainIntrinsicSize, GenericLineClamp,\n    GenericOverflowClipMargin, GenericPerspective, OverflowClipMarginBox,\n};\nuse crate::values::specified::length::{LengthPercentage, NonNegativeLength};\nuse crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercentage};\nuse crate::values::CustomIdent;\nuse cssparser::Parser;\nuse num_traits::FromPrimitive;\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, KeywordsCollectFn, ParseError};\nuse style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};\n\n#[cfg(not(feature = \"servo\"))]\nfn grid_enabled() -> bool {\n    true\n}\n\n#[cfg(feature = \"servo\")]\nfn grid_enabled() -> bool {\n    static_prefs::pref!(\"layout.grid.enabled\")\n}\n\n#[inline]\nfn appearance_base_enabled(_context: &ParserContext) -> bool {\n    static_prefs::pref!(\"layout.css.appearance-base.enabled\")\n}\n\n#[inline]\nfn appearance_base_select_enabled(_context: &ParserContext) -> bool {\n    static_prefs::pref!(\"dom.select.customizable_select.enabled\")\n}\n\n/// The specified value of `overflow-clip-margin`.\npub type OverflowClipMargin = GenericOverflowClipMargin<NonNegativeLength>;\n\nimpl Parse for OverflowClipMargin {\n    // <visual-box> || <length [0,∞]>\n    fn parse<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::Zero;\n        let mut offset = None;\n        let mut visual_box = None;\n        loop {\n            if offset.is_none() {\n                offset = input\n                    .try_parse(|i| NonNegativeLength::parse(context, i))\n                    .ok();\n            }\n            if visual_box.is_none() {\n                visual_box = input.try_parse(OverflowClipMarginBox::parse).ok();\n                if visual_box.is_some() {\n                    continue;\n                }\n            }\n            break;\n        }\n        if offset.is_none() && visual_box.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(Self {\n            offset: offset.unwrap_or_else(NonNegativeLength::zero),\n            visual_box: visual_box.unwrap_or(OverflowClipMarginBox::PaddingBox),\n        })\n    }\n}\n\n/// Defines an element’s display type, which consists of\n/// the two basic qualities of how an element generates boxes\n/// <https://drafts.csswg.org/css-display/#propdef-display>\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum DisplayOutside {\n    None = 0,\n    Inline,\n    Block,\n    TableCaption,\n    InternalTable,\n    #[cfg(feature = \"gecko\")]\n    InternalRuby,\n}\n\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum DisplayInside {\n    None = 0,\n    Contents,\n    Flow,\n    FlowRoot,\n    Flex,\n    Grid,\n    Table,\n    TableRowGroup,\n    TableColumn,\n    TableColumnGroup,\n    TableHeaderGroup,\n    TableFooterGroup,\n    TableRow,\n    TableCell,\n    #[cfg(feature = \"gecko\")]\n    Ruby,\n    #[cfg(feature = \"gecko\")]\n    RubyBase,\n    #[cfg(feature = \"gecko\")]\n    RubyBaseContainer,\n    #[cfg(feature = \"gecko\")]\n    RubyText,\n    #[cfg(feature = \"gecko\")]\n    RubyTextContainer,\n    #[cfg(feature = \"gecko\")]\n    WebkitBox,\n}\n\nimpl DisplayInside {\n    fn is_valid_for_list_item(self) -> bool {\n        match self {\n            DisplayInside::Flow => true,\n            #[cfg(feature = \"gecko\")]\n            DisplayInside::FlowRoot => true,\n            _ => false,\n        }\n    }\n\n    /// https://drafts.csswg.org/css-display/#inside-model:\n    ///     If <display-outside> is omitted, the element’s outside display type defaults to block\n    ///     — except for ruby, which defaults to inline.\n    fn default_display_outside(self) -> DisplayOutside {\n        match self {\n            #[cfg(feature = \"gecko\")]\n            DisplayInside::Ruby => DisplayOutside::Inline,\n            _ => DisplayOutside::Block,\n        }\n    }\n}\n\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct Display(u16);\n\n/// Gecko-only impl block for Display (shared stuff later in this file):\n#[allow(missing_docs)]\n#[allow(non_upper_case_globals)]\nimpl Display {\n    // Our u16 bits are used as follows: LOOOOOOOIIIIIIII\n    pub const LIST_ITEM_MASK: u16 = 0b1000000000000000;\n    pub const OUTSIDE_MASK: u16 = 0b0111111100000000;\n    pub const INSIDE_MASK: u16 = 0b0000000011111111;\n    pub const OUTSIDE_SHIFT: u16 = 8;\n\n    /// https://drafts.csswg.org/css-display/#the-display-properties\n    /// ::new() inlined so cbindgen can use it\n    pub const None: Self =\n        Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16);\n    pub const Contents: Self = Self(\n        ((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16,\n    );\n    pub const Inline: Self =\n        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);\n    pub const InlineBlock: Self = Self(\n        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,\n    );\n    pub const Block: Self =\n        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16);\n    #[cfg(feature = \"gecko\")]\n    pub const FlowRoot: Self = Self(\n        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::FlowRoot as u16,\n    );\n    pub const Flex: Self =\n        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);\n    pub const InlineFlex: Self =\n        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flex as u16);\n    pub const Grid: Self =\n        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);\n    pub const InlineGrid: Self =\n        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);\n    pub const Table: Self =\n        Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16);\n    pub const InlineTable: Self = Self(\n        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16,\n    );\n    pub const TableCaption: Self = Self(\n        ((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16,\n    );\n    #[cfg(feature = \"gecko\")]\n    pub const Ruby: Self =\n        Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Ruby as u16);\n    #[cfg(feature = \"gecko\")]\n    pub const WebkitBox: Self = Self(\n        ((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,\n    );\n    #[cfg(feature = \"gecko\")]\n    pub const WebkitInlineBox: Self = Self(\n        ((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::WebkitBox as u16,\n    );\n\n    // Internal table boxes.\n\n    pub const TableRowGroup: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableRowGroup as u16,\n    );\n    pub const TableHeaderGroup: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableHeaderGroup as u16,\n    );\n    pub const TableFooterGroup: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableFooterGroup as u16,\n    );\n    pub const TableColumn: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableColumn as u16,\n    );\n    pub const TableColumnGroup: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableColumnGroup as u16,\n    );\n    pub const TableRow: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableRow as u16,\n    );\n    pub const TableCell: Self = Self(\n        ((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::TableCell as u16,\n    );\n\n    /// Internal ruby boxes.\n    #[cfg(feature = \"gecko\")]\n    pub const RubyBase: Self = Self(\n        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::RubyBase as u16,\n    );\n    #[cfg(feature = \"gecko\")]\n    pub const RubyBaseContainer: Self = Self(\n        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::RubyBaseContainer as u16,\n    );\n    #[cfg(feature = \"gecko\")]\n    pub const RubyText: Self = Self(\n        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::RubyText as u16,\n    );\n    #[cfg(feature = \"gecko\")]\n    pub const RubyTextContainer: Self = Self(\n        ((DisplayOutside::InternalRuby as u16) << Self::OUTSIDE_SHIFT)\n            | DisplayInside::RubyTextContainer as u16,\n    );\n\n    /// Make a raw display value from <display-outside> and <display-inside> values.\n    #[inline]\n    const fn new(outside: DisplayOutside, inside: DisplayInside) -> Self {\n        Self((outside as u16) << Self::OUTSIDE_SHIFT | inside as u16)\n    }\n\n    /// Make a display enum value from <display-outside> and <display-inside> values.\n    #[inline]\n    fn from3(outside: DisplayOutside, inside: DisplayInside, list_item: bool) -> Self {\n        let v = Self::new(outside, inside);\n        if !list_item {\n            return v;\n        }\n        Self(v.0 | Self::LIST_ITEM_MASK)\n    }\n\n    /// Accessor for the <display-inside> value.\n    #[inline]\n    pub fn inside(&self) -> DisplayInside {\n        DisplayInside::from_u16(self.0 & Self::INSIDE_MASK).unwrap()\n    }\n\n    /// Accessor for the <display-outside> value.\n    #[inline]\n    pub fn outside(&self) -> DisplayOutside {\n        DisplayOutside::from_u16((self.0 & Self::OUTSIDE_MASK) >> Self::OUTSIDE_SHIFT).unwrap()\n    }\n\n    /// Returns the raw underlying u16 value.\n    #[inline]\n    pub const fn to_u16(&self) -> u16 {\n        self.0\n    }\n\n    /// Whether this is `display: inline` (or `inline list-item`).\n    #[inline]\n    pub fn is_inline_flow(&self) -> bool {\n        self.outside() == DisplayOutside::Inline && self.inside() == DisplayInside::Flow\n    }\n\n    /// Returns whether this `display` value is some kind of list-item.\n    #[inline]\n    pub const fn is_list_item(&self) -> bool {\n        (self.0 & Self::LIST_ITEM_MASK) != 0\n    }\n\n    /// Returns whether this `display` value is a ruby level container.\n    pub fn is_ruby_level_container(&self) -> bool {\n        match *self {\n            #[cfg(feature = \"gecko\")]\n            Display::RubyBaseContainer | Display::RubyTextContainer => true,\n            _ => false,\n        }\n    }\n\n    /// Returns whether this `display` value is one of the types for ruby.\n    pub fn is_ruby_type(&self) -> bool {\n        match self.inside() {\n            #[cfg(feature = \"gecko\")]\n            DisplayInside::Ruby\n            | DisplayInside::RubyBase\n            | DisplayInside::RubyText\n            | DisplayInside::RubyBaseContainer\n            | DisplayInside::RubyTextContainer => true,\n            _ => false,\n        }\n    }\n}\n\n/// Shared Display impl for both Gecko and Servo.\nimpl Display {\n    /// The initial display value.\n    #[inline]\n    pub fn inline() -> Self {\n        Display::Inline\n    }\n\n    /// Returns whether this `display` value is the display of a flex or\n    /// grid container.\n    ///\n    /// This is used to implement various style fixups.\n    pub fn is_item_container(&self) -> bool {\n        match self.inside() {\n            DisplayInside::Flex => true,\n            DisplayInside::Grid => true,\n            _ => false,\n        }\n    }\n\n    /// Returns whether an element with this display type is a line\n    /// participant, which means it may lay its children on the same\n    /// line as itself.\n    pub fn is_line_participant(&self) -> bool {\n        if self.is_inline_flow() {\n            return true;\n        }\n        match *self {\n            #[cfg(feature = \"gecko\")]\n            Display::Contents | Display::Ruby | Display::RubyBaseContainer => true,\n            _ => false,\n        }\n    }\n\n    /// Convert this display into an equivalent block display.\n    ///\n    /// Also used for :root style adjustments.\n    pub fn equivalent_block_display(&self, is_root_element: bool) -> Self {\n        // Special handling for `contents` and `list-item`s on the root element.\n        if is_root_element && (self.is_contents() || self.is_list_item()) {\n            return Display::Block;\n        }\n\n        match self.outside() {\n            DisplayOutside::Inline => {\n                let inside = match self.inside() {\n                    // `inline-block` blockifies to `block` rather than\n                    // `flow-root`, for legacy reasons.\n                    DisplayInside::FlowRoot => DisplayInside::Flow,\n                    inside => inside,\n                };\n                Display::from3(DisplayOutside::Block, inside, self.is_list_item())\n            },\n            DisplayOutside::Block | DisplayOutside::None => *self,\n            _ => Display::Block,\n        }\n    }\n\n    /// Convert this display into an equivalent inline-outside display.\n    /// https://drafts.csswg.org/css-display/#inlinify\n    #[cfg(feature = \"gecko\")]\n    pub fn inlinify(&self) -> Self {\n        match self.outside() {\n            DisplayOutside::Block => {\n                let inside = match self.inside() {\n                    // `display: block` inlinifies to `display: inline-block`,\n                    // rather than `inline`, for legacy reasons.\n                    DisplayInside::Flow => DisplayInside::FlowRoot,\n                    inside => inside,\n                };\n                Display::from3(DisplayOutside::Inline, inside, self.is_list_item())\n            },\n            _ => *self,\n        }\n    }\n\n    /// Returns true if the value is `Contents`\n    #[inline]\n    pub fn is_contents(&self) -> bool {\n        match *self {\n            Display::Contents => true,\n            _ => false,\n        }\n    }\n\n    /// Returns true if the value is `None`\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        *self == Display::None\n    }\n}\n\nenum DisplayKeyword {\n    Full(Display),\n    Inside(DisplayInside),\n    Outside(DisplayOutside),\n    ListItem,\n}\n\nimpl DisplayKeyword {\n    fn parse<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {\n        use self::DisplayKeyword::*;\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"none\" => Full(Display::None),\n            \"contents\" => Full(Display::Contents),\n            \"inline-block\" => Full(Display::InlineBlock),\n            \"inline-table\" => Full(Display::InlineTable),\n            \"-webkit-flex\" => Full(Display::Flex),\n            \"inline-flex\" | \"-webkit-inline-flex\" => Full(Display::InlineFlex),\n            \"inline-grid\" if grid_enabled() => Full(Display::InlineGrid),\n            \"table-caption\" => Full(Display::TableCaption),\n            \"table-row-group\" => Full(Display::TableRowGroup),\n            \"table-header-group\" => Full(Display::TableHeaderGroup),\n            \"table-footer-group\" => Full(Display::TableFooterGroup),\n            \"table-column\" => Full(Display::TableColumn),\n            \"table-column-group\" => Full(Display::TableColumnGroup),\n            \"table-row\" => Full(Display::TableRow),\n            \"table-cell\" => Full(Display::TableCell),\n            #[cfg(feature = \"gecko\")]\n            \"ruby-base\" => Full(Display::RubyBase),\n            #[cfg(feature = \"gecko\")]\n            \"ruby-base-container\" => Full(Display::RubyBaseContainer),\n            #[cfg(feature = \"gecko\")]\n            \"ruby-text\" => Full(Display::RubyText),\n            #[cfg(feature = \"gecko\")]\n            \"ruby-text-container\" => Full(Display::RubyTextContainer),\n            #[cfg(feature = \"gecko\")]\n            \"-webkit-box\" => Full(Display::WebkitBox),\n            #[cfg(feature = \"gecko\")]\n            \"-webkit-inline-box\" => Full(Display::WebkitInlineBox),\n\n            /// <display-outside> = block | inline | run-in\n            /// https://drafts.csswg.org/css-display/#typedef-display-outside\n            \"block\" => Outside(DisplayOutside::Block),\n            \"inline\" => Outside(DisplayOutside::Inline),\n\n            \"list-item\" => ListItem,\n\n            /// <display-inside> = flow | flow-root | table | flex | grid | ruby\n            /// https://drafts.csswg.org/css-display/#typedef-display-inside\n            \"flow\" => Inside(DisplayInside::Flow),\n            \"flex\" => Inside(DisplayInside::Flex),\n            \"flow-root\" => Inside(DisplayInside::FlowRoot),\n            \"table\" => Inside(DisplayInside::Table),\n            \"grid\" if grid_enabled() => Inside(DisplayInside::Grid),\n            #[cfg(feature = \"gecko\")]\n            \"ruby\" => Inside(DisplayInside::Ruby),\n        })\n    }\n}\n\nimpl ToCss for Display {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        let outside = self.outside();\n        let inside = self.inside();\n        match *self {\n            Display::Block | Display::Inline => outside.to_css(dest),\n            Display::InlineBlock => dest.write_str(\"inline-block\"),\n            #[cfg(feature = \"gecko\")]\n            Display::WebkitInlineBox => dest.write_str(\"-webkit-inline-box\"),\n            Display::TableCaption => dest.write_str(\"table-caption\"),\n            _ => match (outside, inside) {\n                (DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str(\"inline-grid\"),\n                (DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str(\"inline-flex\"),\n                (DisplayOutside::Inline, DisplayInside::Table) => dest.write_str(\"inline-table\"),\n                #[cfg(feature = \"gecko\")]\n                (DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str(\"block ruby\"),\n                (_, inside) => {\n                    if self.is_list_item() {\n                        if outside != DisplayOutside::Block {\n                            outside.to_css(dest)?;\n                            dest.write_char(' ')?;\n                        }\n                        if inside != DisplayInside::Flow {\n                            inside.to_css(dest)?;\n                            dest.write_char(' ')?;\n                        }\n                        dest.write_str(\"list-item\")\n                    } else {\n                        inside.to_css(dest)\n                    }\n                },\n            },\n        }\n    }\n}\n\nimpl Parse for Display {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Display, ParseError<'i>> {\n        let mut got_list_item = false;\n        let mut inside = None;\n        let mut outside = None;\n        match DisplayKeyword::parse(input)? {\n            DisplayKeyword::Full(d) => return Ok(d),\n            DisplayKeyword::Outside(o) => {\n                outside = Some(o);\n            },\n            DisplayKeyword::Inside(i) => {\n                inside = Some(i);\n            },\n            DisplayKeyword::ListItem => {\n                got_list_item = true;\n            },\n        };\n\n        while let Ok(kw) = input.try_parse(DisplayKeyword::parse) {\n            match kw {\n                DisplayKeyword::ListItem if !got_list_item => {\n                    got_list_item = true;\n                },\n                DisplayKeyword::Outside(o) if outside.is_none() => {\n                    outside = Some(o);\n                },\n                DisplayKeyword::Inside(i) if inside.is_none() => {\n                    inside = Some(i);\n                },\n                _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n            }\n        }\n\n        let inside = inside.unwrap_or(DisplayInside::Flow);\n        let outside = outside.unwrap_or_else(|| inside.default_display_outside());\n        if got_list_item && !inside.is_valid_for_list_item() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        return Ok(Display::from3(outside, inside, got_list_item));\n    }\n}\n\nimpl SpecifiedValueInfo for Display {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        f(&[\n            \"block\",\n            \"contents\",\n            \"flex\",\n            \"flow-root\",\n            \"flow-root list-item\",\n            \"grid\",\n            \"inline\",\n            \"inline-block\",\n            \"inline-flex\",\n            \"inline-grid\",\n            \"inline-table\",\n            \"inline list-item\",\n            \"inline flow-root list-item\",\n            \"list-item\",\n            \"none\",\n            \"block ruby\",\n            \"ruby\",\n            \"ruby-base\",\n            \"ruby-base-container\",\n            \"ruby-text\",\n            \"ruby-text-container\",\n            \"table\",\n            \"table-caption\",\n            \"table-cell\",\n            \"table-column\",\n            \"table-column-group\",\n            \"table-footer-group\",\n            \"table-header-group\",\n            \"table-row\",\n            \"table-row-group\",\n            \"-webkit-box\",\n            \"-webkit-inline-box\",\n        ]);\n    }\n}\n\n/// A specified value for the `contain-intrinsic-size` property.\npub type ContainIntrinsicSize = GenericContainIntrinsicSize<NonNegativeLength>;\n\n/// A specified value for the `line-clamp` property.\npub type LineClamp = GenericLineClamp<Integer>;\n\n/// A specified value for the `baseline-shift` property.\npub type BaselineShift = GenericBaselineShift<LengthPercentage>;\n\nimpl Parse for BaselineShift {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(lp) =\n            input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))\n        {\n            return Ok(BaselineShift::Length(lp));\n        }\n\n        Ok(BaselineShift::Keyword(BaselineShiftKeyword::parse(input)?))\n    }\n}\n\n/// A specified value for the `dominant-baseline` property.\n/// https://drafts.csswg.org/css-inline-3/#dominant-baseline\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToComputedValue,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum DominantBaseline {\n    /// Equivalent to 'alphabetic' in horizontal writing modes and in vertical writing\n    /// modes when 'text-orientation' is sideways. Equivalent to 'central' in vertical\n    /// writing modes when 'text-orientation' is 'mixed' or 'upright'.\n    Auto,\n    /// Use the text-under baseline.\n    #[parse(aliases = \"text-after-edge\")]\n    TextBottom,\n    /// Use the alphabetic baseline.\n    Alphabetic,\n    /// Use the ideographic-under baseline.\n    Ideographic,\n    /// In general, use the x-middle baselines; except under text-orientation: upright\n    /// (where the alphabetic and x-height baselines are essentially meaningless) use\n    /// the central baseline instead.\n    Middle,\n    /// Use the central baseline.\n    Central,\n    /// Use the math baseline.\n    Mathematical,\n    /// Use the hanging baseline.\n    Hanging,\n    /// Use the text-over baseline.\n    #[parse(aliases = \"text-before-edge\")]\n    TextTop,\n}\n\n/// A specified value for the `alignment-baseline` property.\n/// https://drafts.csswg.org/css-inline-3/#alignment-baseline\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToComputedValue,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum AlignmentBaseline {\n    /// Use the dominant baseline choice of the parent.\n    Baseline,\n    /// Use the text-under baseline.\n    TextBottom,\n    /// Use the alphabetic baseline.\n    /// TODO: Bug 2010717 - Remove css(skip) to support alignment-baseline: alphabetic\n    #[css(skip)]\n    Alphabetic,\n    /// Use the ideographic-under baseline.\n    /// TODO: Bug 2010718 - Remove css(skip) support alignment-baseline: ideographic\n    #[css(skip)]\n    Ideographic,\n    /// In general, use the x-middle baselines; except under text-orientation: upright\n    /// (where the alphabetic and x-height baselines are essentially meaningless) use\n    /// the central baseline instead.\n    Middle,\n    /// Use the central baseline.\n    /// TODO: Bug 2010719 - Remove css(skip) to support alignment-baseline: central\n    #[css(skip)]\n    Central,\n    /// Use the math baseline.\n    /// TODO: Bug 2010720 - Remove css(skip) to support alignment-baseline: mathematical\n    #[css(skip)]\n    Mathematical,\n    /// Use the hanging baseline.\n    /// TODO: Bug 2017197 - Remove css(skip) to support alignment-baseline: hanging\n    #[css(skip)]\n    Hanging,\n    /// Use the text-over baseline.\n    TextTop,\n    /// Used to implement the deprecated \"align=middle\" attribute for HTML img elements.\n    #[cfg(feature = \"gecko\")]\n    MozMiddleWithBaseline,\n}\n\n/// A specified value for the `baseline-source` property.\n/// https://drafts.csswg.org/css-inline-3/#baseline-source\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToComputedValue,\n    ToResolvedValue,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum BaselineSource {\n    /// `Last` for `inline-block`, `First` otherwise.\n    Auto,\n    /// Use first baseline for alignment.\n    First,\n    /// Use last baseline for alignment.\n    Last,\n}\n\nimpl BaselineSource {\n    /// Parse baseline source, but without the auto keyword, for the shorthand.\n    pub fn parse_non_auto<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"first\" => Self::First,\n            \"last\" => Self::Last,\n        })\n    }\n}\n\n/// https://drafts.csswg.org/css-scroll-snap-1/#snap-axis\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ScrollSnapAxis {\n    X,\n    Y,\n    Block,\n    Inline,\n    Both,\n}\n\n/// https://drafts.csswg.org/css-scroll-snap-1/#snap-strictness\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ScrollSnapStrictness {\n    #[css(skip)]\n    None, // Used to represent scroll-snap-type: none.  It's not parsed.\n    Mandatory,\n    Proximity,\n}\n\n/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct ScrollSnapType {\n    axis: ScrollSnapAxis,\n    strictness: ScrollSnapStrictness,\n}\n\nimpl ScrollSnapType {\n    /// Returns `none`.\n    #[inline]\n    pub fn none() -> Self {\n        Self {\n            axis: ScrollSnapAxis::Both,\n            strictness: ScrollSnapStrictness::None,\n        }\n    }\n}\n\nimpl Parse for ScrollSnapType {\n    /// none | [ x | y | block | inline | both ] [ mandatory | proximity ]?\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(ScrollSnapType::none());\n        }\n\n        let axis = ScrollSnapAxis::parse(input)?;\n        let strictness = input\n            .try_parse(ScrollSnapStrictness::parse)\n            .unwrap_or(ScrollSnapStrictness::Proximity);\n        Ok(Self { axis, strictness })\n    }\n}\n\nimpl ToCss for ScrollSnapType {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.strictness == ScrollSnapStrictness::None {\n            return dest.write_str(\"none\");\n        }\n        self.axis.to_css(dest)?;\n        if self.strictness != ScrollSnapStrictness::Proximity {\n            dest.write_char(' ')?;\n            self.strictness.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// Specified value of scroll-snap-align keyword value.\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum ScrollSnapAlignKeyword {\n    None,\n    Start,\n    End,\n    Center,\n}\n\n/// https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub struct ScrollSnapAlign {\n    block: ScrollSnapAlignKeyword,\n    inline: ScrollSnapAlignKeyword,\n}\n\nimpl ScrollSnapAlign {\n    /// Returns `none`.\n    #[inline]\n    pub fn none() -> Self {\n        ScrollSnapAlign {\n            block: ScrollSnapAlignKeyword::None,\n            inline: ScrollSnapAlignKeyword::None,\n        }\n    }\n}\n\nimpl Parse for ScrollSnapAlign {\n    /// [ none | start | end | center ]{1,2}\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<ScrollSnapAlign, ParseError<'i>> {\n        let block = ScrollSnapAlignKeyword::parse(input)?;\n        let inline = input\n            .try_parse(ScrollSnapAlignKeyword::parse)\n            .unwrap_or(block);\n        Ok(ScrollSnapAlign { block, inline })\n    }\n}\n\nimpl ToCss for ScrollSnapAlign {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.block.to_css(dest)?;\n        if self.block != self.inline {\n            dest.write_char(' ')?;\n            self.inline.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ScrollSnapStop {\n    Normal,\n    Always,\n}\n\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum OverscrollBehavior {\n    Auto,\n    Contain,\n    None,\n}\n\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum OverflowAnchor {\n    Auto,\n    None,\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(comma)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// Provides a rendering hint to the user agent, stating what kinds of changes\n/// the author expects to perform on the element.\n///\n/// `auto` is represented by an empty `features` list.\n///\n/// <https://drafts.csswg.org/css-will-change/#will-change>\npub struct WillChange {\n    /// The features that are supposed to change.\n    ///\n    /// TODO(emilio): Consider using ArcSlice since we just clone them from the\n    /// specified value? That'd save an allocation, which could be worth it.\n    #[css(iterable, if_empty = \"auto\")]\n    features: crate::OwnedSlice<CustomIdent>,\n    /// A bitfield with the kind of change that the value will create, based\n    /// on the above field.\n    #[css(skip)]\n    pub bits: WillChangeBits,\n}\n\nimpl WillChange {\n    #[inline]\n    /// Get default value of `will-change` as `auto`\n    pub fn auto() -> Self {\n        Self::default()\n    }\n}\n\n/// The change bits that we care about.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct WillChangeBits(u16);\nbitflags! {\n    impl WillChangeBits: u16 {\n        /// Whether a property which can create a stacking context **on any\n        /// box** will change.\n        const STACKING_CONTEXT_UNCONDITIONAL = 1 << 0;\n        /// Whether `transform` or related properties will change.\n        const TRANSFORM = 1 << 1;\n        /// Whether `scroll-position` will change.\n        const SCROLL = 1 << 2;\n        /// Whether `contain` will change.\n        const CONTAIN = 1 << 3;\n        /// Whether `opacity` will change.\n        const OPACITY = 1 << 4;\n        /// Whether `perspective` will change.\n        const PERSPECTIVE = 1 << 5;\n        /// Whether `z-index` will change.\n        const Z_INDEX = 1 << 6;\n        /// Whether any property which creates a containing block for non-svg\n        /// text frames will change.\n        const FIXPOS_CB_NON_SVG = 1 << 7;\n        /// Whether the position property will change.\n        const POSITION = 1 << 8;\n        /// Whether the view-transition-name property will change.\n        const VIEW_TRANSITION_NAME = 1 << 9;\n        /// Whether any property which establishes a backdrop-root will change.\n        /// See https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty\n        const BACKDROP_ROOT = 1 << 10;\n    }\n}\n\nfn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {\n    match longhand {\n        LonghandId::Opacity => WillChangeBits::OPACITY | WillChangeBits::BACKDROP_ROOT,\n        LonghandId::Contain => WillChangeBits::CONTAIN,\n        LonghandId::Perspective => WillChangeBits::PERSPECTIVE,\n        LonghandId::Position => {\n            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::POSITION\n        },\n        LonghandId::ZIndex => WillChangeBits::Z_INDEX,\n        LonghandId::Transform\n        | LonghandId::TransformStyle\n        | LonghandId::Translate\n        | LonghandId::Rotate\n        | LonghandId::Scale\n        | LonghandId::OffsetPath => WillChangeBits::TRANSFORM,\n        LonghandId::Filter | LonghandId::BackdropFilter => {\n            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL\n                | WillChangeBits::BACKDROP_ROOT\n                | WillChangeBits::FIXPOS_CB_NON_SVG\n        },\n        LonghandId::ViewTransitionName => {\n            WillChangeBits::VIEW_TRANSITION_NAME | WillChangeBits::BACKDROP_ROOT\n        },\n        LonghandId::MixBlendMode => {\n            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT\n        },\n        LonghandId::Isolation | LonghandId::MaskImage => {\n            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL\n        },\n        LonghandId::ClipPath => {\n            WillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | WillChangeBits::BACKDROP_ROOT\n        },\n        _ => WillChangeBits::empty(),\n    }\n}\n\nfn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {\n    let id = match PropertyId::parse_ignoring_rule_type(ident, context) {\n        Ok(id) => id,\n        Err(..) => return WillChangeBits::empty(),\n    };\n\n    match id.as_shorthand() {\n        Ok(shorthand) => shorthand\n            .longhands()\n            .fold(WillChangeBits::empty(), |flags, p| {\n                flags | change_bits_for_longhand(p)\n            }),\n        Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),\n        Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),\n    }\n}\n\nimpl Parse for WillChange {\n    /// auto | <animateable-feature>#\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"auto\"))\n            .is_ok()\n        {\n            return Ok(Self::default());\n        }\n\n        let mut bits = WillChangeBits::empty();\n        let custom_idents = input.parse_comma_separated(|i| {\n            let location = i.current_source_location();\n            let parser_ident = i.expect_ident()?;\n            let ident = CustomIdent::from_ident(\n                location,\n                parser_ident,\n                &[\"will-change\", \"none\", \"all\", \"auto\"],\n            )?;\n\n            if context.in_ua_sheet() && ident.0 == atom!(\"-moz-fixed-pos-containing-block\") {\n                bits |= WillChangeBits::FIXPOS_CB_NON_SVG;\n            } else if ident.0 == atom!(\"scroll-position\") {\n                bits |= WillChangeBits::SCROLL;\n            } else {\n                bits |= change_bits_for_maybe_property(&parser_ident, context);\n            }\n            Ok(ident)\n        })?;\n\n        Ok(Self {\n            features: custom_idents.into(),\n            bits,\n        })\n    }\n}\n\n/// Values for the `touch-action` property.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(single = \"none,auto,manipulation\", mixed = \"pan-x,pan-y,pinch-zoom\"))]\n#[repr(C)]\npub struct TouchAction(u8);\nbitflags! {\n    impl TouchAction: u8 {\n        /// `none` variant\n        const NONE = 1 << 0;\n        /// `auto` variant\n        const AUTO = 1 << 1;\n        /// `pan-x` variant\n        const PAN_X = 1 << 2;\n        /// `pan-y` variant\n        const PAN_Y = 1 << 3;\n        /// `manipulation` variant\n        const MANIPULATION = 1 << 4;\n        /// `pinch-zoom` variant\n        const PINCH_ZOOM = 1 << 5;\n    }\n}\n\nimpl TouchAction {\n    #[inline]\n    /// Get default `touch-action` as `auto`\n    pub fn auto() -> TouchAction {\n        TouchAction::AUTO\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"none,strict,content\",\n    mixed = \"size,layout,style,paint,inline-size\",\n    overlapping_bits\n))]\n#[repr(C)]\n/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property\npub struct Contain(u8);\nbitflags! {\n    impl Contain: u8 {\n        /// `none` variant, just for convenience.\n        const NONE = 0;\n        /// `inline-size` variant, turns on single-axis inline size containment\n        const INLINE_SIZE = 1 << 0;\n        /// `block-size` variant, turns on single-axis block size containment, internal only\n        const BLOCK_SIZE = 1 << 1;\n        /// `layout` variant, turns on layout containment\n        const LAYOUT = 1 << 2;\n        /// `style` variant, turns on style containment\n        const STYLE = 1 << 3;\n        /// `paint` variant, turns on paint containment\n        const PAINT = 1 << 4;\n        /// 'size' variant, turns on size containment\n        const SIZE = 1 << 5 | Contain::INLINE_SIZE.bits() | Contain::BLOCK_SIZE.bits();\n        /// `content` variant, turns on layout and paint containment\n        const CONTENT = 1 << 6 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits();\n        /// `strict` variant, turns on all types of containment\n        const STRICT = 1 << 7 | Contain::LAYOUT.bits() | Contain::STYLE.bits() | Contain::PAINT.bits() | Contain::SIZE.bits();\n    }\n}\n\nimpl Parse for ContainIntrinsicSize {\n    /// none | <length> | auto <length>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(l) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {\n            return Ok(Self::Length(l));\n        }\n\n        if input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok() {\n            if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n                return Ok(Self::AutoNone);\n            }\n\n            let l = NonNegativeLength::parse(context, input)?;\n            return Ok(Self::AutoLength(l));\n        }\n\n        input.expect_ident_matching(\"none\")?;\n        Ok(Self::None)\n    }\n}\n\nimpl Parse for LineClamp {\n    /// none | <positive-integer>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(i) =\n            input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i))\n        {\n            return Ok(Self(i.0));\n        }\n        input.expect_ident_matching(\"none\")?;\n        Ok(Self::none())\n    }\n}\n\n/// https://drafts.csswg.org/css-contain-2/#content-visibility\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ContentVisibility {\n    /// `auto` variant, the element turns on layout containment, style containment, and paint\n    /// containment. In addition, if the element is not relevant to the user (such as by being\n    /// offscreen) it also skips its content\n    Auto,\n    /// `hidden` variant, the element skips its content\n    Hidden,\n    /// 'visible' variant, no effect\n    Visible,\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    PartialEq,\n    Eq,\n    MallocSizeOf,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    Parse,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"normal\",\n    mixed = \"size,inline-size,scroll-state\",\n    validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\n/// Specified keyword values for the container-type property.\n/// Spec: normal | [ [ size | inline-size ] || scroll-state ]\n///\n/// Container Queries are moved from css-contain-3 to css-conditional-5 in August 2022:\n/// https://drafts.csswg.org/css-contain-3/#container-type\n/// https://drafts.csswg.org/css-conditional-5/#container-type\npub struct ContainerType(u8);\nbitflags! {\n    impl ContainerType: u8 {\n        /// The `normal` variant.\n        const NORMAL = 0;\n        /// The `inline-size` variant.\n        const INLINE_SIZE = 1 << 0;\n        /// The `size` variant.\n        const SIZE = 1 << 1;\n        /// The `scroll-state` variant.\n        const SCROLL_STATE = 1 << 2;\n    }\n}\n\nimpl ContainerType {\n    fn validate_mixed_flags(&self) -> bool {\n        // size and inline-size can't be mixed together.\n        if self.contains(Self::SIZE | Self::INLINE_SIZE) {\n            return false;\n        }\n        if self.contains(Self::SCROLL_STATE)\n            && !static_prefs::pref!(\"layout.css.scroll-state.enabled\")\n        {\n            return false;\n        }\n        true\n    }\n\n    /// Is this container-type: normal?\n    pub fn is_normal(self) -> bool {\n        self == Self::NORMAL\n    }\n\n    /// Is this type containing size in any way?\n    pub fn is_size_container_type(self) -> bool {\n        self.intersects(Self::SIZE | Self::INLINE_SIZE)\n    }\n}\n\n/// https://drafts.csswg.org/css-contain-3/#container-name\n#[repr(transparent)]\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\npub struct ContainerName(#[css(iterable, if_empty = \"none\")] pub crate::OwnedSlice<CustomIdent>);\n\nimpl ContainerName {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self(Default::default())\n    }\n\n    /// Returns whether this is the `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    fn parse_internal<'i>(\n        input: &mut Parser<'i, '_>,\n        for_query: bool,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut idents = vec![];\n        let location = input.current_source_location();\n        let first = input.expect_ident()?;\n        if !for_query && first.eq_ignore_ascii_case(\"none\") {\n            return Ok(Self::none());\n        }\n        const DISALLOWED_CONTAINER_NAMES: &'static [&'static str] = &[\"none\", \"not\", \"or\", \"and\"];\n        idents.push(CustomIdent::from_ident(\n            location,\n            first,\n            DISALLOWED_CONTAINER_NAMES,\n        )?);\n        if !for_query {\n            while let Ok(name) =\n                input.try_parse(|input| CustomIdent::parse(input, DISALLOWED_CONTAINER_NAMES))\n            {\n                idents.push(name);\n            }\n        }\n        Ok(ContainerName(idents.into()))\n    }\n\n    /// https://github.com/w3c/csswg-drafts/issues/7203\n    /// Only a single name allowed in @container rule.\n    /// Disallow none for container-name in @container rule.\n    pub fn parse_for_query<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(input, /* for_query = */ true)\n    }\n}\n\nimpl Parse for ContainerName {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(input, /* for_query = */ false)\n    }\n}\n\n/// A specified value for the `perspective` property.\npub type Perspective = GenericPerspective<NonNegativeLength>;\n\n/// https://drafts.csswg.org/css-box/#propdef-float\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum Float {\n    Left,\n    Right,\n    None,\n    // https://drafts.csswg.org/css-logical-props/#float-clear\n    InlineStart,\n    InlineEnd,\n}\n\nimpl Float {\n    /// Returns true if `self` is not `None`.\n    pub fn is_floating(self) -> bool {\n        self != Self::None\n    }\n}\n\n/// https://drafts.csswg.org/css2/#propdef-clear\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum Clear {\n    None,\n    Left,\n    Right,\n    Both,\n    // https://drafts.csswg.org/css-logical-props/#float-clear\n    InlineStart,\n    InlineEnd,\n}\n\n/// https://drafts.csswg.org/css-ui/#propdef-resize\n#[allow(missing_docs)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\npub enum Resize {\n    None,\n    Both,\n    Horizontal,\n    Vertical,\n    // https://drafts.csswg.org/css-logical-1/#resize\n    Inline,\n    Block,\n}\n\n/// The value for the `appearance` property.\n///\n/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum Appearance {\n    /// No appearance at all.\n    None,\n    /// Default appearance for the element.\n    ///\n    /// This value doesn't make sense for -moz-default-appearance, but we don't bother to guard\n    /// against parsing it.\n    Auto,\n    /// A searchfield.\n    Searchfield,\n    /// A multi-line text field, e.g. HTML <textarea>.\n    Textarea,\n    /// A checkbox element.\n    Checkbox,\n    /// A radio element within a radio group.\n    Radio,\n    /// A dropdown list.\n    Menulist,\n    /// List boxes.\n    Listbox,\n    /// A horizontal meter bar.\n    Meter,\n    /// A horizontal progress bar.\n    ProgressBar,\n    /// A typical dialog button.\n    Button,\n    /// A single-line text field, e.g. HTML <input type=text>.\n    Textfield,\n    /// The dropdown button(s) that open up a dropdown list.\n    MenulistButton,\n    /// https://drafts.csswg.org/css-forms/#appearance\n    #[parse(condition = \"appearance_base_enabled\")]\n    Base,\n    /// Only relevant to the <select> element and ::picker(select) pseudo-element, allowing them to\n    /// be styled.\n    #[parse(condition = \"appearance_base_select_enabled\")]\n    BaseSelect,\n    /// Menu Popup background.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Menupopup,\n    /// The \"arrowed\" part of the dropdown button that open up a dropdown list.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMenulistArrowButton,\n    /// For HTML's <input type=number>\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    NumberInput,\n    /// For HTML's <input type=password>\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    PasswordInput,\n    /// nsRangeFrame and its subparts\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Range,\n    /// The scrollbar slider\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarHorizontal,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarVertical,\n    /// A scrollbar button (up/down/left/right).\n    /// Keep these in order (some code casts these values to `int` in order to\n    /// compare them against each other).\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarbuttonUp,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarbuttonDown,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarbuttonLeft,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarbuttonRight,\n    /// The scrollbar thumb.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarthumbHorizontal,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    ScrollbarthumbVertical,\n    /// The scroll corner\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Scrollcorner,\n    /// The up button of a spin control.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    SpinnerUpbutton,\n    /// The down button of a spin control.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    SpinnerDownbutton,\n    /// A single toolbar button (with no associated dropdown).\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Toolbarbutton,\n    /// A tooltip.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Tooltip,\n\n    /// Sidebar appearance.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozSidebar,\n\n    /// Mac help button.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacHelpButton,\n\n    /// An appearance value for the root, so that we can get tinting and unified toolbar looks\n    /// (which require a transparent gecko background) without really using the whole transparency\n    /// set-up which otherwise loses window borders, see bug 1870481.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacWindow,\n\n    /// Windows themed window frame elements.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowButtonBox,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowButtonClose,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowButtonMaximize,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowButtonMinimize,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowButtonRestore,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowTitlebar,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowTitlebarMaximized,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozWindowDecorations,\n\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacDisclosureButtonClosed,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacDisclosureButtonOpen,\n\n    /// A themed focus outline (for outline:auto).\n    ///\n    /// This isn't exposed to CSS at all, just here for convenience.\n    #[css(skip)]\n    FocusOutline,\n\n    /// A dummy variant that should be last to let the GTK widget do hackery.\n    #[css(skip)]\n    Count,\n}\n\n/// A kind of break between two boxes.\n///\n/// https://drafts.csswg.org/css-break/#break-between\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum BreakBetween {\n    Always,\n    Auto,\n    Page,\n    Avoid,\n    Left,\n    Right,\n}\n\nimpl BreakBetween {\n    /// Parse a legacy break-between value for `page-break-{before,after}`.\n    ///\n    /// See https://drafts.csswg.org/css-break/#page-break-properties.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    #[inline]\n    pub(crate) fn parse_legacy<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        let break_value = BreakBetween::parse(input)?;\n        match break_value {\n            BreakBetween::Always => Ok(BreakBetween::Page),\n            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {\n                Ok(break_value)\n            },\n            BreakBetween::Page => {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n        }\n    }\n\n    /// Serialize a legacy break-between value for `page-break-*`.\n    ///\n    /// See https://drafts.csswg.org/css-break/#page-break-properties.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => {\n                self.to_css(dest)\n            },\n            BreakBetween::Page => dest.write_str(\"always\"),\n            BreakBetween::Always => Ok(()),\n        }\n    }\n}\n\n/// A kind of break within a box.\n///\n/// https://drafts.csswg.org/css-break/#break-within\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum BreakWithin {\n    Auto,\n    Avoid,\n    AvoidPage,\n    AvoidColumn,\n}\n\nimpl BreakWithin {\n    /// Parse a legacy break-between value for `page-break-inside`.\n    ///\n    /// See https://drafts.csswg.org/css-break/#page-break-properties.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    #[inline]\n    pub(crate) fn parse_legacy<'i>(\n        _: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        let break_value = BreakWithin::parse(input)?;\n        match break_value {\n            BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value),\n            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n        }\n    }\n\n    /// Serialize a legacy break-between value for `page-break-inside`.\n    ///\n    /// See https://drafts.csswg.org/css-break/#page-break-properties.\n    #[cfg_attr(feature = \"servo\", allow(unused))]\n    pub(crate) fn to_css_legacy<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest),\n            BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()),\n        }\n    }\n}\n\n/// The value for the `overflow-x` / `overflow-y` properties.\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum Overflow {\n    Visible,\n    Hidden,\n    Scroll,\n    Auto,\n    Clip,\n}\n\n// This can be derived once we remove or keep `-moz-hidden-unscrollable`\n// indefinitely.\nimpl Parse for Overflow {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"visible\" => Self::Visible,\n            \"hidden\" => Self::Hidden,\n            \"scroll\" => Self::Scroll,\n            \"auto\" | \"overlay\" => Self::Auto,\n            \"clip\" => Self::Clip,\n            #[cfg(feature = \"gecko\")]\n            \"-moz-hidden-unscrollable\" if static_prefs::pref!(\"layout.css.overflow-moz-hidden-unscrollable.enabled\") => {\n                Overflow::Clip\n            },\n        })\n    }\n}\n\nimpl Overflow {\n    /// Return true if the value will create a scrollable box.\n    #[inline]\n    pub fn is_scrollable(&self) -> bool {\n        matches!(*self, Self::Hidden | Self::Scroll | Self::Auto)\n    }\n    /// Convert the value to a scrollable value if it's not already scrollable.\n    /// This maps `visible` to `auto` and `clip` to `hidden`.\n    #[inline]\n    pub fn to_scrollable(&self) -> Self {\n        match *self {\n            Self::Hidden | Self::Scroll | Self::Auto => *self,\n            Self::Visible => Self::Auto,\n            Self::Clip => Self::Hidden,\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[css(bitflags(\n    single = \"auto\",\n    mixed = \"stable,both-edges\",\n    validate_mixed = \"Self::has_stable\"\n))]\n/// Values for scrollbar-gutter:\n/// <https://drafts.csswg.org/css-overflow-3/#scrollbar-gutter-property>\npub struct ScrollbarGutter(u8);\nbitflags! {\n    impl ScrollbarGutter: u8 {\n        /// `auto` variant. Just for convenience if there is no flag set.\n        const AUTO = 0;\n        /// `stable` variant.\n        const STABLE = 1 << 0;\n        /// `both-edges` variant.\n        const BOTH_EDGES = 1 << 1;\n    }\n}\n\nimpl ScrollbarGutter {\n    #[inline]\n    fn has_stable(&self) -> bool {\n        self.intersects(Self::STABLE)\n    }\n}\n\n/// A specified value for the zoom property.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, Parse, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\n#[allow(missing_docs)]\n#[typed(todo_derive_fields)]\npub enum Zoom {\n    Normal,\n    /// An internal value that resets the effective zoom to 1. Used for scrollbar parts, which\n    /// disregard zoom. We use this name because WebKit has this value exposed to the web.\n    #[parse(condition = \"ParserContext::in_ua_sheet\")]\n    Document,\n    Value(NonNegativeNumberOrPercentage),\n}\n\nimpl Zoom {\n    /// Return a particular number value of the zoom property.\n    #[inline]\n    pub fn new_number(n: f32) -> Self {\n        Self::Value(NonNegativeNumberOrPercentage::new_number(n))\n    }\n}\n\npub use crate::values::generics::box_::PositionProperty;\n"
  },
  {
    "path": "style/values/specified/calc.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [Calc expressions][calc].\n//!\n//! [calc]: https://drafts.csswg.org/css-values/#calc-notation\n\nuse crate::color::parsing::ChannelKeyword;\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::calc::{\n    self as generic, CalcNodeLeaf, CalcUnits, GenericAnchorFunctionFallback, MinMaxOp, ModRemOp,\n    PositivePercentageBasis, RoundingStrategy, SortKey,\n};\nuse crate::values::generics::length::GenericAnchorSizeFunction;\nuse crate::values::generics::position::{\n    AnchorSideKeyword, GenericAnchorFunction, GenericAnchorSide, TreeScoped,\n};\nuse crate::values::specified::length::{AbsoluteLength, FontRelativeLength, NoCalcLength};\nuse crate::values::specified::length::{ContainerRelativeLength, ViewportPercentageLength};\nuse crate::values::specified::{self, Angle, Resolution, Time};\nuse crate::values::{\n    reify_number, reify_percentage, serialize_number, serialize_percentage, CSSFloat, DashedIdent,\n};\nuse cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};\nuse debug_unreachable::debug_unreachable;\nuse smallvec::SmallVec;\nuse std::cmp;\nuse std::fmt::{self, Write};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{\n    CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, ToTyped, TypedValue,\n};\nuse thin_vec::ThinVec;\n\n/// The name of the mathematical function that we're parsing.\n#[derive(Clone, Copy, Debug, Parse)]\npub enum MathFunction {\n    /// `calc()`: https://drafts.csswg.org/css-values-4/#funcdef-calc\n    Calc,\n    /// `min()`: https://drafts.csswg.org/css-values-4/#funcdef-min\n    Min,\n    /// `max()`: https://drafts.csswg.org/css-values-4/#funcdef-max\n    Max,\n    /// `clamp()`: https://drafts.csswg.org/css-values-4/#funcdef-clamp\n    Clamp,\n    /// `round()`: https://drafts.csswg.org/css-values-4/#funcdef-round\n    Round,\n    /// `mod()`: https://drafts.csswg.org/css-values-4/#funcdef-mod\n    Mod,\n    /// `rem()`: https://drafts.csswg.org/css-values-4/#funcdef-rem\n    Rem,\n    /// `sin()`: https://drafts.csswg.org/css-values-4/#funcdef-sin\n    Sin,\n    /// `cos()`: https://drafts.csswg.org/css-values-4/#funcdef-cos\n    Cos,\n    /// `tan()`: https://drafts.csswg.org/css-values-4/#funcdef-tan\n    Tan,\n    /// `asin()`: https://drafts.csswg.org/css-values-4/#funcdef-asin\n    Asin,\n    /// `acos()`: https://drafts.csswg.org/css-values-4/#funcdef-acos\n    Acos,\n    /// `atan()`: https://drafts.csswg.org/css-values-4/#funcdef-atan\n    Atan,\n    /// `atan2()`: https://drafts.csswg.org/css-values-4/#funcdef-atan2\n    Atan2,\n    /// `pow()`: https://drafts.csswg.org/css-values-4/#funcdef-pow\n    Pow,\n    /// `sqrt()`: https://drafts.csswg.org/css-values-4/#funcdef-sqrt\n    Sqrt,\n    /// `hypot()`: https://drafts.csswg.org/css-values-4/#funcdef-hypot\n    Hypot,\n    /// `log()`: https://drafts.csswg.org/css-values-4/#funcdef-log\n    Log,\n    /// `exp()`: https://drafts.csswg.org/css-values-4/#funcdef-exp\n    Exp,\n    /// `abs()`: https://drafts.csswg.org/css-values-4/#funcdef-abs\n    Abs,\n    /// `sign()`: https://drafts.csswg.org/css-values-4/#funcdef-sign\n    Sign,\n}\n\n/// A leaf node inside a `Calc` expression's AST.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\n#[repr(u8)]\npub enum Leaf {\n    /// `<length>`\n    Length(NoCalcLength),\n    /// `<angle>`\n    Angle(Angle),\n    /// `<time>`\n    Time(Time),\n    /// `<resolution>`\n    Resolution(Resolution),\n    /// A component of a color.\n    ColorComponent(ChannelKeyword),\n    /// `<percentage>`\n    Percentage(CSSFloat),\n    /// `<number>`\n    Number(CSSFloat),\n}\n\nimpl Leaf {\n    fn as_length(&self) -> Option<&NoCalcLength> {\n        match *self {\n            Self::Length(ref l) => Some(l),\n            _ => None,\n        }\n    }\n}\n\nimpl ToCss for Leaf {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Self::Length(ref l) => l.to_css(dest),\n            Self::Number(n) => serialize_number(n, /* was_calc = */ false, dest),\n            Self::Resolution(ref r) => r.to_css(dest),\n            Self::Percentage(p) => serialize_percentage(p, dest),\n            Self::Angle(ref a) => a.to_css(dest),\n            Self::Time(ref t) => t.to_css(dest),\n            Self::ColorComponent(ref s) => s.to_css(dest),\n        }\n    }\n}\n\nimpl ToTyped for Leaf {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        // XXX Only supporting Length, Number and Percentage for now\n        match *self {\n            Self::Length(ref l) => l.to_typed(dest),\n            Self::Number(n) => reify_number(n, /* was_calc = */ false, dest),\n            Self::Percentage(p) => reify_percentage(p, /* was_calc = */ false, dest),\n            _ => Err(()),\n        }\n    }\n}\n\n/// A struct to hold a simplified `<length>` or `<percentage>` expression.\n///\n/// In some cases, e.g. DOMMatrix, we support calc(), but reject all the\n/// relative lengths, and to_computed_pixel_length_without_context() handles\n/// this case. Therefore, if you want to add a new field, please make sure this\n/// function work properly.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem, ToTyped)]\n#[allow(missing_docs)]\npub struct CalcLengthPercentage {\n    #[css(skip)]\n    pub clamping_mode: AllowedNumericType,\n    pub node: CalcNode,\n}\n\nimpl CalcLengthPercentage {\n    fn same_unit_length_as(a: &Self, b: &Self) -> Option<(CSSFloat, CSSFloat)> {\n        debug_assert_eq!(a.clamping_mode, b.clamping_mode);\n        debug_assert_eq!(a.clamping_mode, AllowedNumericType::All);\n\n        let a = a.node.as_leaf()?;\n        let b = b.node.as_leaf()?;\n\n        if a.sort_key() != b.sort_key() {\n            return None;\n        }\n\n        let a = a.as_length()?.unitless_value();\n        let b = b.as_length()?.unitless_value();\n        return Some((a, b));\n    }\n}\n\nimpl SpecifiedValueInfo for CalcLengthPercentage {}\n\n/// Should parsing anchor-positioning functions in `calc()` be allowed?\n#[derive(Clone, Copy, PartialEq)]\npub enum AllowAnchorPositioningFunctions {\n    /// Don't allow any anchor positioning function.\n    No,\n    /// Allow `anchor-size()` to be parsed.\n    AllowAnchorSize,\n    /// Allow `anchor()` and `anchor-size()` to be parsed.\n    AllowAnchorAndAnchorSize,\n}\n\nbitflags! {\n    /// Additional functions within math functions that are permitted to be parsed depending on\n    /// the context of parsing (e.g. Parsing `inset` allows use of `anchor()` within `calc()`).\n    #[derive(Clone, Copy, PartialEq, Eq)]\n    struct AdditionalFunctions: u8 {\n        /// `anchor()` function.\n        const ANCHOR = 1 << 0;\n        /// `anchor-size()` function.\n        const ANCHOR_SIZE = 1 << 1;\n    }\n}\n\n/// What is allowed to be parsed for math functions within in this context?\n#[derive(Clone, Copy)]\npub struct AllowParse {\n    /// Units allowed to be parsed.\n    units: CalcUnits,\n    /// Additional functions allowed to be parsed in this context.\n    additional_functions: AdditionalFunctions,\n}\n\nimpl AllowParse {\n    /// Allow only specified units to be parsed, without any additional functions.\n    pub fn new(units: CalcUnits) -> Self {\n        Self {\n            units,\n            additional_functions: AdditionalFunctions::empty(),\n        }\n    }\n\n    /// Add new units to the allowed units to be parsed.\n    fn new_including(mut self, units: CalcUnits) -> Self {\n        self.units |= units;\n        self\n    }\n\n    /// Should given unit be allowed to parse?\n    fn includes(&self, unit: CalcUnits) -> bool {\n        self.units.intersects(unit)\n    }\n}\n\nimpl generic::CalcNodeLeaf for Leaf {\n    fn unit(&self) -> CalcUnits {\n        match self {\n            Leaf::Length(_) => CalcUnits::LENGTH,\n            Leaf::Angle(_) => CalcUnits::ANGLE,\n            Leaf::Time(_) => CalcUnits::TIME,\n            Leaf::Resolution(_) => CalcUnits::RESOLUTION,\n            Leaf::ColorComponent(_) => CalcUnits::COLOR_COMPONENT,\n            Leaf::Percentage(_) => CalcUnits::PERCENTAGE,\n            Leaf::Number(_) => CalcUnits::empty(),\n        }\n    }\n\n    fn unitless_value(&self) -> Option<f32> {\n        Some(match *self {\n            Self::Length(ref l) => l.unitless_value(),\n            Self::Percentage(n) | Self::Number(n) => n,\n            Self::Resolution(ref r) => r.dppx(),\n            Self::Angle(ref a) => a.degrees(),\n            Self::Time(ref t) => t.seconds(),\n            Self::ColorComponent(_) => return None,\n        })\n    }\n\n    fn new_number(value: f32) -> Self {\n        Self::Number(value)\n    }\n\n    fn compare(&self, other: &Self, basis: PositivePercentageBasis) -> Option<cmp::Ordering> {\n        use self::Leaf::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        if matches!(self, Percentage(..)) && matches!(basis, PositivePercentageBasis::Unknown) {\n            return None;\n        }\n\n        let self_negative = self.is_negative().unwrap_or(false);\n        if self_negative != other.is_negative().unwrap_or(false) {\n            return Some(if self_negative {\n                cmp::Ordering::Less\n            } else {\n                cmp::Ordering::Greater\n            });\n        }\n\n        match (self, other) {\n            (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other),\n            (&Length(ref one), &Length(ref other)) => one.partial_cmp(other),\n            (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()),\n            (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()),\n            (&Resolution(ref one), &Resolution(ref other)) => one.dppx().partial_cmp(&other.dppx()),\n            (&Number(ref one), &Number(ref other)) => one.partial_cmp(other),\n            (&ColorComponent(ref one), &ColorComponent(ref other)) => one.partial_cmp(other),\n            _ => {\n                match *self {\n                    Length(..) | Percentage(..) | Angle(..) | Time(..) | Number(..)\n                    | Resolution(..) | ColorComponent(..) => {},\n                }\n                unsafe {\n                    debug_unreachable!(\"Forgot a branch?\");\n                }\n            },\n        }\n    }\n\n    fn as_number(&self) -> Option<f32> {\n        match *self {\n            Leaf::Length(_)\n            | Leaf::Angle(_)\n            | Leaf::Time(_)\n            | Leaf::Resolution(_)\n            | Leaf::Percentage(_)\n            | Leaf::ColorComponent(_) => None,\n            Leaf::Number(value) => Some(value),\n        }\n    }\n\n    fn sort_key(&self) -> SortKey {\n        match *self {\n            Self::Number(..) => SortKey::Number,\n            Self::Percentage(..) => SortKey::Percentage,\n            Self::Time(..) => SortKey::S,\n            Self::Resolution(..) => SortKey::Dppx,\n            Self::Angle(..) => SortKey::Deg,\n            Self::Length(ref l) => match *l {\n                NoCalcLength::Absolute(..) => SortKey::Px,\n                NoCalcLength::FontRelative(ref relative) => match *relative {\n                    FontRelativeLength::Em(..) => SortKey::Em,\n                    FontRelativeLength::Ex(..) => SortKey::Ex,\n                    FontRelativeLength::Rex(..) => SortKey::Rex,\n                    FontRelativeLength::Ch(..) => SortKey::Ch,\n                    FontRelativeLength::Rch(..) => SortKey::Rch,\n                    FontRelativeLength::Cap(..) => SortKey::Cap,\n                    FontRelativeLength::Rcap(..) => SortKey::Rcap,\n                    FontRelativeLength::Ic(..) => SortKey::Ic,\n                    FontRelativeLength::Ric(..) => SortKey::Ric,\n                    FontRelativeLength::Rem(..) => SortKey::Rem,\n                    FontRelativeLength::Lh(..) => SortKey::Lh,\n                    FontRelativeLength::Rlh(..) => SortKey::Rlh,\n                },\n                NoCalcLength::ViewportPercentage(ref vp) => match *vp {\n                    ViewportPercentageLength::Vh(..) => SortKey::Vh,\n                    ViewportPercentageLength::Svh(..) => SortKey::Svh,\n                    ViewportPercentageLength::Lvh(..) => SortKey::Lvh,\n                    ViewportPercentageLength::Dvh(..) => SortKey::Dvh,\n                    ViewportPercentageLength::Vw(..) => SortKey::Vw,\n                    ViewportPercentageLength::Svw(..) => SortKey::Svw,\n                    ViewportPercentageLength::Lvw(..) => SortKey::Lvw,\n                    ViewportPercentageLength::Dvw(..) => SortKey::Dvw,\n                    ViewportPercentageLength::Vmax(..) => SortKey::Vmax,\n                    ViewportPercentageLength::Svmax(..) => SortKey::Svmax,\n                    ViewportPercentageLength::Lvmax(..) => SortKey::Lvmax,\n                    ViewportPercentageLength::Dvmax(..) => SortKey::Dvmax,\n                    ViewportPercentageLength::Vmin(..) => SortKey::Vmin,\n                    ViewportPercentageLength::Svmin(..) => SortKey::Svmin,\n                    ViewportPercentageLength::Lvmin(..) => SortKey::Lvmin,\n                    ViewportPercentageLength::Dvmin(..) => SortKey::Dvmin,\n                    ViewportPercentageLength::Vb(..) => SortKey::Vb,\n                    ViewportPercentageLength::Svb(..) => SortKey::Svb,\n                    ViewportPercentageLength::Lvb(..) => SortKey::Lvb,\n                    ViewportPercentageLength::Dvb(..) => SortKey::Dvb,\n                    ViewportPercentageLength::Vi(..) => SortKey::Vi,\n                    ViewportPercentageLength::Svi(..) => SortKey::Svi,\n                    ViewportPercentageLength::Lvi(..) => SortKey::Lvi,\n                    ViewportPercentageLength::Dvi(..) => SortKey::Dvi,\n                },\n                NoCalcLength::ContainerRelative(ref cq) => match *cq {\n                    ContainerRelativeLength::Cqw(..) => SortKey::Cqw,\n                    ContainerRelativeLength::Cqh(..) => SortKey::Cqh,\n                    ContainerRelativeLength::Cqi(..) => SortKey::Cqi,\n                    ContainerRelativeLength::Cqb(..) => SortKey::Cqb,\n                    ContainerRelativeLength::Cqmin(..) => SortKey::Cqmin,\n                    ContainerRelativeLength::Cqmax(..) => SortKey::Cqmax,\n                },\n                NoCalcLength::ServoCharacterWidth(..) => unreachable!(),\n            },\n            Self::ColorComponent(..) => SortKey::ColorComponent,\n        }\n    }\n\n    fn simplify(&mut self) {\n        if let Self::Length(NoCalcLength::Absolute(ref mut abs)) = *self {\n            *abs = AbsoluteLength::Px(abs.to_px());\n        }\n    }\n\n    /// Tries to merge one sum to another, that is, perform `x` + `y`.\n    ///\n    /// Only handles leaf nodes, it's the caller's responsibility to simplify\n    /// them before calling this if needed.\n    fn try_sum_in_place(&mut self, other: &Self) -> Result<(), ()> {\n        use self::Leaf::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        match (self, other) {\n            (&mut Number(ref mut one), &Number(ref other))\n            | (&mut Percentage(ref mut one), &Percentage(ref other)) => {\n                *one += *other;\n            },\n            (&mut Angle(ref mut one), &Angle(ref other)) => {\n                *one = specified::Angle::from_calc(one.degrees() + other.degrees());\n            },\n            (&mut Time(ref mut one), &Time(ref other)) => {\n                *one = specified::Time::from_seconds(one.seconds() + other.seconds());\n            },\n            (&mut Resolution(ref mut one), &Resolution(ref other)) => {\n                *one = specified::Resolution::from_dppx(one.dppx() + other.dppx());\n            },\n            (&mut Length(ref mut one), &Length(ref other)) => {\n                *one = one.try_op(other, std::ops::Add::add)?;\n            },\n            (&mut ColorComponent(_), &ColorComponent(_)) => {\n                // Can not get the sum of color components, because they haven't been resolved yet.\n                return Err(());\n            },\n            _ => {\n                match *other {\n                    Number(..) | Percentage(..) | Angle(..) | Time(..) | Resolution(..)\n                    | Length(..) | ColorComponent(..) => {},\n                }\n                unsafe {\n                    debug_unreachable!();\n                }\n            },\n        }\n\n        Ok(())\n    }\n\n    fn try_product_in_place(&mut self, other: &mut Self) -> bool {\n        if let Self::Number(ref mut left) = *self {\n            if let Self::Number(ref right) = *other {\n                // Both sides are numbers, so we can just modify the left side.\n                *left *= *right;\n                true\n            } else {\n                // The right side is not a number, so the result should be in the units of the right\n                // side.\n                if other.map(|v| v * *left).is_ok() {\n                    std::mem::swap(self, other);\n                    true\n                } else {\n                    false\n                }\n            }\n        } else if let Self::Number(ref right) = *other {\n            // The left side is not a number, but the right side is, so the result is the left\n            // side unit.\n            self.map(|v| v * *right).is_ok()\n        } else {\n            // Neither side is a number, so a product is not possible.\n            false\n        }\n    }\n\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::Leaf::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        match (self, other) {\n            (&Number(one), &Number(other)) => {\n                return Ok(Leaf::Number(op(one, other)));\n            },\n            (&Percentage(one), &Percentage(other)) => {\n                return Ok(Leaf::Percentage(op(one, other)));\n            },\n            (&Angle(ref one), &Angle(ref other)) => {\n                return Ok(Leaf::Angle(specified::Angle::from_calc(op(\n                    one.degrees(),\n                    other.degrees(),\n                ))));\n            },\n            (&Resolution(ref one), &Resolution(ref other)) => {\n                return Ok(Leaf::Resolution(specified::Resolution::from_dppx(op(\n                    one.dppx(),\n                    other.dppx(),\n                ))));\n            },\n            (&Time(ref one), &Time(ref other)) => {\n                return Ok(Leaf::Time(specified::Time::from_seconds(op(\n                    one.seconds(),\n                    other.seconds(),\n                ))));\n            },\n            (&Length(ref one), &Length(ref other)) => {\n                return Ok(Leaf::Length(one.try_op(other, op)?));\n            },\n            (&ColorComponent(..), &ColorComponent(..)) => {\n                return Err(());\n            },\n            _ => {\n                match *other {\n                    Number(..) | Percentage(..) | Angle(..) | Time(..) | Length(..)\n                    | Resolution(..) | ColorComponent(..) => {},\n                }\n                unsafe {\n                    debug_unreachable!();\n                }\n            },\n        }\n    }\n\n    fn map(&mut self, mut op: impl FnMut(f32) -> f32) -> Result<(), ()> {\n        Ok(match self {\n            Leaf::Length(one) => *one = one.map(op),\n            Leaf::Angle(one) => *one = specified::Angle::from_calc(op(one.degrees())),\n            Leaf::Time(one) => *one = specified::Time::from_seconds(op(one.seconds())),\n            Leaf::Resolution(one) => *one = specified::Resolution::from_dppx(op(one.dppx())),\n            Leaf::Percentage(one) => *one = op(*one),\n            Leaf::Number(one) => *one = op(*one),\n            Leaf::ColorComponent(..) => return Err(()),\n        })\n    }\n}\n\nimpl GenericAnchorSide<Box<CalcNode>> {\n    fn parse_in_calc<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(k) = input.try_parse(|i| AnchorSideKeyword::parse(i)) {\n            return Ok(Self::Keyword(k));\n        }\n        Ok(Self::Percentage(Box::new(CalcNode::parse_argument(\n            context,\n            input,\n            AllowParse::new(CalcUnits::PERCENTAGE),\n        )?)))\n    }\n}\n\nfn parse_anchor_function_fallback<'i, 't>(\n    context: &ParserContext,\n    additional_functions: AdditionalFunctions,\n    input: &mut Parser<'i, 't>,\n) -> Result<Box<GenericAnchorFunctionFallback<Leaf>>, ParseError<'i>> {\n    if let Ok(l) = input.try_parse(|i| -> Result<CalcNode, ParseError<'i>> {\n        Ok(CalcNode::Leaf(match i.next()? {\n            &Token::Number { value, .. } => {\n                if value != 0.0 {\n                    return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                Leaf::Length(NoCalcLength::Absolute(AbsoluteLength::Px(0.0)))\n            },\n            &Token::Dimension {\n                value, ref unit, ..\n            } => Leaf::Length(\n                NoCalcLength::parse_dimension_with_context(context, value, unit)\n                    .map_err(|_| i.new_custom_error(StyleParseErrorKind::UnspecifiedError))?,\n            ),\n            &Token::Percentage { unit_value, .. } => Leaf::Percentage(unit_value),\n            _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }))\n    }) {\n        return Ok(Box::new(GenericAnchorFunctionFallback::new(false, l)));\n    }\n    let node = CalcNode::parse_argument(\n        context,\n        input,\n        AllowParse {\n            units: CalcUnits::LENGTH_PERCENTAGE,\n            additional_functions,\n        },\n    )?\n    .into_length_or_percentage(AllowedNumericType::All)\n    .map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?\n    .node;\n    Ok(Box::new(GenericAnchorFunctionFallback::new(true, node)))\n}\n\nimpl GenericAnchorFunction<Box<CalcNode>, Box<GenericAnchorFunctionFallback<Leaf>>> {\n    fn parse_in_calc<'i, 't>(\n        context: &ParserContext,\n        additional_functions: AdditionalFunctions,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        input.parse_nested_block(|i| {\n            let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();\n            let side = GenericAnchorSide::parse_in_calc(context, i)?;\n            let target_element = if target_element.is_none() {\n                i.try_parse(|i| DashedIdent::parse(context, i)).ok()\n            } else {\n                target_element\n            };\n            let fallback = i\n                .try_parse(|i| {\n                    i.expect_comma()?;\n                    parse_anchor_function_fallback(context, additional_functions, i)\n                })\n                .ok();\n            Ok(Self {\n                target_element: TreeScoped::with_default_level(\n                    target_element.unwrap_or_else(DashedIdent::empty),\n                ),\n                side,\n                fallback: fallback.into(),\n            })\n        })\n    }\n}\n\nimpl GenericAnchorSizeFunction<Box<GenericAnchorFunctionFallback<Leaf>>> {\n    fn parse_in_calc<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        GenericAnchorSizeFunction::parse_inner(context, input, |i| {\n            parse_anchor_function_fallback(context, AdditionalFunctions::ANCHOR_SIZE, i)\n        })\n    }\n}\n\n/// Specified `anchor()` function in math functions.\npub type CalcAnchorFunction = generic::GenericCalcAnchorFunction<Leaf>;\n/// Specified `anchor-size()` function in math functions.\npub type CalcAnchorSizeFunction = generic::GenericCalcAnchorSizeFunction<Leaf>;\n\n/// A calc node representation for specified values.\npub type CalcNode = generic::GenericCalcNode<Leaf>;\nimpl CalcNode {\n    /// Tries to parse a single element in the expression, that is, a\n    /// `<length>`, `<angle>`, `<time>`, `<percentage>`, `<resolution>`, etc.\n    ///\n    /// May return a \"complex\" `CalcNode`, in the presence of a parenthesized\n    /// expression, for example.\n    fn parse_one<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allowed: AllowParse,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        match input.next()? {\n            &Token::Number { value, .. } => Ok(CalcNode::Leaf(Leaf::Number(value))),\n            &Token::Dimension {\n                value, ref unit, ..\n            } => {\n                if allowed.includes(CalcUnits::LENGTH) {\n                    if let Ok(l) = NoCalcLength::parse_dimension_with_context(context, value, unit)\n                    {\n                        return Ok(CalcNode::Leaf(Leaf::Length(l)));\n                    }\n                }\n                if allowed.includes(CalcUnits::ANGLE) {\n                    if let Ok(a) = Angle::parse_dimension(value, unit, /* from_calc = */ true) {\n                        return Ok(CalcNode::Leaf(Leaf::Angle(a)));\n                    }\n                }\n                if allowed.includes(CalcUnits::TIME) {\n                    if let Ok(t) = Time::parse_dimension(value, unit) {\n                        return Ok(CalcNode::Leaf(Leaf::Time(t)));\n                    }\n                }\n                if allowed.includes(CalcUnits::RESOLUTION) {\n                    if let Ok(t) = Resolution::parse_dimension(value, unit) {\n                        return Ok(CalcNode::Leaf(Leaf::Resolution(t)));\n                    }\n                }\n                return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            },\n            &Token::Percentage { unit_value, .. } if allowed.includes(CalcUnits::PERCENTAGE) => {\n                Ok(CalcNode::Leaf(Leaf::Percentage(unit_value)))\n            },\n            &Token::ParenthesisBlock => {\n                input.parse_nested_block(|input| CalcNode::parse_argument(context, input, allowed))\n            },\n            &Token::Function(ref name)\n                if allowed\n                    .additional_functions\n                    .intersects(AdditionalFunctions::ANCHOR)\n                    && name.eq_ignore_ascii_case(\"anchor\") =>\n            {\n                let anchor_function = GenericAnchorFunction::parse_in_calc(\n                    context,\n                    allowed.additional_functions,\n                    input,\n                )?;\n                Ok(CalcNode::Anchor(Box::new(anchor_function)))\n            },\n            &Token::Function(ref name)\n                if allowed\n                    .additional_functions\n                    .intersects(AdditionalFunctions::ANCHOR_SIZE)\n                    && name.eq_ignore_ascii_case(\"anchor-size\") =>\n            {\n                let anchor_size_function =\n                    GenericAnchorSizeFunction::parse_in_calc(context, input)?;\n                Ok(CalcNode::AnchorSize(Box::new(anchor_size_function)))\n            },\n            &Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                CalcNode::parse(context, input, function, allowed)\n            },\n            &Token::Ident(ref ident) => {\n                let leaf = match_ignore_ascii_case! { &**ident,\n                    \"e\" => Leaf::Number(std::f32::consts::E),\n                    \"pi\" => Leaf::Number(std::f32::consts::PI),\n                    \"infinity\" => Leaf::Number(f32::INFINITY),\n                    \"-infinity\" => Leaf::Number(f32::NEG_INFINITY),\n                    \"nan\" => Leaf::Number(f32::NAN),\n                    _ => {\n                        if crate::color::parsing::rcs_enabled() &&\n                            allowed.includes(CalcUnits::COLOR_COMPONENT)\n                        {\n                            if let Ok(channel_keyword) = ChannelKeyword::from_ident(&ident) {\n                                Leaf::ColorComponent(channel_keyword)\n                            } else {\n                                return Err(location\n                                    .new_unexpected_token_error(Token::Ident(ident.clone())));\n                            }\n                        } else {\n                            return Err(\n                                location.new_unexpected_token_error(Token::Ident(ident.clone()))\n                            );\n                        }\n                    },\n                };\n                Ok(CalcNode::Leaf(leaf))\n            },\n            t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n\n    /// Parse a top-level `calc` expression, with all nested sub-expressions.\n    ///\n    /// This is in charge of parsing, for example, `2 + 3 * 100%`.\n    pub fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        function: MathFunction,\n        allowed: AllowParse,\n    ) -> Result<Self, ParseError<'i>> {\n        input.parse_nested_block(|input| {\n            match function {\n                MathFunction::Calc => Self::parse_argument(context, input, allowed),\n                MathFunction::Clamp => {\n                    let min_val = if input\n                        .try_parse(|min| min.expect_ident_matching(\"none\"))\n                        .ok()\n                        .is_none()\n                    {\n                        Some(Self::parse_argument(context, input, allowed)?)\n                    } else {\n                        None\n                    };\n\n                    input.expect_comma()?;\n                    let center = Self::parse_argument(context, input, allowed)?;\n                    input.expect_comma()?;\n\n                    let max_val = if input\n                        .try_parse(|max| max.expect_ident_matching(\"none\"))\n                        .ok()\n                        .is_none()\n                    {\n                        Some(Self::parse_argument(context, input, allowed)?)\n                    } else {\n                        None\n                    };\n\n                    // Specification does not state how serialization should occur for clamp\n                    // https://github.com/w3c/csswg-drafts/issues/13535\n                    // tentatively partially serialize to min/max\n                    // clamp(MIN, VAL, none) is equivalent to max(MIN, VAL)\n                    // clamp(none, VAL, MAX) is equivalent to min(VAL, MAX)\n                    // clamp(none, VAL, none) is equivalent to just calc(VAL)\n                    Ok(match (min_val, max_val) {\n                        (None, None) => center,\n                        (None, Some(max)) => Self::MinMax(vec![center, max].into(), MinMaxOp::Min),\n                        (Some(min), None) => Self::MinMax(vec![min, center].into(), MinMaxOp::Max),\n                        (Some(min), Some(max)) => Self::Clamp {\n                            min: Box::new(min),\n                            center: Box::new(center),\n                            max: Box::new(max),\n                        },\n                    })\n                },\n                MathFunction::Round => {\n                    let strategy = input.try_parse(parse_rounding_strategy);\n\n                    // <rounding-strategy> = nearest | up | down | to-zero\n                    // https://drafts.csswg.org/css-values-4/#calc-syntax\n                    fn parse_rounding_strategy<'i, 't>(\n                        input: &mut Parser<'i, 't>,\n                    ) -> Result<RoundingStrategy, ParseError<'i>> {\n                        Ok(try_match_ident_ignore_ascii_case! { input,\n                            \"nearest\" => RoundingStrategy::Nearest,\n                            \"up\" => RoundingStrategy::Up,\n                            \"down\" => RoundingStrategy::Down,\n                            \"to-zero\" => RoundingStrategy::ToZero,\n                        })\n                    }\n\n                    if strategy.is_ok() {\n                        input.expect_comma()?;\n                    }\n\n                    let value = Self::parse_argument(context, input, allowed)?;\n\n                    // <step> defaults to the number 1 if not provided\n                    // https://drafts.csswg.org/css-values-4/#funcdef-round\n                    let step = input.try_parse(|input| {\n                        input.expect_comma()?;\n                        Self::parse_argument(context, input, allowed)\n                    });\n\n                    let step = step.unwrap_or(Self::Leaf(Leaf::Number(1.0)));\n\n                    Ok(Self::Round {\n                        strategy: strategy.unwrap_or(RoundingStrategy::Nearest),\n                        value: Box::new(value),\n                        step: Box::new(step),\n                    })\n                },\n                MathFunction::Mod | MathFunction::Rem => {\n                    let dividend = Self::parse_argument(context, input, allowed)?;\n                    input.expect_comma()?;\n                    let divisor = Self::parse_argument(context, input, allowed)?;\n\n                    let op = match function {\n                        MathFunction::Mod => ModRemOp::Mod,\n                        MathFunction::Rem => ModRemOp::Rem,\n                        _ => unreachable!(),\n                    };\n                    Ok(Self::ModRem {\n                        dividend: Box::new(dividend),\n                        divisor: Box::new(divisor),\n                        op,\n                    })\n                },\n                MathFunction::Min | MathFunction::Max => {\n                    // TODO(emilio): The common case for parse_comma_separated\n                    // is just one element, but for min / max is two, really...\n                    //\n                    // Consider adding an API to cssparser to specify the\n                    // initial vector capacity?\n                    let arguments = input.parse_comma_separated(|input| {\n                        let result = Self::parse_argument(context, input, allowed)?;\n                        Ok(result)\n                    })?;\n\n                    let op = match function {\n                        MathFunction::Min => MinMaxOp::Min,\n                        MathFunction::Max => MinMaxOp::Max,\n                        _ => unreachable!(),\n                    };\n\n                    Ok(Self::MinMax(arguments.into(), op))\n                },\n                MathFunction::Sin | MathFunction::Cos | MathFunction::Tan => {\n                    let a = Self::parse_angle_argument(context, input)?;\n\n                    let number = match function {\n                        MathFunction::Sin => a.sin(),\n                        MathFunction::Cos => a.cos(),\n                        MathFunction::Tan => a.tan(),\n                        _ => unsafe {\n                            debug_unreachable!(\"We just checked!\");\n                        },\n                    };\n\n                    Ok(Self::Leaf(Leaf::Number(number)))\n                },\n                MathFunction::Asin | MathFunction::Acos | MathFunction::Atan => {\n                    let a = Self::parse_number_argument(context, input)?;\n\n                    let radians = match function {\n                        MathFunction::Asin => a.asin(),\n                        MathFunction::Acos => a.acos(),\n                        MathFunction::Atan => a.atan(),\n                        _ => unsafe {\n                            debug_unreachable!(\"We just checked!\");\n                        },\n                    };\n\n                    Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))\n                },\n                MathFunction::Atan2 => {\n                    let allow_all = allowed.new_including(CalcUnits::ALL);\n                    let a = Self::parse_argument(context, input, allow_all)?;\n                    input.expect_comma()?;\n                    let b = Self::parse_argument(context, input, allow_all)?;\n\n                    let radians = Self::try_resolve(input, || {\n                        if let Ok(a) = a.to_number() {\n                            let b = b.to_number()?;\n                            return Ok(a.atan2(b));\n                        }\n\n                        if let Ok(a) = a.to_percentage() {\n                            let b = b.to_percentage()?;\n                            return Ok(a.atan2(b));\n                        }\n\n                        if let Ok(a) = a.to_time(None) {\n                            let b = b.to_time(None)?;\n                            return Ok(a.seconds().atan2(b.seconds()));\n                        }\n\n                        if let Ok(a) = a.to_angle() {\n                            let b = b.to_angle()?;\n                            return Ok(a.radians().atan2(b.radians()));\n                        }\n\n                        if let Ok(a) = a.to_resolution() {\n                            let b = b.to_resolution()?;\n                            return Ok(a.dppx().atan2(b.dppx()));\n                        }\n\n                        let a = a.into_length_or_percentage(AllowedNumericType::All)?;\n                        let b = b.into_length_or_percentage(AllowedNumericType::All)?;\n                        let (a, b) = CalcLengthPercentage::same_unit_length_as(&a, &b).ok_or(())?;\n\n                        Ok(a.atan2(b))\n                    })?;\n\n                    Ok(Self::Leaf(Leaf::Angle(Angle::from_radians(radians))))\n                },\n                MathFunction::Pow => {\n                    let a = Self::parse_number_argument(context, input)?;\n                    input.expect_comma()?;\n                    let b = Self::parse_number_argument(context, input)?;\n\n                    let number = a.powf(b);\n\n                    Ok(Self::Leaf(Leaf::Number(number)))\n                },\n                MathFunction::Sqrt => {\n                    let a = Self::parse_number_argument(context, input)?;\n\n                    let number = a.sqrt();\n\n                    Ok(Self::Leaf(Leaf::Number(number)))\n                },\n                MathFunction::Hypot => {\n                    let arguments = input.parse_comma_separated(|input| {\n                        let result = Self::parse_argument(context, input, allowed)?;\n                        Ok(result)\n                    })?;\n\n                    Ok(Self::Hypot(arguments.into()))\n                },\n                MathFunction::Log => {\n                    let a = Self::parse_number_argument(context, input)?;\n                    let b = input\n                        .try_parse(|input| {\n                            input.expect_comma()?;\n                            Self::parse_number_argument(context, input)\n                        })\n                        .ok();\n\n                    let number = match b {\n                        Some(b) => a.log(b),\n                        None => a.ln(),\n                    };\n\n                    Ok(Self::Leaf(Leaf::Number(number)))\n                },\n                MathFunction::Exp => {\n                    let a = Self::parse_number_argument(context, input)?;\n                    let number = a.exp();\n                    Ok(Self::Leaf(Leaf::Number(number)))\n                },\n                MathFunction::Abs => {\n                    let node = Self::parse_argument(context, input, allowed)?;\n                    Ok(Self::Abs(Box::new(node)))\n                },\n                MathFunction::Sign => {\n                    // The sign of a percentage is dependent on the percentage basis, so if\n                    // percentages aren't allowed (so there's no basis) we shouldn't allow them in\n                    // sign(). The rest of the units are safe tho.\n                    let node = Self::parse_argument(\n                        context,\n                        input,\n                        allowed.new_including(CalcUnits::ALL - CalcUnits::PERCENTAGE),\n                    )?;\n                    Ok(Self::Sign(Box::new(node)))\n                },\n            }\n        })\n    }\n\n    fn parse_angle_argument<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<CSSFloat, ParseError<'i>> {\n        let argument = Self::parse_argument(context, input, AllowParse::new(CalcUnits::ANGLE))?;\n        argument\n            .to_number()\n            .or_else(|()| Ok(argument.to_angle()?.radians()))\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    fn parse_number_argument<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<CSSFloat, ParseError<'i>> {\n        Self::parse_argument(context, input, AllowParse::new(CalcUnits::empty()))?\n            .to_number()\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    fn parse_argument<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allowed: AllowParse,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut sum = SmallVec::<[CalcNode; 1]>::new();\n        let first = Self::parse_product(context, input, allowed)?;\n        sum.push(first);\n        loop {\n            let start = input.state();\n            match input.next_including_whitespace() {\n                Ok(&Token::WhiteSpace(_)) => {\n                    if input.is_exhausted() {\n                        break; // allow trailing whitespace\n                    }\n                    match *input.next()? {\n                        Token::Delim('+') => {\n                            let rhs = Self::parse_product(context, input, allowed)?;\n                            if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {\n                                sum.push(rhs);\n                            }\n                        },\n                        Token::Delim('-') => {\n                            let mut rhs = Self::parse_product(context, input, allowed)?;\n                            rhs.negate();\n                            if sum.last_mut().unwrap().try_sum_in_place(&rhs).is_err() {\n                                sum.push(rhs);\n                            }\n                        },\n                        _ => {\n                            input.reset(&start);\n                            break;\n                        },\n                    }\n                },\n                _ => {\n                    input.reset(&start);\n                    break;\n                },\n            }\n        }\n\n        Ok(if sum.len() == 1 {\n            sum.drain(..).next().unwrap()\n        } else {\n            Self::Sum(sum.into_boxed_slice().into())\n        })\n    }\n\n    /// Parse a top-level `calc` expression, and all the products that may\n    /// follow, and stop as soon as a non-product expression is found.\n    ///\n    /// This should parse correctly:\n    ///\n    /// * `2`\n    /// * `2 * 2`\n    /// * `2 * 2 + 2` (but will leave the `+ 2` unparsed).\n    ///\n    fn parse_product<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allowed: AllowParse,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut product = SmallVec::<[CalcNode; 1]>::new();\n        let first = Self::parse_one(context, input, allowed)?;\n        product.push(first);\n\n        loop {\n            let start = input.state();\n            match input.next() {\n                Ok(&Token::Delim('*')) => {\n                    let mut rhs = Self::parse_one(context, input, allowed)?;\n\n                    // We can unwrap here, becuase we start the function by adding a node to\n                    // the list.\n                    if !product.last_mut().unwrap().try_product_in_place(&mut rhs) {\n                        product.push(rhs);\n                    }\n                },\n                Ok(&Token::Delim('/')) => {\n                    let rhs = Self::parse_one(context, input, allowed)?;\n\n                    enum InPlaceDivisionResult {\n                        /// The right was merged into the left.\n                        Merged,\n                        /// The right is not a number or could not be resolved, so the left is\n                        /// unchanged.\n                        Unchanged,\n                        /// The right was resolved, but was not a number, so the calculation is\n                        /// invalid.\n                        Invalid,\n                    }\n\n                    fn try_division_in_place(\n                        left: &mut CalcNode,\n                        right: &CalcNode,\n                    ) -> InPlaceDivisionResult {\n                        if let Ok(resolved) = right.resolve() {\n                            if let Some(number) = resolved.as_number() {\n                                if number != 1.0 && left.is_product_distributive() {\n                                    if left.map(|l| l / number).is_err() {\n                                        return InPlaceDivisionResult::Invalid;\n                                    }\n                                    return InPlaceDivisionResult::Merged;\n                                }\n                            } else {\n                                // Color components are valid denominators, but they can't resolve\n                                // at parse time.\n                                return if resolved.unit().contains(CalcUnits::COLOR_COMPONENT) {\n                                    InPlaceDivisionResult::Unchanged\n                                } else {\n                                    InPlaceDivisionResult::Invalid\n                                };\n                            }\n                        }\n                        InPlaceDivisionResult::Unchanged\n                    }\n\n                    // The right hand side of a division *must* be a number, so if we can\n                    // already resolve it, then merge it with the last node on the product list.\n                    // We can unwrap here, becuase we start the function by adding a node to\n                    // the list.\n                    match try_division_in_place(&mut product.last_mut().unwrap(), &rhs) {\n                        InPlaceDivisionResult::Merged => {},\n                        InPlaceDivisionResult::Unchanged => {\n                            product.push(Self::Invert(Box::new(rhs)))\n                        },\n                        InPlaceDivisionResult::Invalid => {\n                            return Err(\n                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                            )\n                        },\n                    }\n                },\n                _ => {\n                    input.reset(&start);\n                    break;\n                },\n            }\n        }\n\n        Ok(if product.len() == 1 {\n            product.drain(..).next().unwrap()\n        } else {\n            Self::Product(product.into_boxed_slice().into())\n        })\n    }\n\n    fn try_resolve<'i, 't, F>(\n        input: &Parser<'i, 't>,\n        closure: F,\n    ) -> Result<CSSFloat, ParseError<'i>>\n    where\n        F: FnOnce() -> Result<CSSFloat, ()>,\n    {\n        closure().map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Tries to simplify this expression into a `<length>` or `<percentage>`\n    /// value.\n    pub fn into_length_or_percentage(\n        mut self,\n        clamping_mode: AllowedNumericType,\n    ) -> Result<CalcLengthPercentage, ()> {\n        self.simplify_and_sort();\n\n        // Although we allow numbers inside CalcLengthPercentage, calculations that resolve to a\n        // number result is still not allowed.\n        let unit = self.unit()?;\n        if !CalcUnits::LENGTH_PERCENTAGE.intersects(unit) {\n            Err(())\n        } else {\n            Ok(CalcLengthPercentage {\n                clamping_mode,\n                node: self,\n            })\n        }\n    }\n\n    /// Tries to simplify this expression into a `<time>` value.\n    fn to_time(&self, clamping_mode: Option<AllowedNumericType>) -> Result<Time, ()> {\n        let seconds = if let Leaf::Time(time) = self.resolve()? {\n            time.seconds()\n        } else {\n            return Err(());\n        };\n\n        Ok(Time::from_seconds_with_calc_clamping_mode(\n            seconds,\n            clamping_mode,\n        ))\n    }\n\n    /// Tries to simplify the expression into a `<resolution>` value.\n    fn to_resolution(&self) -> Result<Resolution, ()> {\n        let dppx = if let Leaf::Resolution(resolution) = self.resolve()? {\n            resolution.dppx()\n        } else {\n            return Err(());\n        };\n\n        Ok(Resolution::from_dppx_calc(dppx))\n    }\n\n    /// Tries to simplify this expression into an `Angle` value.\n    fn to_angle(&self) -> Result<Angle, ()> {\n        let degrees = if let Leaf::Angle(angle) = self.resolve()? {\n            angle.degrees()\n        } else {\n            return Err(());\n        };\n\n        let result = Angle::from_calc(degrees);\n        Ok(result)\n    }\n\n    /// Tries to simplify this expression into a `<number>` value.\n    fn to_number(&self) -> Result<CSSFloat, ()> {\n        let number = if let Leaf::Number(number) = self.resolve()? {\n            number\n        } else {\n            return Err(());\n        };\n\n        let result = number;\n\n        Ok(result)\n    }\n\n    /// Tries to simplify this expression into a `<percentage>` value.\n    fn to_percentage(&self) -> Result<CSSFloat, ()> {\n        if let Leaf::Percentage(percentage) = self.resolve()? {\n            Ok(percentage)\n        } else {\n            Err(())\n        }\n    }\n\n    /// Given a function name, and the location from where the token came from,\n    /// return a mathematical function corresponding to that name or an error.\n    #[inline]\n    pub fn math_function<'i>(\n        _: &ParserContext,\n        name: &CowRcStr<'i>,\n        location: cssparser::SourceLocation,\n    ) -> Result<MathFunction, ParseError<'i>> {\n        let function = match MathFunction::from_ident(&*name) {\n            Ok(f) => f,\n            Err(()) => {\n                return Err(location.new_unexpected_token_error(Token::Function(name.clone())))\n            },\n        };\n\n        Ok(function)\n    }\n\n    /// Convenience parsing function for `<length> | <percentage>`, and, optionally, `anchor()`.\n    pub fn parse_length_or_percentage<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        clamping_mode: AllowedNumericType,\n        function: MathFunction,\n        allow_anchor: AllowAnchorPositioningFunctions,\n    ) -> Result<CalcLengthPercentage, ParseError<'i>> {\n        let allowed = if allow_anchor == AllowAnchorPositioningFunctions::No {\n            AllowParse::new(CalcUnits::LENGTH_PERCENTAGE)\n        } else {\n            AllowParse {\n                units: CalcUnits::LENGTH_PERCENTAGE,\n                additional_functions: match allow_anchor {\n                    AllowAnchorPositioningFunctions::No => unreachable!(),\n                    AllowAnchorPositioningFunctions::AllowAnchorSize => {\n                        AdditionalFunctions::ANCHOR_SIZE\n                    },\n                    AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize => {\n                        AdditionalFunctions::ANCHOR | AdditionalFunctions::ANCHOR_SIZE\n                    },\n                },\n            }\n        };\n        Self::parse(context, input, function, allowed)?\n            .into_length_or_percentage(clamping_mode)\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for percentages.\n    pub fn parse_percentage<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        function: MathFunction,\n    ) -> Result<CSSFloat, ParseError<'i>> {\n        Self::parse(\n            context,\n            input,\n            function,\n            AllowParse::new(CalcUnits::PERCENTAGE),\n        )?\n        .to_percentage()\n        .map(crate::values::normalize)\n        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for `<length>`.\n    pub fn parse_length<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        clamping_mode: AllowedNumericType,\n        function: MathFunction,\n    ) -> Result<CalcLengthPercentage, ParseError<'i>> {\n        Self::parse(context, input, function, AllowParse::new(CalcUnits::LENGTH))?\n            .into_length_or_percentage(clamping_mode)\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for `<number>`.\n    pub fn parse_number<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        function: MathFunction,\n    ) -> Result<CSSFloat, ParseError<'i>> {\n        Self::parse(\n            context,\n            input,\n            function,\n            AllowParse::new(CalcUnits::empty()),\n        )?\n        .to_number()\n        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for `<angle>`.\n    pub fn parse_angle<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        function: MathFunction,\n    ) -> Result<Angle, ParseError<'i>> {\n        Self::parse(context, input, function, AllowParse::new(CalcUnits::ANGLE))?\n            .to_angle()\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for `<time>`.\n    pub fn parse_time<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        clamping_mode: AllowedNumericType,\n        function: MathFunction,\n    ) -> Result<Time, ParseError<'i>> {\n        Self::parse(context, input, function, AllowParse::new(CalcUnits::TIME))?\n            .to_time(Some(clamping_mode))\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n\n    /// Convenience parsing function for `<resolution>`.\n    pub fn parse_resolution<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        function: MathFunction,\n    ) -> Result<Resolution, ParseError<'i>> {\n        Self::parse(\n            context,\n            input,\n            function,\n            AllowParse::new(CalcUnits::RESOLUTION),\n        )?\n        .to_resolution()\n        .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/color.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified color values.\n\nuse super::AllowQuirks;\nuse crate::color::mix::ColorInterpolationMethod;\nuse crate::color::{parsing, AbsoluteColor, ColorFunction, ColorMixItemList, ColorSpace};\nuse crate::derives::*;\nuse crate::device::Device;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};\nuse crate::values::generics::color::{\n    ColorMixFlags, GenericCaretColor, GenericColorMix, GenericColorMixItem, GenericColorOrAuto,\n    GenericLightDark,\n};\nuse crate::values::specified::percentage::ToPercentage;\nuse crate::values::specified::Percentage;\nuse crate::values::{normalize, CustomIdent};\nuse cssparser::{match_ignore_ascii_case, BasicParseErrorKind, ParseErrorKind, Parser, Token};\nuse std::fmt::{self, Write};\nuse std::io::Write as IoWrite;\nuse style_traits::{\n    owned_slice::OwnedSlice, CssType, CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo,\n    StyleParseErrorKind, ToCss, ValueParseErrorKind,\n};\n\n/// A specified color-mix().\npub type ColorMix = GenericColorMix<Color, Percentage>;\n\nimpl ColorMix {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        preserve_authored: PreserveAuthored,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"color-mix\")?;\n\n        input.parse_nested_block(|input| {\n            // If the color interpolation method is omitted, default to \"in oklab\".\n            // See: https://github.com/web-platform-tests/interop/issues/1166\n            let interpolation = input\n                .try_parse(|input| -> Result<_, ParseError<'i>> {\n                    let interpolation = ColorInterpolationMethod::parse(context, input)?;\n                    input.expect_comma()?;\n                    Ok(interpolation)\n                })\n                .unwrap_or_default();\n\n            let try_parse_percentage = |input: &mut Parser| -> Option<Percentage> {\n                input\n                    .try_parse(|input| Percentage::parse_zero_to_a_hundred(context, input))\n                    .ok()\n            };\n\n            let allow_multiple_items =\n                static_prefs::pref!(\"layout.css.color-mix-multi-color.enabled\");\n\n            let mut items = ColorMixItemList::default();\n\n            loop {\n                let mut percentage = try_parse_percentage(input);\n\n                let color = Color::parse_internal(context, input, preserve_authored)?;\n\n                if percentage.is_none() {\n                    percentage = try_parse_percentage(input);\n                }\n\n                items.push((color, percentage));\n\n                if input.try_parse(|i| i.expect_comma()).is_err() {\n                    break;\n                }\n\n                // Early exit to avoid parsing more than 2 colors if the pref is not enabled.\n                if !allow_multiple_items && items.len() == 2 {\n                    break;\n                }\n            }\n\n            // ...the color-mix() function takes a list of one or more <color> specifications...\n            // <https://drafts.csswg.org/css-color-5/#color-mix>\n            let min_item_count = if allow_multiple_items { 1 } else { 2 };\n            if items.len() < min_item_count {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            // Normalize percentages per:\n            // https://drafts.csswg.org/css-values-5/#normalize-mix-percentages\n            let (mut sum_specified, mut missing) = (0.0, 0);\n            for (_, percentage) in items.iter() {\n                if let Some(p) = percentage {\n                    sum_specified += p.to_percentage();\n                } else {\n                    missing += 1;\n                }\n            }\n\n            let default_for_missing_items = match missing {\n                0 => None,\n                m if m == items.len() => Some(Percentage::new(1.0 / items.len() as f32)),\n                m => Some(Percentage::new((1.0 - sum_specified) / m as f32)),\n            };\n\n            if let Some(default) = default_for_missing_items {\n                for (_, percentage) in items.iter_mut() {\n                    if percentage.is_none() {\n                        *percentage = Some(default);\n                    }\n                }\n            }\n\n            let mut total = 0.0;\n            let finalized = items\n                .into_iter()\n                .map(|(color, percentage)| {\n                    let percentage = percentage.expect(\"percentage filled above\");\n                    total += percentage.to_percentage();\n                    GenericColorMixItem { color, percentage }\n                })\n                .collect::<ColorMixItemList<_>>();\n\n            if total <= 0.0 {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            // Pass RESULT_IN_MODERN_SYNTAX here, because the result of the color-mix() function\n            // should always be in the modern color syntax to allow for out of gamut results and\n            // to preserve floating point precision.\n            Ok(ColorMix {\n                interpolation,\n                items: OwnedSlice::from_slice(&finalized),\n                flags: ColorMixFlags::NORMALIZE_WEIGHTS | ColorMixFlags::RESULT_IN_MODERN_SYNTAX,\n            })\n        })\n    }\n}\n\n/// Container holding an absolute color and the text specified by an author.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct Absolute {\n    /// The specified color.\n    pub color: AbsoluteColor,\n    /// Authored representation.\n    pub authored: Option<Box<str>>,\n}\n\nimpl ToCss for Absolute {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if let Some(ref authored) = self.authored {\n            dest.write_str(authored)\n        } else {\n            self.color.to_css(dest)\n        }\n    }\n}\n\n/// Specified color value\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem, ToTyped)]\n#[typed(todo_derive_fields)]\npub enum Color {\n    /// The 'currentColor' keyword\n    CurrentColor,\n    /// An absolute color.\n    /// https://w3c.github.io/csswg-drafts/css-color-4/#typedef-absolute-color-function\n    Absolute(Box<Absolute>),\n    /// A color function that could not be resolved to a [Color::Absolute] color at parse time.\n    /// Right now this is only the case for relative colors with `currentColor` as the origin.\n    ColorFunction(Box<ColorFunction<Self>>),\n    /// A system color.\n    System(SystemColor),\n    /// A color mix.\n    ColorMix(Box<ColorMix>),\n    /// A light-dark() color.\n    LightDark(Box<GenericLightDark<Self>>),\n    /// The contrast-color function.\n    ContrastColor(Box<Color>),\n    /// Quirksmode-only rule for inheriting color from the body\n    InheritFromBodyQuirk,\n}\n\nimpl From<AbsoluteColor> for Color {\n    #[inline]\n    fn from(value: AbsoluteColor) -> Self {\n        Self::from_absolute_color(value)\n    }\n}\n\n/// System colors. A bunch of these are ad-hoc, others come from Windows:\n///\n///   https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor\n///\n/// Others are HTML/CSS specific. Spec is:\n///\n///   https://drafts.csswg.org/css-color/#css-system-colors\n///   https://drafts.csswg.org/css-color/#deprecated-system-colors\n#[allow(missing_docs)]\n#[cfg(feature = \"gecko\")]\n#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum SystemColor {\n    Activeborder,\n    /// Background in the (active) titlebar.\n    Activecaption,\n    Appworkspace,\n    Background,\n    Buttonface,\n    Buttonhighlight,\n    Buttonshadow,\n    Buttontext,\n    Buttonborder,\n    /// Text color in the (active) titlebar.\n    Captiontext,\n    #[parse(aliases = \"-moz-field\")]\n    Field,\n    /// Used for disabled field backgrounds.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozDisabledfield,\n    #[parse(aliases = \"-moz-fieldtext\")]\n    Fieldtext,\n\n    Mark,\n    Marktext,\n\n    /// Combobox widgets\n    MozComboboxtext,\n    MozCombobox,\n\n    Graytext,\n    Highlight,\n    Highlighttext,\n    Inactiveborder,\n    /// Background in the (inactive) titlebar.\n    Inactivecaption,\n    /// Text color in the (inactive) titlebar.\n    Inactivecaptiontext,\n    Infobackground,\n    Infotext,\n    Menu,\n    Menutext,\n    Scrollbar,\n    Threeddarkshadow,\n    Threedface,\n    Threedhighlight,\n    Threedlightshadow,\n    Threedshadow,\n    Window,\n    Windowframe,\n    Windowtext,\n    #[parse(aliases = \"-moz-default-color\")]\n    Canvastext,\n    #[parse(aliases = \"-moz-default-background-color\")]\n    Canvas,\n    MozDialog,\n    MozDialogtext,\n    /// Used for selected but not focused cell backgrounds.\n    #[parse(aliases = \"-moz-html-cellhighlight\")]\n    MozCellhighlight,\n    /// Used for selected but not focused cell text.\n    #[parse(aliases = \"-moz-html-cellhighlighttext\")]\n    MozCellhighlighttext,\n    /// Used for selected and focused html cell backgrounds.\n    Selecteditem,\n    /// Used for selected and focused html cell text.\n    Selecteditemtext,\n    /// Used for menu item backgrounds when hovered.\n    MozMenuhover,\n    /// Used for menu item backgrounds when hovered and disabled.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMenuhoverdisabled,\n    /// Used for menu item text when hovered.\n    MozMenuhovertext,\n    /// Used for menubar item text when hovered.\n    MozMenubarhovertext,\n\n    /// On platforms where this color is the same as field, or transparent, use fieldtext as\n    /// foreground color.\n    MozOddtreerow,\n\n    /// Used for button text background when hovered.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonhoverface,\n    /// Used for button text color when hovered.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonhovertext,\n    /// Used for button border color when hovered.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonhoverborder,\n    /// Used for button background when pressed.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonactiveface,\n    /// Used for button text when pressed.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonactivetext,\n    /// Used for button border when pressed.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtonactiveborder,\n\n    /// Used for button background when disabled.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtondisabledface,\n    /// Used for button border when disabled.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButtondisabledborder,\n\n    /// Colors used for the header bar (sorta like the tab bar / menubar).\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozHeaderbar,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozHeaderbartext,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozHeaderbarinactive,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozHeaderbarinactivetext,\n\n    /// Foreground color of default buttons.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacDefaultbuttontext,\n    /// Ring color around text fields and lists.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacFocusring,\n    /// Text color of disabled text on toolbars.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozMacDisabledtoolbartext,\n    /// The background of a sidebar.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozSidebar,\n    /// The foreground color of a sidebar.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozSidebartext,\n    /// The border color of a sidebar.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozSidebarborder,\n\n    /// Theme accent color.\n    /// https://drafts.csswg.org/css-color-4/#valdef-system-color-accentcolor\n    Accentcolor,\n\n    /// Foreground for the accent color.\n    /// https://drafts.csswg.org/css-color-4/#valdef-system-color-accentcolortext\n    Accentcolortext,\n\n    /// The background-color for :autofill-ed inputs.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozAutofillBackground,\n\n    #[parse(aliases = \"-moz-hyperlinktext\")]\n    Linktext,\n    #[parse(aliases = \"-moz-activehyperlinktext\")]\n    Activetext,\n    #[parse(aliases = \"-moz-visitedhyperlinktext\")]\n    Visitedtext,\n\n    /// Color of tree column headers\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheader,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheadertext,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheaderhover,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheaderhovertext,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheaderactive,\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozColheaderactivetext,\n\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    TextSelectDisabledBackground,\n    #[css(skip)]\n    TextSelectAttentionBackground,\n    #[css(skip)]\n    TextSelectAttentionForeground,\n    #[css(skip)]\n    TextHighlightBackground,\n    #[css(skip)]\n    TextHighlightForeground,\n    #[css(skip)]\n    TargetTextBackground,\n    #[css(skip)]\n    TargetTextForeground,\n    #[css(skip)]\n    IMERawInputBackground,\n    #[css(skip)]\n    IMERawInputForeground,\n    #[css(skip)]\n    IMERawInputUnderline,\n    #[css(skip)]\n    IMESelectedRawTextBackground,\n    #[css(skip)]\n    IMESelectedRawTextForeground,\n    #[css(skip)]\n    IMESelectedRawTextUnderline,\n    #[css(skip)]\n    IMEConvertedTextBackground,\n    #[css(skip)]\n    IMEConvertedTextForeground,\n    #[css(skip)]\n    IMEConvertedTextUnderline,\n    #[css(skip)]\n    IMESelectedConvertedTextBackground,\n    #[css(skip)]\n    IMESelectedConvertedTextForeground,\n    #[css(skip)]\n    IMESelectedConvertedTextUnderline,\n    #[css(skip)]\n    SpellCheckerUnderline,\n    #[css(skip)]\n    ThemedScrollbar,\n    #[css(skip)]\n    ThemedScrollbarThumb,\n    #[css(skip)]\n    ThemedScrollbarThumbHover,\n    #[css(skip)]\n    ThemedScrollbarThumbActive,\n\n    #[css(skip)]\n    End, // Just for array-indexing purposes.\n}\n\n#[cfg(feature = \"gecko\")]\nimpl SystemColor {\n    #[inline]\n    fn compute(&self, cx: &Context) -> ComputedColor {\n        use crate::gecko_bindings::bindings;\n\n        let color = cx.device().system_nscolor(*self, cx.builder.color_scheme);\n        if cx.for_non_inherited_property {\n            cx.rule_cache_conditions\n                .borrow_mut()\n                .set_color_scheme_dependency(cx.builder.color_scheme);\n        }\n        if color == bindings::NS_SAME_AS_FOREGROUND_COLOR {\n            return ComputedColor::currentcolor();\n        }\n        ComputedColor::Absolute(AbsoluteColor::from_nscolor(color))\n    }\n}\n\n/// System colors. A bunch of these are ad-hoc, others come from Windows:\n///\n///   https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsyscolor\n///\n/// Others are HTML/CSS specific. Spec is:\n///\n///   https://drafts.csswg.org/css-color/#css-system-colors\n///   https://drafts.csswg.org/css-color/#deprecated-system-colors\n#[allow(missing_docs)]\n#[cfg(feature = \"servo\")]\n#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum SystemColor {\n    Accentcolor,\n    Accentcolortext,\n    Activetext,\n    Linktext,\n    Visitedtext,\n    Buttonborder,\n    Buttonface,\n    Buttontext,\n    Canvas,\n    Canvastext,\n    Field,\n    Fieldtext,\n    Graytext,\n    Highlight,\n    Highlighttext,\n    Mark,\n    Marktext,\n    Selecteditem,\n    Selecteditemtext,\n\n    // Deprecated system colors.\n    Activeborder,\n    Inactiveborder,\n    Threeddarkshadow,\n    Threedhighlight,\n    Threedlightshadow,\n    Threedshadow,\n    Windowframe,\n    Buttonhighlight,\n    Buttonshadow,\n    Threedface,\n    Activecaption,\n    Appworkspace,\n    Background,\n    Inactivecaption,\n    Infobackground,\n    Menu,\n    Scrollbar,\n    Window,\n    Captiontext,\n    Infotext,\n    Menutext,\n    Windowtext,\n    Inactivecaptiontext,\n}\n\n#[cfg(feature = \"servo\")]\nimpl SystemColor {\n    #[inline]\n    fn compute(&self, cx: &Context) -> ComputedColor {\n        if cx.for_non_inherited_property {\n            cx.rule_cache_conditions\n                .borrow_mut()\n                .set_color_scheme_dependency(cx.builder.color_scheme);\n        }\n\n        ComputedColor::Absolute(cx.device().system_color(*self, cx.builder.color_scheme))\n    }\n}\n\n/// Whether to preserve authored colors during parsing. That's useful only if we\n/// plan to serialize the color back.\n#[derive(Copy, Clone)]\nenum PreserveAuthored {\n    No,\n    Yes,\n}\n\nimpl Parse for Color {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, PreserveAuthored::Yes)\n    }\n}\n\nimpl Color {\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        preserve_authored: PreserveAuthored,\n    ) -> Result<Self, ParseError<'i>> {\n        let authored = match preserve_authored {\n            PreserveAuthored::No => None,\n            PreserveAuthored::Yes => {\n                // Currently we only store authored value for color keywords,\n                // because all browsers serialize those values as keywords for\n                // specified value.\n                let start = input.state();\n                let authored = input.expect_ident_cloned().ok();\n                input.reset(&start);\n                authored\n            },\n        };\n\n        match input.try_parse(|i| parsing::parse_color_with(context, i)) {\n            Ok(mut color) => {\n                if let Color::Absolute(ref mut absolute) = color {\n                    // Because we can't set the `authored` value at construction time, we have to set it\n                    // here.\n                    absolute.authored = authored.map(|s| s.to_ascii_lowercase().into_boxed_str());\n                }\n                Ok(color)\n            },\n            Err(e) => {\n                {\n                    #[cfg(feature = \"gecko\")]\n                    if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) {\n                        return Ok(Color::System(system));\n                    }\n                    #[cfg(feature = \"servo\")]\n                    if let Ok(system) = input.try_parse(SystemColor::parse) {\n                        return Ok(Color::System(system));\n                    }\n                }\n                if let Ok(mix) = input.try_parse(|i| ColorMix::parse(context, i, preserve_authored))\n                {\n                    return Ok(Color::ColorMix(Box::new(mix)));\n                }\n\n                if let Ok(ld) = input.try_parse(|i| {\n                    GenericLightDark::parse_with(i, |i| {\n                        Self::parse_internal(context, i, preserve_authored)\n                    })\n                }) {\n                    return Ok(Color::LightDark(Box::new(ld)));\n                }\n\n                if static_prefs::pref!(\"layout.css.contrast-color.enabled\") {\n                    if let Ok(c) = input.try_parse(|i| {\n                        i.expect_function_matching(\"contrast-color\")?;\n                        i.parse_nested_block(|i| {\n                            Self::parse_internal(context, i, preserve_authored)\n                        })\n                    }) {\n                        return Ok(Color::ContrastColor(Box::new(c)));\n                    }\n                }\n\n                match e.kind {\n                    ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken(t)) => {\n                        Err(e.location.new_custom_error(StyleParseErrorKind::ValueError(\n                            ValueParseErrorKind::InvalidColor(t),\n                        )))\n                    },\n                    _ => Err(e),\n                }\n            },\n        }\n    }\n\n    /// Returns whether a given color is valid for authors.\n    pub fn is_valid(context: &ParserContext, input: &mut Parser) -> bool {\n        input\n            .parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No))\n            .is_ok()\n    }\n\n    /// Tries to parse a color and compute it with a given device.\n    pub fn parse_and_compute(\n        context: &ParserContext,\n        input: &mut Parser,\n        device: Option<&Device>,\n    ) -> Result<ComputedColor, ()> {\n        use crate::error_reporting::ContextualParseError;\n        let start = input.position();\n        let result = input\n            .parse_entirely(|input| Self::parse_internal(context, input, PreserveAuthored::No));\n\n        let specified = match result {\n            Ok(s) => s,\n            Err(e) => {\n                if context.error_reporting_enabled() {\n                    // Ignore other kinds of errors that might be reported, such as\n                    // ParseErrorKind::Basic(BasicParseErrorKind::UnexpectedToken),\n                    // since Gecko didn't use to report those to the error console.\n                    //\n                    // TODO(emilio): Revise whether we want to keep this at all, we\n                    // use this only for canvas, this warnings are disabled by\n                    // default and not available on OffscreenCanvas anyways...\n                    if let ParseErrorKind::Custom(StyleParseErrorKind::ValueError(..)) = e.kind {\n                        let location = e.location.clone();\n                        let error =\n                            ContextualParseError::UnsupportedValue(input.slice_from(start), e);\n                        context.log_css_error(location, error);\n                    }\n                }\n                return Err(());\n            },\n        };\n\n        match device {\n            Some(device) => {\n                Context::for_media_query_evaluation(device, device.quirks_mode(), |context| {\n                    specified.to_computed_color(Some(&context))\n                })\n            },\n            None => specified.to_computed_color(None),\n        }\n    }\n}\n\nimpl ToCss for Color {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Color::CurrentColor => dest.write_str(\"currentcolor\"),\n            Color::Absolute(ref absolute) => absolute.to_css(dest),\n            Color::ColorFunction(ref color_function) => color_function.to_css(dest),\n            Color::ColorMix(ref mix) => mix.to_css(dest),\n            Color::LightDark(ref ld) => ld.to_css(dest),\n            Color::ContrastColor(ref c) => {\n                dest.write_str(\"contrast-color(\")?;\n                c.to_css(dest)?;\n                dest.write_char(')')\n            },\n            Color::System(system) => system.to_css(dest),\n            Color::InheritFromBodyQuirk => dest.write_str(\"-moz-inherit-from-body-quirk\"),\n        }\n    }\n}\n\nimpl Color {\n    /// Returns whether this color is allowed in forced-colors mode.\n    pub fn honored_in_forced_colors_mode(&self, allow_transparent: bool) -> bool {\n        match *self {\n            Self::InheritFromBodyQuirk => false,\n            Self::CurrentColor => true,\n            Self::System(..) => true,\n            Self::Absolute(ref absolute) => allow_transparent && absolute.color.is_transparent(),\n            Self::ColorFunction(ref color_function) => {\n                // For now we allow transparent colors if we can resolve the color function.\n                // <https://bugzilla.mozilla.org/show_bug.cgi?id=1923053>\n                color_function\n                    .resolve_to_absolute()\n                    .map(|resolved| allow_transparent && resolved.is_transparent())\n                    .unwrap_or(false)\n            },\n            Self::LightDark(ref ld) => {\n                ld.light.honored_in_forced_colors_mode(allow_transparent)\n                    && ld.dark.honored_in_forced_colors_mode(allow_transparent)\n            },\n            Self::ColorMix(ref mix) => mix\n                .items\n                .iter()\n                .all(|item| item.color.honored_in_forced_colors_mode(allow_transparent)),\n            Self::ContrastColor(ref c) => c.honored_in_forced_colors_mode(allow_transparent),\n        }\n    }\n\n    /// Returns currentcolor value.\n    #[inline]\n    pub fn currentcolor() -> Self {\n        Self::CurrentColor\n    }\n\n    /// Returns transparent value.\n    #[inline]\n    pub fn transparent() -> Self {\n        // We should probably set authored to \"transparent\", but maybe it doesn't matter.\n        Self::from_absolute_color(AbsoluteColor::TRANSPARENT_BLACK)\n    }\n\n    /// Create a color from an [`AbsoluteColor`].\n    pub fn from_absolute_color(color: AbsoluteColor) -> Self {\n        Color::Absolute(Box::new(Absolute {\n            color,\n            authored: None,\n        }))\n    }\n\n    /// Resolve this Color into an AbsoluteColor if it does not use any of the\n    /// forms that are invalid in an absolute color.\n    ///   https://drafts.csswg.org/css-color-5/#absolute-color\n    /// Returns None if the specified color is not valid as an absolute color.\n    pub fn resolve_to_absolute(&self) -> Result<AbsoluteColor, ()> {\n        use crate::values::specified::percentage::ToPercentage;\n\n        match self {\n            Self::Absolute(c) => Ok(c.color),\n            Self::ColorFunction(ref color_function) => color_function.resolve_to_absolute(),\n            Self::ColorMix(ref mix) => {\n                use crate::color::mix;\n\n                let mut items = ColorMixItemList::with_capacity(mix.items.len());\n                for item in mix.items.iter() {\n                    items.push(mix::ColorMixItem::new(\n                        item.color.resolve_to_absolute()?,\n                        item.percentage.to_percentage(),\n                    ))\n                }\n\n                Ok(mix::mix_many(mix.interpolation, items, mix.flags))\n            },\n            _ => Err(()),\n        }\n    }\n\n    /// Parse a color, with quirks.\n    ///\n    /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        input.try_parse(|i| Self::parse(context, i)).or_else(|e| {\n            if !allow_quirks.allowed(context.quirks_mode) {\n                return Err(e);\n            }\n            Color::parse_quirky_color(input).map_err(|_| e)\n        })\n    }\n\n    fn parse_hash<'i>(\n        bytes: &[u8],\n        loc: &cssparser::SourceLocation,\n    ) -> Result<Self, ParseError<'i>> {\n        match cssparser::color::parse_hash_color(bytes) {\n            Ok((r, g, b, a)) => Ok(Self::from_absolute_color(AbsoluteColor::srgb_legacy(\n                r, g, b, a,\n            ))),\n            Err(()) => Err(loc.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }\n    }\n\n    /// Parse a <quirky-color> value.\n    ///\n    /// <https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk>\n    fn parse_quirky_color<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let (value, unit) = match *input.next()? {\n            Token::Number {\n                int_value: Some(integer),\n                ..\n            } => (integer, None),\n            Token::Dimension {\n                int_value: Some(integer),\n                ref unit,\n                ..\n            } => (integer, Some(unit)),\n            Token::Ident(ref ident) => {\n                if ident.len() != 3 && ident.len() != 6 {\n                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                return Self::parse_hash(ident.as_bytes(), &location);\n            },\n            ref t => {\n                return Err(location.new_unexpected_token_error(t.clone()));\n            },\n        };\n        if value < 0 {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let length = if value <= 9 {\n            1\n        } else if value <= 99 {\n            2\n        } else if value <= 999 {\n            3\n        } else if value <= 9999 {\n            4\n        } else if value <= 99999 {\n            5\n        } else if value <= 999999 {\n            6\n        } else {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        };\n        let total = length + unit.as_ref().map_or(0, |d| d.len());\n        if total > 6 {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let mut serialization = [b'0'; 6];\n        let space_padding = 6 - total;\n        let mut written = space_padding;\n        let mut buf = itoa::Buffer::new();\n        let s = buf.format(value);\n        (&mut serialization[written..])\n            .write_all(s.as_bytes())\n            .unwrap();\n        written += s.len();\n        if let Some(unit) = unit {\n            written += (&mut serialization[written..])\n                .write(unit.as_bytes())\n                .unwrap();\n        }\n        debug_assert_eq!(written, 6);\n        Self::parse_hash(&serialization, &location)\n    }\n}\n\nimpl Color {\n    /// Converts this Color into a ComputedColor.\n    ///\n    /// If `context` is `None`, and the specified color requires data from\n    /// the context to resolve, then `None` is returned.\n    pub fn to_computed_color(&self, context: Option<&Context>) -> Result<ComputedColor, ()> {\n        macro_rules! adjust_absolute_color {\n            ($color:expr) => {{\n                // Computed lightness values can not be NaN.\n                if matches!(\n                    $color.color_space,\n                    ColorSpace::Lab | ColorSpace::Oklab | ColorSpace::Lch | ColorSpace::Oklch\n                ) {\n                    $color.components.0 = normalize($color.components.0);\n                }\n\n                // Computed RGB and XYZ components can not be NaN.\n                if !$color.is_legacy_syntax() && $color.color_space.is_rgb_or_xyz_like() {\n                    $color.components = $color.components.map(normalize);\n                }\n\n                $color.alpha = normalize($color.alpha);\n            }};\n        }\n\n        Ok(match *self {\n            Color::CurrentColor => ComputedColor::CurrentColor,\n            Color::Absolute(ref absolute) => {\n                let mut color = absolute.color;\n                adjust_absolute_color!(color);\n                ComputedColor::Absolute(color)\n            },\n            Color::ColorFunction(ref color_function) => {\n                debug_assert!(color_function.has_origin_color(),\n                    \"no need for a ColorFunction if it doesn't contain an unresolvable origin color\");\n\n                // Try to eagerly resolve the color function before making it a computed color.\n                if let Ok(absolute) = color_function.resolve_to_absolute() {\n                    ComputedColor::Absolute(absolute)\n                } else {\n                    let color_function = color_function\n                        .map_origin_color(|origin_color| origin_color.to_computed_color(context))?;\n                    ComputedColor::ColorFunction(Box::new(color_function))\n                }\n            },\n            Color::LightDark(ref ld) => ld.compute(context.ok_or(())?),\n            Color::ColorMix(ref mix) => {\n                use crate::values::computed::percentage::Percentage;\n\n                let mut items = ColorMixItemList::with_capacity(mix.items.len());\n                for item in mix.items.iter() {\n                    items.push(GenericColorMixItem {\n                        color: item.color.to_computed_color(context)?,\n                        percentage: Percentage(item.percentage.get()),\n                    });\n                }\n\n                ComputedColor::from_color_mix(GenericColorMix {\n                    interpolation: mix.interpolation,\n                    items: OwnedSlice::from_slice(items.as_slice()),\n                    flags: mix.flags,\n                })\n            },\n            Color::ContrastColor(ref c) => {\n                ComputedColor::ContrastColor(Box::new(c.to_computed_color(context)?))\n            },\n            Color::System(system) => system.compute(context.ok_or(())?),\n            Color::InheritFromBodyQuirk => {\n                ComputedColor::Absolute(context.ok_or(())?.device().body_text_color())\n            },\n        })\n    }\n}\n\nimpl ToComputedValue for Color {\n    type ComputedValue = ComputedColor;\n\n    fn to_computed_value(&self, context: &Context) -> ComputedColor {\n        self.to_computed_color(Some(context)).unwrap_or_else(|_| {\n            debug_assert!(\n                false,\n                \"Specified color could not be resolved to a computed color!\"\n            );\n            ComputedColor::Absolute(AbsoluteColor::BLACK)\n        })\n    }\n\n    fn from_computed_value(computed: &ComputedColor) -> Self {\n        match *computed {\n            ComputedColor::Absolute(ref color) => Self::from_absolute_color(color.clone()),\n            ComputedColor::ColorFunction(ref color_function) => {\n                let color_function = color_function\n                    .map_origin_color(|o| Ok(Self::from_computed_value(o)))\n                    .unwrap();\n                Self::ColorFunction(Box::new(color_function))\n            },\n            ComputedColor::CurrentColor => Color::CurrentColor,\n            ComputedColor::ColorMix(ref mix) => {\n                Color::ColorMix(Box::new(ToComputedValue::from_computed_value(&**mix)))\n            },\n            ComputedColor::ContrastColor(ref c) => {\n                Self::ContrastColor(Box::new(ToComputedValue::from_computed_value(&**c)))\n            },\n        }\n    }\n}\n\nimpl SpecifiedValueInfo for Color {\n    const SUPPORTED_TYPES: u8 = CssType::COLOR;\n\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        // We are not going to insert all the color names here. Caller and\n        // devtools should take care of them. XXX Actually, transparent\n        // should probably be handled that way as well.\n        // XXX `currentColor` should really be `currentcolor`. But let's\n        // keep it consistent with the old system for now.\n        f(&[\n            \"currentColor\",\n            \"transparent\",\n            \"rgb\",\n            \"rgba\",\n            \"hsl\",\n            \"hsla\",\n            \"hwb\",\n            \"color\",\n            \"lab\",\n            \"lch\",\n            \"oklab\",\n            \"oklch\",\n            \"color-mix\",\n            \"contrast-color\",\n            \"light-dark\",\n        ]);\n    }\n}\n\n/// Specified value for the \"color\" property, which resolves the `currentcolor`\n/// keyword to the parent color instead of self's color.\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub struct ColorPropertyValue(pub Color);\n\nimpl ToComputedValue for ColorPropertyValue {\n    type ComputedValue = AbsoluteColor;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        let current_color = context.builder.get_parent_inherited_text().clone_color();\n        self.0\n            .to_computed_value(context)\n            .resolve_to_absolute(&current_color)\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        ColorPropertyValue(Color::from_absolute_color(*computed).into())\n    }\n}\n\nimpl Parse for ColorPropertyValue {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Color::parse_quirky(context, input, AllowQuirks::Yes).map(ColorPropertyValue)\n    }\n}\n\n/// auto | <color>\npub type ColorOrAuto = GenericColorOrAuto<Color>;\n\n/// caret-color\npub type CaretColor = GenericCaretColor<Color>;\n\nimpl Parse for CaretColor {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        ColorOrAuto::parse(context, input).map(GenericCaretColor)\n    }\n}\n\n/// Various flags to represent the color-scheme property in an efficient\n/// way.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\n#[value_info(other_values = \"light,dark,only\")]\npub struct ColorSchemeFlags(u8);\nbitflags! {\n    impl ColorSchemeFlags: u8 {\n        /// Whether the author specified `light`.\n        const LIGHT = 1 << 0;\n        /// Whether the author specified `dark`.\n        const DARK = 1 << 1;\n        /// Whether the author specified `only`.\n        const ONLY = 1 << 2;\n    }\n}\n\n/// <https://drafts.csswg.org/css-color-adjust/#color-scheme-prop>\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n#[value_info(other_values = \"normal\")]\npub struct ColorScheme {\n    #[ignore_malloc_size_of = \"Arc\"]\n    idents: crate::ArcSlice<CustomIdent>,\n    /// The computed bits for the known color schemes (plus the only keyword).\n    pub bits: ColorSchemeFlags,\n}\n\nimpl ColorScheme {\n    /// Returns the `normal` value.\n    pub fn normal() -> Self {\n        Self {\n            idents: Default::default(),\n            bits: ColorSchemeFlags::empty(),\n        }\n    }\n\n    /// Returns the raw bitfield.\n    pub fn raw_bits(&self) -> u8 {\n        self.bits.bits()\n    }\n}\n\nimpl Parse for ColorScheme {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut idents = vec![];\n        let mut bits = ColorSchemeFlags::empty();\n\n        let mut location = input.current_source_location();\n        while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {\n            let mut is_only = false;\n            match_ignore_ascii_case! { &ident,\n                \"normal\" => {\n                    if idents.is_empty() && bits.is_empty() {\n                        return Ok(Self::normal());\n                    }\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                },\n                \"light\" => bits.insert(ColorSchemeFlags::LIGHT),\n                \"dark\" => bits.insert(ColorSchemeFlags::DARK),\n                \"only\" => {\n                    if bits.intersects(ColorSchemeFlags::ONLY) {\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                    bits.insert(ColorSchemeFlags::ONLY);\n                    is_only = true;\n                },\n                _ => {},\n            };\n\n            if is_only {\n                if !idents.is_empty() {\n                    // Only is allowed either at the beginning or at the end,\n                    // but not in the middle.\n                    break;\n                }\n            } else {\n                idents.push(CustomIdent::from_ident(location, &ident, &[])?);\n            }\n            location = input.current_source_location();\n        }\n\n        if idents.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(Self {\n            idents: crate::ArcSlice::from_iter(idents.into_iter()),\n            bits,\n        })\n    }\n}\n\nimpl ToCss for ColorScheme {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.idents.is_empty() {\n            debug_assert!(self.bits.is_empty());\n            return dest.write_str(\"normal\");\n        }\n        let mut first = true;\n        for ident in self.idents.iter() {\n            if !first {\n                dest.write_char(' ')?;\n            }\n            first = false;\n            ident.to_css(dest)?;\n        }\n        if self.bits.intersects(ColorSchemeFlags::ONLY) {\n            dest.write_str(\" only\")?;\n        }\n        Ok(())\n    }\n}\n\n/// https://drafts.csswg.org/css-color-adjust/#print-color-adjust\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum PrintColorAdjust {\n    /// Ignore backgrounds and darken text.\n    Economy,\n    /// Respect specified colors.\n    Exact,\n}\n\n/// https://drafts.csswg.org/css-color-adjust-1/#forced-color-adjust-prop\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ForcedColorAdjust {\n    /// Adjust colors if needed.\n    Auto,\n    /// Respect specified colors.\n    None,\n}\n\n/// Possible values for the forced-colors media query.\n/// <https://drafts.csswg.org/mediaqueries-5/#forced-colors>\n#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]\n#[repr(u8)]\npub enum ForcedColors {\n    /// Page colors are not being forced.\n    None,\n    /// Page colors would be forced in content.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    Requested,\n    /// Page colors are being forced.\n    Active,\n}\n\nimpl ForcedColors {\n    /// Returns whether forced-colors is active for this page.\n    pub fn is_active(self) -> bool {\n        matches!(self, Self::Active)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/column.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for the column properties.\n\nuse crate::values::generics::column::GenericColumnCount;\nuse crate::values::specified::PositiveInteger;\n\n/// A specified type for `column-count` values.\npub type ColumnCount = GenericColumnCount<PositiveInteger>;\n"
  },
  {
    "path": "style/values/specified/counters.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for counter properties.\n\nuse crate::counter_style::CounterStyle;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::counters as generics;\nuse crate::values::generics::counters::CounterPair;\nuse crate::values::specified::image::Image;\nuse crate::values::specified::Attr;\nuse crate::values::specified::Integer;\nuse crate::values::CustomIdent;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse selectors::parser::SelectorParseErrorKind;\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n#[derive(PartialEq)]\nenum CounterType {\n    Increment,\n    Set,\n    Reset,\n}\n\nimpl CounterType {\n    fn default_value(&self) -> i32 {\n        match *self {\n            Self::Increment => 1,\n            Self::Reset | Self::Set => 0,\n        }\n    }\n}\n\n/// A specified value for the `counter-increment` property.\npub type CounterIncrement = generics::GenericCounterIncrement<Integer>;\n\nimpl Parse for CounterIncrement {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self::new(parse_counters(\n            context,\n            input,\n            CounterType::Increment,\n        )?))\n    }\n}\n\n/// A specified value for the `counter-set` property.\npub type CounterSet = generics::GenericCounterSet<Integer>;\n\nimpl Parse for CounterSet {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self::new(parse_counters(context, input, CounterType::Set)?))\n    }\n}\n\n/// A specified value for the `counter-reset` property.\npub type CounterReset = generics::GenericCounterReset<Integer>;\n\nimpl Parse for CounterReset {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self::new(parse_counters(\n            context,\n            input,\n            CounterType::Reset,\n        )?))\n    }\n}\n\nfn parse_counters<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    counter_type: CounterType,\n) -> Result<Vec<CounterPair<Integer>>, ParseError<'i>> {\n    if input\n        .try_parse(|input| input.expect_ident_matching(\"none\"))\n        .is_ok()\n    {\n        return Ok(vec![]);\n    }\n\n    let mut counters = Vec::new();\n    loop {\n        let location = input.current_source_location();\n        let (name, is_reversed) = match input.next() {\n            Ok(&Token::Ident(ref ident)) => {\n                (CustomIdent::from_ident(location, ident, &[\"none\"])?, false)\n            },\n            Ok(&Token::Function(ref name))\n                if counter_type == CounterType::Reset && name.eq_ignore_ascii_case(\"reversed\") =>\n            {\n                input\n                    .parse_nested_block(|input| Ok((CustomIdent::parse(input, &[\"none\"])?, true)))?\n            },\n            Ok(t) => {\n                let t = t.clone();\n                return Err(location.new_unexpected_token_error(t));\n            },\n            Err(_) => break,\n        };\n\n        let value = match input.try_parse(|input| Integer::parse(context, input)) {\n            Ok(start) => {\n                if start.value() == i32::min_value() {\n                    // The spec says that values must be clamped to the valid range,\n                    // and we reserve i32::min_value() as an internal magic value.\n                    // https://drafts.csswg.org/css-lists/#auto-numbering\n                    Integer::new(i32::min_value() + 1)\n                } else {\n                    start\n                }\n            },\n            _ => Integer::new(if is_reversed {\n                i32::min_value()\n            } else {\n                counter_type.default_value()\n            }),\n        };\n        counters.push(CounterPair {\n            name,\n            value,\n            is_reversed,\n        });\n    }\n\n    if !counters.is_empty() {\n        Ok(counters)\n    } else {\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n/// The specified value for the `content` property.\npub type Content = generics::GenericContent<Image>;\n\n/// The specified value for a content item in the `content` property.\npub type ContentItem = generics::GenericContentItem<Image>;\n\nimpl Content {\n    fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyle {\n        use crate::counter_style::CounterStyleParsingFlags;\n        input\n            .try_parse(|input| {\n                input.expect_comma()?;\n                CounterStyle::parse(context, input, CounterStyleParsingFlags::empty())\n            })\n            .unwrap_or_else(|_| CounterStyle::decimal())\n    }\n}\n\nimpl Parse for Content {\n    // normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |\n    // no-close-quote ]+\n    // TODO: <uri>, attr(<identifier>)\n    #[cfg_attr(feature = \"servo\", allow(unused_mut))]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(generics::Content::Normal);\n        }\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(generics::Content::None);\n        }\n\n        let mut items = thin_vec::ThinVec::new();\n        let mut alt_start = None;\n        loop {\n            if alt_start.is_none() {\n                if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) {\n                    items.push(generics::ContentItem::Image(image));\n                    continue;\n                }\n            }\n            let Ok(t) = input.next() else { break };\n            match *t {\n                Token::QuotedString(ref value) => {\n                    items.push(generics::ContentItem::String(\n                        value.as_ref().to_owned().into(),\n                    ));\n                },\n                Token::Function(ref name) => {\n                    // FIXME(emilio): counter() / counters() should be valid per spec past\n                    // the alt marker, but it's likely non-trivial to support and other\n                    // browsers don't support it either, so restricting it for now.\n                    let result = match_ignore_ascii_case! { &name,\n                        \"counter\" if alt_start.is_none() => input.parse_nested_block(|input| {\n                            let name = CustomIdent::parse(input, &[])?;\n                            let style = Content::parse_counter_style(context, input);\n                            Ok(generics::ContentItem::Counter(name, style))\n                        }),\n                        \"counters\" if alt_start.is_none() => input.parse_nested_block(|input| {\n                            let name = CustomIdent::parse(input, &[])?;\n                            input.expect_comma()?;\n                            let separator = input.expect_string()?.as_ref().to_owned().into();\n                            let style = Content::parse_counter_style(context, input);\n                            Ok(generics::ContentItem::Counters(name, separator, style))\n                        }),\n                        \"attr\" if !static_prefs::pref!(\"layout.css.attr.enabled\") => input.parse_nested_block(|input| {\n                            Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))\n                        }),\n                        _ => {\n                            use style_traits::StyleParseErrorKind;\n                            let name = name.clone();\n                            return Err(input.new_custom_error(\n                                StyleParseErrorKind::UnexpectedFunction(name),\n                            ))\n                        }\n                    }?;\n                    items.push(result);\n                },\n                Token::Ident(ref ident) if alt_start.is_none() => {\n                    items.push(match_ignore_ascii_case! { &ident,\n                        \"open-quote\" => generics::ContentItem::OpenQuote,\n                        \"close-quote\" => generics::ContentItem::CloseQuote,\n                        \"no-open-quote\" => generics::ContentItem::NoOpenQuote,\n                        \"no-close-quote\" => generics::ContentItem::NoCloseQuote,\n                        #[cfg(feature = \"gecko\")]\n                        \"-moz-alt-content\" if context.in_ua_sheet() => {\n                            generics::ContentItem::MozAltContent\n                        },\n                        #[cfg(feature = \"gecko\")]\n                        \"-moz-label-content\" if context.chrome_rules_enabled() => {\n                            generics::ContentItem::MozLabelContent\n                        },\n                        _ =>{\n                            let ident = ident.clone();\n                            return Err(input.new_custom_error(\n                                SelectorParseErrorKind::UnexpectedIdent(ident)\n                            ));\n                        }\n                    });\n                },\n                Token::Delim('/')\n                    if alt_start.is_none()\n                        && !items.is_empty()\n                        && static_prefs::pref!(\"layout.css.content.alt-text.enabled\") =>\n                {\n                    alt_start = Some(items.len());\n                },\n                ref t => {\n                    let t = t.clone();\n                    return Err(input.new_unexpected_token_error(t));\n                },\n            }\n        }\n        if items.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let alt_start = alt_start.unwrap_or(items.len());\n        Ok(generics::Content::Items(generics::GenericContentItems {\n            items,\n            alt_start,\n        }))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/easing.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS Easing functions.\nuse crate::parser::{Parse, ParserContext};\nuse crate::piecewise_linear::{PiecewiseLinearFunction, PiecewiseLinearFunctionBuilder};\nuse crate::values::computed::easing::TimingFunction as ComputedTimingFunction;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::easing::TimingFunction as GenericTimingFunction;\nuse crate::values::generics::easing::{StepPosition, TimingKeyword};\nuse crate::values::specified::{AnimationName, Integer, Number, Percentage};\nuse cssparser::{match_ignore_ascii_case, Delimiter, Parser, Token};\nuse selectors::parser::SelectorParseErrorKind;\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// A specified timing function.\npub type TimingFunction = GenericTimingFunction<Integer, Number, PiecewiseLinearFunction>;\n\nimpl Parse for TimingFunction {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(keyword) = input.try_parse(TimingKeyword::parse) {\n            return Ok(GenericTimingFunction::Keyword(keyword));\n        }\n        if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) {\n            let position = match_ignore_ascii_case! { &ident,\n                \"step-start\" => StepPosition::Start,\n                \"step-end\" => StepPosition::End,\n                _ => {\n                    return Err(input.new_custom_error(\n                        SelectorParseErrorKind::UnexpectedIdent(ident.clone())\n                    ));\n                },\n            };\n            return Ok(GenericTimingFunction::Steps(Integer::new(1), position));\n        }\n        let location = input.current_source_location();\n        let function = input.expect_function()?.clone();\n        input.parse_nested_block(move |i| {\n            match_ignore_ascii_case! { &function,\n                \"cubic-bezier\" => Self::parse_cubic_bezier(context, i),\n                \"steps\" => Self::parse_steps(context, i),\n                \"linear\" => Self::parse_linear_function(context, i),\n                _ => Err(location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),\n            }\n        })\n    }\n}\n\nimpl TimingFunction {\n    fn parse_cubic_bezier<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let x1 = Number::parse(context, input)?;\n        input.expect_comma()?;\n        let y1 = Number::parse(context, input)?;\n        input.expect_comma()?;\n        let x2 = Number::parse(context, input)?;\n        input.expect_comma()?;\n        let y2 = Number::parse(context, input)?;\n\n        if x1.get() < 0.0 || x1.get() > 1.0 || x2.get() < 0.0 || x2.get() > 1.0 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(GenericTimingFunction::CubicBezier { x1, y1, x2, y2 })\n    }\n\n    fn parse_steps<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let steps = Integer::parse_positive(context, input)?;\n        let position = input\n            .try_parse(|i| {\n                i.expect_comma()?;\n                StepPosition::parse(i)\n            })\n            .unwrap_or(StepPosition::End);\n\n        // jump-none accepts a positive integer greater than 1.\n        // FIXME(emilio): The spec asks us to avoid rejecting it at parse\n        // time except until computed value time.\n        //\n        // It's not totally clear it's worth it though, and no other browser\n        // does this.\n        if position == StepPosition::JumpNone && steps.value() <= 1 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(GenericTimingFunction::Steps(steps, position))\n    }\n\n    fn parse_linear_function<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut builder = PiecewiseLinearFunctionBuilder::default();\n        let mut num_specified_stops = 0;\n        // Closely follows `parse_comma_separated`, but can generate multiple entries for one comma-separated entry.\n        loop {\n            input.parse_until_before(Delimiter::Comma, |i| {\n                let builder = &mut builder;\n                let mut input_start = i.try_parse(|i| Percentage::parse(context, i)).ok();\n                let mut input_end = i.try_parse(|i| Percentage::parse(context, i)).ok();\n\n                let output = Number::parse(context, i)?;\n                if input_start.is_none() {\n                    debug_assert!(input_end.is_none(), \"Input end parsed without input start?\");\n                    input_start = i.try_parse(|i| Percentage::parse(context, i)).ok();\n                    input_end = i.try_parse(|i| Percentage::parse(context, i)).ok();\n                }\n                builder.push(output.into(), input_start.map(|v| v.get()).into());\n                num_specified_stops += 1;\n                if input_end.is_some() {\n                    debug_assert!(\n                        input_start.is_some(),\n                        \"Input end valid but not input start?\"\n                    );\n                    builder.push(output.into(), input_end.map(|v| v.get()).into());\n                }\n\n                Ok(())\n            })?;\n\n            match input.next() {\n                Err(_) => break,\n                Ok(&Token::Comma) => continue,\n                Ok(_) => unreachable!(),\n            }\n        }\n        // By spec, specifying only a single stop makes the function invalid, even if that single entry may generate\n        // two entries.\n        if num_specified_stops < 2 {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(GenericTimingFunction::LinearFunction(builder.build()))\n    }\n\n    /// Returns true if the name matches any keyword.\n    #[inline]\n    pub fn match_keywords(name: &AnimationName) -> bool {\n        if let Some(name) = name.as_atom() {\n            #[cfg(feature = \"gecko\")]\n            return name.with_str(|n| TimingKeyword::from_ident(n).is_ok());\n            #[cfg(feature = \"servo\")]\n            return TimingKeyword::from_ident(name).is_ok();\n        }\n        false\n    }\n}\n\n// We need this for converting the specified TimingFunction into computed TimingFunction without\n// Context (for some FFIs in glue.rs). In fact, we don't really need Context to get the computed\n// value of TimingFunction.\nimpl TimingFunction {\n    /// Generate the ComputedTimingFunction without Context.\n    pub fn to_computed_value_without_context(&self) -> ComputedTimingFunction {\n        match &self {\n            GenericTimingFunction::Steps(steps, pos) => {\n                GenericTimingFunction::Steps(steps.value(), *pos)\n            },\n            GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {\n                GenericTimingFunction::CubicBezier {\n                    x1: x1.get(),\n                    y1: y1.get(),\n                    x2: x2.get(),\n                    y2: y2.get(),\n                }\n            },\n            GenericTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(*keyword),\n            GenericTimingFunction::LinearFunction(function) => {\n                GenericTimingFunction::LinearFunction(function.clone())\n            },\n        }\n    }\n}\n\nimpl ToComputedValue for TimingFunction {\n    type ComputedValue = ComputedTimingFunction;\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        self.to_computed_value_without_context()\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match &computed {\n            ComputedTimingFunction::Steps(steps, pos) => Self::Steps(Integer::new(*steps), *pos),\n            ComputedTimingFunction::CubicBezier { x1, y1, x2, y2 } => Self::CubicBezier {\n                x1: Number::new(*x1),\n                y1: Number::new(*y1),\n                x2: Number::new(*x2),\n                y2: Number::new(*y2),\n            },\n            ComputedTimingFunction::Keyword(keyword) => GenericTimingFunction::Keyword(*keyword),\n            ComputedTimingFunction::LinearFunction(function) => {\n                GenericTimingFunction::LinearFunction(function.clone())\n            },\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/effects.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values related to effects.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::effects::BoxShadow as ComputedBoxShadow;\nuse crate::values::computed::effects::SimpleShadow as ComputedSimpleShadow;\n#[cfg(feature = \"gecko\")]\nuse crate::values::computed::url::ComputedUrl;\nuse crate::values::computed::Angle as ComputedAngle;\nuse crate::values::computed::CSSPixelLength as ComputedCSSPixelLength;\nuse crate::values::computed::Filter as ComputedFilter;\nuse crate::values::computed::NonNegativeLength as ComputedNonNegativeLength;\nuse crate::values::computed::NonNegativeNumber as ComputedNonNegativeNumber;\nuse crate::values::computed::Number as ComputedNumber;\nuse crate::values::computed::ZeroToOneNumber as ComputedZeroToOneNumber;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::effects::BoxShadow as GenericBoxShadow;\nuse crate::values::generics::effects::Filter as GenericFilter;\nuse crate::values::generics::effects::SimpleShadow as GenericSimpleShadow;\nuse crate::values::generics::{NonNegative, ZeroToOne};\nuse crate::values::specified::color::Color;\nuse crate::values::specified::length::{Length, NonNegativeLength};\n#[cfg(feature = \"gecko\")]\nuse crate::values::specified::url::SpecifiedUrl;\nuse crate::values::specified::{Angle, NonNegativeNumberOrPercentage, Number, NumberOrPercentage};\n#[cfg(feature = \"servo\")]\nuse crate::values::Impossible;\nuse crate::Zero;\nuse cssparser::{match_ignore_ascii_case, BasicParseErrorKind, Parser, Token};\nuse style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};\n\n/// A specified value for a single shadow of the `box-shadow` property.\npub type BoxShadow =\n    GenericBoxShadow<Option<Color>, Length, Option<NonNegativeLength>, Option<Length>>;\n\n/// A specified value for a single `filter`.\n#[cfg(feature = \"gecko\")]\npub type SpecifiedFilter = GenericFilter<Angle, FilterFactor, Length, SimpleShadow, SpecifiedUrl>;\n\n/// A specified value for a single `filter`.\n#[cfg(feature = \"servo\")]\npub type SpecifiedFilter = GenericFilter<Angle, FilterFactor, Length, SimpleShadow, Impossible>;\n\npub use self::SpecifiedFilter as Filter;\n\n/// The factor for a filter function.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\npub struct FilterFactor(NumberOrPercentage);\n\nimpl FilterFactor {\n    fn to_number(&self) -> Number {\n        self.0.to_number()\n    }\n}\n\nimpl ToComputedValue for FilterFactor {\n    type ComputedValue = ComputedNumber;\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        self.0.to_number().get()\n    }\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self(NumberOrPercentage::Number(Number::new(*computed)))\n    }\n}\n\n/// Clamp the value to 1 if the value is over 100%.\n#[inline]\nfn clamp_to_one(number: NumberOrPercentage) -> NumberOrPercentage {\n    match number {\n        NumberOrPercentage::Percentage(percent) => {\n            NumberOrPercentage::Percentage(percent.clamp_to_hundred())\n        },\n        NumberOrPercentage::Number(number) => NumberOrPercentage::Number(number.clamp_to_one()),\n    }\n}\n\ntype NonNegativeFactor = NonNegative<FilterFactor>;\nimpl NonNegativeFactor {\n    fn one() -> Self {\n        Self(FilterFactor(NumberOrPercentage::Number(Number::new(1.))))\n    }\n}\n\nimpl Parse for NonNegativeFactor {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self(FilterFactor(\n            NonNegativeNumberOrPercentage::parse(context, input)?.0,\n        )))\n    }\n}\n\ntype ZeroToOneFactor = ZeroToOne<FilterFactor>;\nimpl ZeroToOneFactor {\n    fn one() -> Self {\n        Self(FilterFactor(NumberOrPercentage::Number(Number::new(1.))))\n    }\n}\n\nimpl Parse for ZeroToOneFactor {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(Self(FilterFactor(clamp_to_one(\n            NumberOrPercentage::parse_non_negative(context, input)?,\n        ))))\n    }\n}\n\n/// A specified value for the `drop-shadow()` filter.\npub type SimpleShadow = GenericSimpleShadow<Option<Color>, Length, Option<NonNegativeLength>>;\n\nimpl Parse for BoxShadow {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut lengths = None;\n        let mut color = None;\n        let mut inset = false;\n\n        loop {\n            if !inset {\n                if input\n                    .try_parse(|input| input.expect_ident_matching(\"inset\"))\n                    .is_ok()\n                {\n                    inset = true;\n                    continue;\n                }\n            }\n            if lengths.is_none() {\n                let value = input.try_parse::<_, _, ParseError>(|i| {\n                    let horizontal = Length::parse(context, i)?;\n                    let vertical = Length::parse(context, i)?;\n                    let (blur, spread) =\n                        match i.try_parse(|i| Length::parse_non_negative(context, i)) {\n                            Ok(blur) => {\n                                let spread = i.try_parse(|i| Length::parse(context, i)).ok();\n                                (Some(blur.into()), spread)\n                            },\n                            Err(_) => (None, None),\n                        };\n                    Ok((horizontal, vertical, blur, spread))\n                });\n                if let Ok(value) = value {\n                    lengths = Some(value);\n                    continue;\n                }\n            }\n            if color.is_none() {\n                if let Ok(value) = input.try_parse(|i| Color::parse(context, i)) {\n                    color = Some(value);\n                    continue;\n                }\n            }\n            break;\n        }\n\n        let lengths =\n            lengths.ok_or(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?;\n        Ok(BoxShadow {\n            base: SimpleShadow {\n                color: color,\n                horizontal: lengths.0,\n                vertical: lengths.1,\n                blur: lengths.2,\n            },\n            spread: lengths.3,\n            inset: inset,\n        })\n    }\n}\n\nimpl ToComputedValue for BoxShadow {\n    type ComputedValue = ComputedBoxShadow;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        ComputedBoxShadow {\n            base: self.base.to_computed_value(context),\n            spread: self\n                .spread\n                .as_ref()\n                .unwrap_or(&Length::zero())\n                .to_computed_value(context),\n            inset: self.inset,\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &ComputedBoxShadow) -> Self {\n        BoxShadow {\n            base: ToComputedValue::from_computed_value(&computed.base),\n            spread: Some(ToComputedValue::from_computed_value(&computed.spread)),\n            inset: computed.inset,\n        }\n    }\n}\n\n// We need this for converting the specified Filter into computed Filter without Context (for\n// some FFIs in glue.rs). This can fail because in some circumstances, we still need Context to\n// determine the computed value.\nimpl Filter {\n    /// Generate the ComputedFilter without Context.\n    pub fn to_computed_value_without_context(&self) -> Result<ComputedFilter, ()> {\n        match *self {\n            Filter::Blur(ref length) => Ok(ComputedFilter::Blur(ComputedNonNegativeLength::new(\n                length.0.to_computed_pixel_length_without_context()?,\n            ))),\n            Filter::Brightness(ref factor) => Ok(ComputedFilter::Brightness(\n                ComputedNonNegativeNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::Contrast(ref factor) => Ok(ComputedFilter::Contrast(\n                ComputedNonNegativeNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::Grayscale(ref factor) => Ok(ComputedFilter::Grayscale(\n                ComputedZeroToOneNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::HueRotate(ref angle) => Ok(ComputedFilter::HueRotate(\n                ComputedAngle::from_degrees(angle.degrees()),\n            )),\n            Filter::Invert(ref factor) => Ok(ComputedFilter::Invert(\n                ComputedZeroToOneNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::Opacity(ref factor) => Ok(ComputedFilter::Opacity(\n                ComputedZeroToOneNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::Saturate(ref factor) => Ok(ComputedFilter::Saturate(\n                ComputedNonNegativeNumber::from(factor.0.to_number().get()),\n            )),\n            Filter::Sepia(ref factor) => Ok(ComputedFilter::Sepia(ComputedZeroToOneNumber::from(\n                factor.0.to_number().get(),\n            ))),\n            Filter::DropShadow(ref shadow) => {\n                if cfg!(feature = \"gecko\") {\n                    let color = shadow\n                        .color\n                        .as_ref()\n                        .unwrap_or(&Color::currentcolor())\n                        .to_computed_color(None)?;\n\n                    let horizontal = ComputedCSSPixelLength::new(\n                        shadow\n                            .horizontal\n                            .to_computed_pixel_length_without_context()?,\n                    );\n                    let vertical = ComputedCSSPixelLength::new(\n                        shadow.vertical.to_computed_pixel_length_without_context()?,\n                    );\n                    let blur = ComputedNonNegativeLength::new(\n                        shadow\n                            .blur\n                            .as_ref()\n                            .unwrap_or(&NonNegativeLength::zero())\n                            .0\n                            .to_computed_pixel_length_without_context()?,\n                    );\n\n                    Ok(ComputedFilter::DropShadow(ComputedSimpleShadow {\n                        color,\n                        horizontal,\n                        vertical,\n                        blur,\n                    }))\n                } else {\n                    Err(())\n                }\n            },\n            #[cfg(feature = \"gecko\")]\n            Filter::Url(ref url) => Ok(ComputedFilter::Url(ComputedUrl(url.clone()))),\n            #[cfg(feature = \"servo\")]\n            Filter::Url(_) => Err(()),\n        }\n    }\n}\n\nimpl Parse for Filter {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        #[cfg(feature = \"gecko\")]\n        {\n            if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {\n                return Ok(GenericFilter::Url(url));\n            }\n        }\n        let location = input.current_source_location();\n        let function = match input.expect_function() {\n            Ok(f) => f.clone(),\n            Err(cssparser::BasicParseError {\n                kind: BasicParseErrorKind::UnexpectedToken(t),\n                location,\n            }) => return Err(location.new_custom_error(ValueParseErrorKind::InvalidFilter(t))),\n            Err(e) => return Err(e.into()),\n        };\n        input.parse_nested_block(|i| {\n            match_ignore_ascii_case! { &*function,\n                \"blur\" => Ok(GenericFilter::Blur(\n                    i.try_parse(|i| NonNegativeLength::parse(context, i))\n                     .unwrap_or(Zero::zero()),\n                )),\n                \"brightness\" => Ok(GenericFilter::Brightness(\n                    i.try_parse(|i| NonNegativeFactor::parse(context, i))\n                     .unwrap_or(NonNegativeFactor::one()),\n                )),\n                \"contrast\" => Ok(GenericFilter::Contrast(\n                    i.try_parse(|i| NonNegativeFactor::parse(context, i))\n                     .unwrap_or(NonNegativeFactor::one()),\n                )),\n                \"grayscale\" => {\n                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.\n                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-grayscale\n                    Ok(GenericFilter::Grayscale(\n                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))\n                         .unwrap_or(ZeroToOneFactor::one()),\n                    ))\n                },\n                \"hue-rotate\" => {\n                    // We allow unitless zero here, see:\n                    // https://github.com/w3c/fxtf-drafts/issues/228\n                    Ok(GenericFilter::HueRotate(\n                        i.try_parse(|i| Angle::parse_with_unitless(context, i))\n                         .unwrap_or(Zero::zero()),\n                    ))\n                },\n                \"invert\" => {\n                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.\n                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-invert\n                    Ok(GenericFilter::Invert(\n                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))\n                         .unwrap_or(ZeroToOneFactor::one()),\n                    ))\n                },\n                \"opacity\" => {\n                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.\n                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-opacity\n                    Ok(GenericFilter::Opacity(\n                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))\n                         .unwrap_or(ZeroToOneFactor::one()),\n                    ))\n                },\n                \"saturate\" => Ok(GenericFilter::Saturate(\n                    i.try_parse(|i| NonNegativeFactor::parse(context, i))\n                     .unwrap_or(NonNegativeFactor::one()),\n                )),\n                \"sepia\" => {\n                    // Values of amount over 100% are allowed but UAs must clamp the values to 1.\n                    // https://drafts.fxtf.org/filter-effects/#funcdef-filter-sepia\n                    Ok(GenericFilter::Sepia(\n                        i.try_parse(|i| ZeroToOneFactor::parse(context, i))\n                         .unwrap_or(ZeroToOneFactor::one()),\n                    ))\n                },\n                \"drop-shadow\" => Ok(GenericFilter::DropShadow(Parse::parse(context, i)?)),\n                _ => Err(location.new_custom_error(\n                    ValueParseErrorKind::InvalidFilter(Token::Function(function.clone()))\n                )),\n            }\n        })\n    }\n}\n\nimpl Parse for SimpleShadow {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let color = input.try_parse(|i| Color::parse(context, i)).ok();\n        let horizontal = Length::parse(context, input)?;\n        let vertical = Length::parse(context, input)?;\n        let blur = input\n            .try_parse(|i| Length::parse_non_negative(context, i))\n            .ok();\n        let blur = blur.map(NonNegative::<Length>);\n        let color = color.or_else(|| input.try_parse(|i| Color::parse(context, i)).ok());\n\n        Ok(SimpleShadow {\n            color,\n            horizontal,\n            vertical,\n            blur,\n        })\n    }\n}\n\nimpl ToComputedValue for SimpleShadow {\n    type ComputedValue = ComputedSimpleShadow;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        ComputedSimpleShadow {\n            color: self\n                .color\n                .as_ref()\n                .unwrap_or(&Color::currentcolor())\n                .to_computed_value(context),\n            horizontal: self.horizontal.to_computed_value(context),\n            vertical: self.vertical.to_computed_value(context),\n            blur: self\n                .blur\n                .as_ref()\n                .unwrap_or(&NonNegativeLength::zero())\n                .to_computed_value(context),\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        SimpleShadow {\n            color: Some(ToComputedValue::from_computed_value(&computed.color)),\n            horizontal: ToComputedValue::from_computed_value(&computed.horizontal),\n            vertical: ToComputedValue::from_computed_value(&computed.vertical),\n            blur: Some(ToComputedValue::from_computed_value(&computed.blur)),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/flex.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values related to flexbox.\n\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::flex::FlexBasis as GenericFlexBasis;\nuse crate::values::specified::Size;\nuse cssparser::Parser;\nuse style_traits::ParseError;\n\n/// A specified value for the `flex-basis` property.\npub type FlexBasis = GenericFlexBasis<Size>;\n\nimpl FlexBasis {\n    /// `auto`\n    #[inline]\n    pub fn auto() -> Self {\n        GenericFlexBasis::Size(Size::auto())\n    }\n\n    /// `0%`\n    #[inline]\n    pub fn zero_percent() -> Self {\n        GenericFlexBasis::Size(Size::zero_percent())\n    }\n}\n\nimpl Parse for FlexBasis {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let v = input.try_parse(|i| {\n            Ok(try_match_ident_ignore_ascii_case! {i, \"content\" => Self::Content, })\n        });\n        if v.is_ok() {\n            return v;\n        }\n        Ok(Self::Size(Size::parse_size_for_flex_basis_width(\n            context, input,\n        )?))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/font.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified values for font properties\n\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::font::{FamilyName, FontFamilyList, SingleFontFamily};\nuse crate::values::computed::Percentage as ComputedPercentage;\nuse crate::values::computed::{font as computed, Length, NonNegativeLength};\nuse crate::values::computed::{CSSPixelLength, Context, ToComputedValue};\nuse crate::values::generics::font::{\n    self as generics, FeatureTagValue, FontSettings, FontTag, GenericLineHeight, VariationValue,\n};\nuse crate::values::generics::NonNegative;\nuse crate::values::specified::length::{FontBaseSize, LineHeightBase, PX_PER_PT};\nuse crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage};\nuse crate::values::specified::{\n    FontRelativeLength, NoCalcLength, NonNegativeLengthPercentage, NonNegativeNumber,\n    NonNegativePercentage, Number,\n};\nuse crate::values::{serialize_atom_identifier, CustomIdent, SelectorParseErrorKind};\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\n#[cfg(feature = \"gecko\")]\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, KeywordsCollectFn, ParseError};\nuse style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};\n\n// FIXME(emilio): The system font code is copy-pasta, and should be cleaned up.\nmacro_rules! system_font_methods {\n    ($ty:ident, $field:ident) => {\n        system_font_methods!($ty);\n\n        fn compute_system(&self, _context: &Context) -> <$ty as ToComputedValue>::ComputedValue {\n            debug_assert!(matches!(*self, $ty::System(..)));\n            #[cfg(feature = \"gecko\")]\n            {\n                _context.cached_system_font.as_ref().unwrap().$field.clone()\n            }\n            #[cfg(feature = \"servo\")]\n            {\n                unreachable!()\n            }\n        }\n    };\n\n    ($ty:ident) => {\n        /// Get a specified value that represents a system font.\n        pub fn system_font(f: SystemFont) -> Self {\n            $ty::System(f)\n        }\n\n        /// Retreive a SystemFont from the specified value.\n        pub fn get_system(&self) -> Option<SystemFont> {\n            if let $ty::System(s) = *self {\n                Some(s)\n            } else {\n                None\n            }\n        }\n    };\n}\n\n/// System fonts.\n#[repr(u8)]\n#[derive(\n    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,\n)]\n#[allow(missing_docs)]\n#[cfg(feature = \"gecko\")]\npub enum SystemFont {\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-caption\n    Caption,\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-icon\n    Icon,\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-menu\n    Menu,\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-message-box\n    MessageBox,\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-small-caption\n    SmallCaption,\n    /// https://drafts.csswg.org/css-fonts/#valdef-font-status-bar\n    StatusBar,\n    /// Internal system font, used by the `<menupopup>`s on macOS.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozPullDownMenu,\n    /// Internal system font, used for `<button>` elements.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozButton,\n    /// Internal font, used by `<select>` elements.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozList,\n    /// Internal font, used by `<input>` elements.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozField,\n    #[css(skip)]\n    End, // Just for indexing purposes.\n}\n\n// We don't parse system fonts in servo, but in the interest of not\n// littering a lot of code with `if engine == \"gecko\"` conditionals,\n// we have a dummy system font module that does nothing\n\n#[derive(\n    Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,\n)]\n#[allow(missing_docs)]\n#[cfg(feature = \"servo\")]\n/// void enum for system font, can never exist\npub enum SystemFont {}\n\n#[allow(missing_docs)]\n#[cfg(feature = \"servo\")]\nimpl SystemFont {\n    pub fn parse(_: &mut Parser) -> Result<Self, ()> {\n        Err(())\n    }\n}\n\nconst DEFAULT_SCRIPT_MIN_SIZE_PT: u32 = 8;\nconst DEFAULT_SCRIPT_SIZE_MULTIPLIER: f64 = 0.71;\n\n/// The minimum font-weight value per:\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values\npub const MIN_FONT_WEIGHT: f32 = 1.;\n\n/// The maximum font-weight value per:\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values\npub const MAX_FONT_WEIGHT: f32 = 1000.;\n\n/// A specified font-weight value.\n///\n/// https://drafts.csswg.org/css-fonts-4/#propdef-font-weight\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum FontWeight {\n    /// `<font-weight-absolute>`\n    Absolute(AbsoluteFontWeight),\n    /// Bolder variant\n    Bolder,\n    /// Lighter variant\n    Lighter,\n    /// System font variant.\n    #[css(skip)]\n    System(SystemFont),\n}\n\nimpl FontWeight {\n    system_font_methods!(FontWeight, font_weight);\n\n    /// `normal`\n    #[inline]\n    pub fn normal() -> Self {\n        FontWeight::Absolute(AbsoluteFontWeight::Normal)\n    }\n\n    /// Get a specified FontWeight from a gecko keyword\n    pub fn from_gecko_keyword(kw: u32) -> Self {\n        debug_assert!(kw % 100 == 0);\n        debug_assert!(kw as f32 <= MAX_FONT_WEIGHT);\n        FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::new(kw as f32)))\n    }\n}\n\nimpl ToComputedValue for FontWeight {\n    type ComputedValue = computed::FontWeight;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            FontWeight::Absolute(ref abs) => abs.compute(),\n            FontWeight::Bolder => context\n                .builder\n                .get_parent_font()\n                .clone_font_weight()\n                .bolder(),\n            FontWeight::Lighter => context\n                .builder\n                .get_parent_font()\n                .clone_font_weight()\n                .lighter(),\n            FontWeight::System(_) => self.compute_system(context),\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &computed::FontWeight) -> Self {\n        FontWeight::Absolute(AbsoluteFontWeight::Weight(Number::from_computed_value(\n            &computed.value(),\n        )))\n    }\n}\n\n/// An absolute font-weight value for a @font-face rule.\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-weight-absolute-values\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum AbsoluteFontWeight {\n    /// A `<number>`, with the additional constraints specified in:\n    ///\n    ///   https://drafts.csswg.org/css-fonts-4/#font-weight-numeric-values\n    Weight(Number),\n    /// Normal font weight. Same as 400.\n    Normal,\n    /// Bold font weight. Same as 700.\n    Bold,\n}\n\nimpl AbsoluteFontWeight {\n    /// Returns the computed value for this absolute font weight.\n    pub fn compute(&self) -> computed::FontWeight {\n        match *self {\n            AbsoluteFontWeight::Weight(weight) => computed::FontWeight::from_float(weight.get()),\n            AbsoluteFontWeight::Normal => computed::FontWeight::NORMAL,\n            AbsoluteFontWeight::Bold => computed::FontWeight::BOLD,\n        }\n    }\n}\n\nimpl Parse for AbsoluteFontWeight {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(number) = input.try_parse(|input| Number::parse(context, input)) {\n            // We could add another AllowedNumericType value, but it doesn't\n            // seem worth it just for a single property with such a weird range,\n            // so we do the clamping here manually.\n            if !number.was_calc()\n                && (number.get() < MIN_FONT_WEIGHT || number.get() > MAX_FONT_WEIGHT)\n            {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n            return Ok(AbsoluteFontWeight::Weight(number));\n        }\n\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"normal\" => AbsoluteFontWeight::Normal,\n            \"bold\" => AbsoluteFontWeight::Bold,\n        })\n    }\n}\n\n/// The specified value of the `font-style` property, without the system font\n/// crap.\npub type SpecifiedFontStyle = generics::FontStyle<Angle>;\n\nimpl ToCss for SpecifiedFontStyle {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            generics::FontStyle::Italic => dest.write_str(\"italic\"),\n            generics::FontStyle::Oblique(ref angle) => {\n                // Not angle.is_zero() because we don't want to serialize\n                // `oblique calc(0deg)` as `normal`.\n                if *angle == Angle::zero() {\n                    dest.write_str(\"normal\")?;\n                } else {\n                    dest.write_str(\"oblique\")?;\n                    if *angle != Self::default_angle() {\n                        dest.write_char(' ')?;\n                        angle.to_css(dest)?;\n                    }\n                }\n                Ok(())\n            },\n        }\n    }\n}\n\nimpl Parse for SpecifiedFontStyle {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(try_match_ident_ignore_ascii_case! { input,\n            \"normal\" => generics::FontStyle::normal(),\n            \"italic\" => generics::FontStyle::Italic,\n            \"oblique\" => {\n                let angle = input.try_parse(|input| Self::parse_angle(context, input))\n                    .unwrap_or_else(|_| Self::default_angle());\n\n                generics::FontStyle::Oblique(angle)\n            },\n        })\n    }\n}\n\nimpl ToComputedValue for SpecifiedFontStyle {\n    type ComputedValue = computed::FontStyle;\n\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        match *self {\n            Self::Italic => computed::FontStyle::ITALIC,\n            Self::Oblique(ref angle) => computed::FontStyle::oblique(angle.degrees()),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        if *computed == computed::FontStyle::ITALIC {\n            return Self::Italic;\n        }\n        let degrees = computed.oblique_degrees();\n        generics::FontStyle::Oblique(Angle::from_degrees(degrees, /* was_calc = */ false))\n    }\n}\n\n/// From https://drafts.csswg.org/css-fonts-4/#valdef-font-style-oblique-angle:\n///\n///     Values less than -90deg or values greater than 90deg are\n///     invalid and are treated as parse errors.\n///\n/// The maximum angle value that `font-style: oblique` should compute to.\npub const FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES: f32 = 90.;\n\n/// The minimum angle value that `font-style: oblique` should compute to.\npub const FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES: f32 = -90.;\n\nimpl SpecifiedFontStyle {\n    /// Gets a clamped angle in degrees from a specified Angle.\n    pub fn compute_angle_degrees(angle: &Angle) -> f32 {\n        angle\n            .degrees()\n            .max(FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES)\n            .min(FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES)\n    }\n\n    /// Parse a suitable angle for font-style: oblique.\n    pub fn parse_angle<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Angle, ParseError<'i>> {\n        let angle = Angle::parse(context, input)?;\n        if angle.was_calc() {\n            return Ok(angle);\n        }\n\n        let degrees = angle.degrees();\n        if degrees < FONT_STYLE_OBLIQUE_MIN_ANGLE_DEGREES\n            || degrees > FONT_STYLE_OBLIQUE_MAX_ANGLE_DEGREES\n        {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        return Ok(angle);\n    }\n\n    /// The default angle for `font-style: oblique`.\n    pub fn default_angle() -> Angle {\n        Angle::from_degrees(\n            computed::FontStyle::DEFAULT_OBLIQUE_DEGREES as f32,\n            /* was_calc = */ false,\n        )\n    }\n}\n\n/// The specified value of the `font-style` property.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\n#[allow(missing_docs)]\n#[typed(todo_derive_fields)]\npub enum FontStyle {\n    Specified(SpecifiedFontStyle),\n    #[css(skip)]\n    System(SystemFont),\n}\n\nimpl FontStyle {\n    /// Return the `normal` value.\n    #[inline]\n    pub fn normal() -> Self {\n        FontStyle::Specified(generics::FontStyle::normal())\n    }\n\n    system_font_methods!(FontStyle, font_style);\n}\n\nimpl ToComputedValue for FontStyle {\n    type ComputedValue = computed::FontStyle;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            FontStyle::Specified(ref specified) => specified.to_computed_value(context),\n            FontStyle::System(..) => self.compute_system(context),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        FontStyle::Specified(SpecifiedFontStyle::from_computed_value(computed))\n    }\n}\n\n/// A value for the `font-stretch` property.\n///\n/// https://drafts.csswg.org/css-fonts-4/#font-stretch-prop\n#[allow(missing_docs)]\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum FontStretch {\n    Stretch(NonNegativePercentage),\n    Keyword(FontStretchKeyword),\n    #[css(skip)]\n    System(SystemFont),\n}\n\n/// A keyword value for `font-stretch`.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\n#[allow(missing_docs)]\npub enum FontStretchKeyword {\n    Normal,\n    Condensed,\n    UltraCondensed,\n    ExtraCondensed,\n    SemiCondensed,\n    SemiExpanded,\n    Expanded,\n    ExtraExpanded,\n    UltraExpanded,\n}\n\nimpl FontStretchKeyword {\n    /// Turns the keyword into a computed value.\n    pub fn compute(&self) -> computed::FontStretch {\n        computed::FontStretch::from_keyword(*self)\n    }\n\n    /// Does the opposite operation to `compute`, in order to serialize keywords\n    /// if possible.\n    pub fn from_percentage(p: f32) -> Option<Self> {\n        computed::FontStretch::from_percentage(p).as_keyword()\n    }\n}\n\nimpl FontStretch {\n    /// `normal`.\n    pub fn normal() -> Self {\n        FontStretch::Keyword(FontStretchKeyword::Normal)\n    }\n\n    system_font_methods!(FontStretch, font_stretch);\n}\n\nimpl ToComputedValue for FontStretch {\n    type ComputedValue = computed::FontStretch;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            FontStretch::Stretch(ref percentage) => {\n                let percentage = percentage.to_computed_value(context).0;\n                computed::FontStretch::from_percentage(percentage.0)\n            },\n            FontStretch::Keyword(ref kw) => kw.compute(),\n            FontStretch::System(_) => self.compute_system(context),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        FontStretch::Stretch(NonNegativePercentage::from_computed_value(&NonNegative(\n            computed.to_percentage(),\n        )))\n    }\n}\n\n/// CSS font keywords\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    Serialize,\n    Deserialize,\n    ToTyped,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum FontSizeKeyword {\n    #[css(keyword = \"xx-small\")]\n    XXSmall,\n    XSmall,\n    Small,\n    Medium,\n    Large,\n    XLarge,\n    #[css(keyword = \"xx-large\")]\n    XXLarge,\n    #[css(keyword = \"xxx-large\")]\n    XXXLarge,\n    /// Indicate whether to apply font-size: math is specified so that extra\n    /// scaling due to math-depth changes is applied during the cascade.\n    #[cfg(feature = \"gecko\")]\n    Math,\n    #[css(skip)]\n    None,\n}\n\nimpl FontSizeKeyword {\n    /// Convert to an HTML <font size> value\n    #[inline]\n    pub fn html_size(self) -> u8 {\n        self as u8\n    }\n\n    /// Returns true if the font size is the math keyword\n    #[cfg(feature = \"gecko\")]\n    pub fn is_math(self) -> bool {\n        matches!(self, Self::Math)\n    }\n\n    /// Returns true if the font size is the math keyword\n    #[cfg(feature = \"servo\")]\n    pub fn is_math(self) -> bool {\n        false\n    }\n}\n\nimpl Default for FontSizeKeyword {\n    fn default() -> Self {\n        FontSizeKeyword::Medium\n    }\n}\n\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Serialize, Deserialize))]\n/// Additional information for keyword-derived font sizes.\npub struct KeywordInfo {\n    /// The keyword used\n    pub kw: FontSizeKeyword,\n    /// A factor to be multiplied by the computed size of the keyword\n    #[css(skip)]\n    pub factor: f32,\n    /// An additional fixed offset to add to the kw * factor in the case of\n    /// `calc()`.\n    #[css(skip)]\n    pub offset: CSSPixelLength,\n}\n\nimpl KeywordInfo {\n    /// KeywordInfo value for font-size: medium\n    pub fn medium() -> Self {\n        Self::new(FontSizeKeyword::Medium)\n    }\n\n    /// KeywordInfo value for font-size: none\n    pub fn none() -> Self {\n        Self::new(FontSizeKeyword::None)\n    }\n\n    fn new(kw: FontSizeKeyword) -> Self {\n        KeywordInfo {\n            kw,\n            factor: 1.,\n            offset: CSSPixelLength::new(0.),\n        }\n    }\n\n    /// Computes the final size for this font-size keyword, accounting for\n    /// text-zoom.\n    fn to_computed_value(&self, context: &Context) -> CSSPixelLength {\n        debug_assert_ne!(self.kw, FontSizeKeyword::None);\n        #[cfg(feature = \"gecko\")]\n        debug_assert_ne!(self.kw, FontSizeKeyword::Math);\n        let base = context.maybe_zoom_text(self.kw.to_length(context).0);\n        let zoom_factor = context.style().effective_zoom.value();\n        CSSPixelLength::new(base.px() * self.factor * zoom_factor)\n            + context.maybe_zoom_text(self.offset)\n    }\n\n    /// Given a parent keyword info (self), apply an additional factor/offset to\n    /// it.\n    fn compose(self, factor: f32) -> Self {\n        if self.kw == FontSizeKeyword::None {\n            return self;\n        }\n        KeywordInfo {\n            kw: self.kw,\n            factor: self.factor * factor,\n            offset: self.offset * factor,\n        }\n    }\n}\n\nimpl SpecifiedValueInfo for KeywordInfo {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        <FontSizeKeyword as SpecifiedValueInfo>::collect_completion_keywords(f);\n    }\n}\n\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\n/// A specified font-size value\npub enum FontSize {\n    /// A length; e.g. 10px.\n    Length(LengthPercentage),\n    /// A keyword value, along with a ratio and absolute offset.\n    /// The ratio in any specified keyword value\n    /// will be 1 (with offset 0), but we cascade keywordness even\n    /// after font-relative (percent and em) values\n    /// have been applied, which is where the ratio\n    /// comes in. The offset comes in if we cascaded a calc value,\n    /// where the font-relative portion (em and percentage) will\n    /// go into the ratio, and the remaining units all computed together\n    /// will go into the offset.\n    /// See bug 1355707.\n    Keyword(KeywordInfo),\n    /// font-size: smaller\n    Smaller,\n    /// font-size: larger\n    Larger,\n    /// Derived from a specified system font.\n    #[css(skip)]\n    System(SystemFont),\n}\n\n/// Specifies a prioritized list of font family names or generic family names.\n#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem, ToTyped)]\n#[cfg_attr(feature = \"servo\", derive(Hash))]\n#[typed(todo_derive_fields)]\npub enum FontFamily {\n    /// List of `font-family`\n    #[css(comma)]\n    Values(#[css(iterable)] FontFamilyList),\n    /// System font\n    #[css(skip)]\n    System(SystemFont),\n}\n\nimpl FontFamily {\n    system_font_methods!(FontFamily, font_family);\n}\n\nimpl ToComputedValue for FontFamily {\n    type ComputedValue = computed::FontFamily;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            FontFamily::Values(ref list) => computed::FontFamily {\n                families: list.clone(),\n                is_system_font: false,\n                is_initial: false,\n            },\n            FontFamily::System(_) => self.compute_system(context),\n        }\n    }\n\n    fn from_computed_value(other: &computed::FontFamily) -> Self {\n        FontFamily::Values(other.families.clone())\n    }\n}\n\n#[cfg(feature = \"gecko\")]\nimpl MallocSizeOf for FontFamily {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        match *self {\n            FontFamily::Values(ref v) => {\n                // Although the family list is refcounted, we always attribute\n                // its size to the specified value.\n                v.list.unconditional_size_of(ops)\n            },\n            FontFamily::System(_) => 0,\n        }\n    }\n}\n\nimpl Parse for FontFamily {\n    /// <family-name>#\n    /// <family-name> = <string> | [ <ident>+ ]\n    /// TODO: <generic-family>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontFamily, ParseError<'i>> {\n        let values =\n            input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?;\n        Ok(FontFamily::Values(FontFamilyList {\n            list: crate::ArcSlice::from_iter(values.into_iter()),\n        }))\n    }\n}\n\nimpl SpecifiedValueInfo for FontFamily {}\n\n/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other\n/// way around because we want the former to exclude generic family keywords.\nimpl Parse for FamilyName {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        match SingleFontFamily::parse(context, input) {\n            Ok(SingleFontFamily::FamilyName(name)) => Ok(name),\n            Ok(SingleFontFamily::Generic(_)) => {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n            Err(e) => Err(e),\n        }\n    }\n}\n\n/// A factor for one of the font-size-adjust metrics, which may be either a number\n/// or the `from-font` keyword.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub enum FontSizeAdjustFactor {\n    /// An explicitly-specified number.\n    Number(NonNegativeNumber),\n    /// The from-font keyword: resolve the number from font metrics.\n    FromFont,\n}\n\n/// Specified value for font-size-adjust, intended to help\n/// preserve the readability of text when font fallback occurs.\n///\n/// https://drafts.csswg.org/css-fonts-5/#font-size-adjust-prop\npub type FontSizeAdjust = generics::GenericFontSizeAdjust<FontSizeAdjustFactor>;\n\nimpl Parse for FontSizeAdjust {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        // First check if we have an adjustment factor without a metrics-basis keyword.\n        if let Ok(factor) = input.try_parse(|i| FontSizeAdjustFactor::parse(context, i)) {\n            return Ok(Self::ExHeight(factor));\n        }\n\n        let ident = input.expect_ident()?;\n        let basis = match_ignore_ascii_case! { &ident,\n            \"none\" => return Ok(Self::None),\n            // Check for size adjustment basis keywords.\n            \"ex-height\" => Self::ExHeight,\n            \"cap-height\" => Self::CapHeight,\n            \"ch-width\" => Self::ChWidth,\n            \"ic-width\" => Self::IcWidth,\n            \"ic-height\" => Self::IcHeight,\n            // Unknown keyword.\n            _ => return Err(location.new_custom_error(\n                SelectorParseErrorKind::UnexpectedIdent(ident.clone())\n            )),\n        };\n\n        Ok(basis(FontSizeAdjustFactor::parse(context, input)?))\n    }\n}\n\n/// This is the ratio applied for font-size: larger\n/// and smaller by both Firefox and Chrome\nconst LARGER_FONT_SIZE_RATIO: f32 = 1.2;\n\n/// The default font size.\npub const FONT_MEDIUM_PX: f32 = 16.0;\n/// The default line height.\npub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2;\n/// The default ex height -- https://drafts.csswg.org/css-values/#ex\n/// > In the cases where it is impossible or impractical to determine the x-height, a value of 0.5em must be assumed\npub const FONT_MEDIUM_EX_PX: f32 = FONT_MEDIUM_PX * 0.5;\n/// The default cap height -- https://drafts.csswg.org/css-values/#cap\n/// > In the cases where it is impossible or impractical to determine the cap-height, the font’s ascent must be used\npub const FONT_MEDIUM_CAP_PX: f32 = FONT_MEDIUM_PX;\n/// The default advance measure -- https://drafts.csswg.org/css-values/#ch\n/// > Thus, the ch unit falls back to 0.5em in the general case\npub const FONT_MEDIUM_CH_PX: f32 = FONT_MEDIUM_PX * 0.5;\n/// The default idographic advance measure -- https://drafts.csswg.org/css-values/#ic\n/// > In the cases where it is impossible or impractical to determine the ideographic advance measure, it must be assumed to be 1em\npub const FONT_MEDIUM_IC_PX: f32 = FONT_MEDIUM_PX;\n\nimpl FontSizeKeyword {\n    #[inline]\n    fn to_length(&self, cx: &Context) -> NonNegativeLength {\n        let font = cx.style().get_font();\n\n        #[cfg(feature = \"servo\")]\n        let family = &font.font_family.families;\n        #[cfg(feature = \"gecko\")]\n        let family = &font.mFont.family.families;\n\n        let generic = family\n            .single_generic()\n            .unwrap_or(computed::GenericFontFamily::None);\n\n        #[cfg(feature = \"gecko\")]\n        let base_size = unsafe {\n            Atom::with(font.mLanguage.mRawPtr, |language| {\n                cx.device().base_size_for_generic(language, generic)\n            })\n        };\n        #[cfg(feature = \"servo\")]\n        let base_size = cx.device().base_size_for_generic(generic);\n\n        self.to_length_without_context(cx.quirks_mode, base_size)\n    }\n\n    /// Resolve a keyword length without any context, with explicit arguments.\n    #[inline]\n    pub fn to_length_without_context(\n        &self,\n        quirks_mode: QuirksMode,\n        base_size: Length,\n    ) -> NonNegativeLength {\n        #[cfg(feature = \"gecko\")]\n        debug_assert_ne!(*self, FontSizeKeyword::Math);\n        // The tables in this function are originally from\n        // nsRuleNode::CalcFontPointSize in Gecko:\n        //\n        // https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3150\n        //\n        // Mapping from base size and HTML size to pixels\n        // The first index is (base_size - 9), the second is the\n        // HTML size. \"0\" is CSS keyword xx-small, not HTML size 0,\n        // since HTML size 0 is the same as 1.\n        //\n        //  xxs   xs      s      m     l      xl     xxl   -\n        //  -     0/1     2      3     4      5      6     7\n        static FONT_SIZE_MAPPING: [[i32; 8]; 8] = [\n            [9, 9, 9, 9, 11, 14, 18, 27],\n            [9, 9, 9, 10, 12, 15, 20, 30],\n            [9, 9, 10, 11, 13, 17, 22, 33],\n            [9, 9, 10, 12, 14, 18, 24, 36],\n            [9, 10, 12, 13, 16, 20, 26, 39],\n            [9, 10, 12, 14, 17, 21, 28, 42],\n            [9, 10, 13, 15, 18, 23, 30, 45],\n            [9, 10, 13, 16, 18, 24, 32, 48],\n        ];\n\n        // This table gives us compatibility with WinNav4 for the default fonts only.\n        // In WinNav4, the default fonts were:\n        //\n        //     Times/12pt ==   Times/16px at 96ppi\n        //   Courier/10pt == Courier/13px at 96ppi\n        //\n        // xxs   xs     s      m      l     xl     xxl    -\n        // -     1      2      3      4     5      6      7\n        static QUIRKS_FONT_SIZE_MAPPING: [[i32; 8]; 8] = [\n            [9, 9, 9, 9, 11, 14, 18, 28],\n            [9, 9, 9, 10, 12, 15, 20, 31],\n            [9, 9, 9, 11, 13, 17, 22, 34],\n            [9, 9, 10, 12, 14, 18, 24, 37],\n            [9, 9, 10, 13, 16, 20, 26, 40],\n            [9, 9, 11, 14, 17, 21, 28, 42],\n            [9, 10, 12, 15, 17, 23, 30, 45],\n            [9, 10, 13, 16, 18, 24, 32, 48],\n        ];\n\n        static FONT_SIZE_FACTORS: [i32; 8] = [60, 75, 89, 100, 120, 150, 200, 300];\n        let base_size_px = base_size.px().round() as i32;\n        let html_size = self.html_size() as usize;\n        NonNegative(if base_size_px >= 9 && base_size_px <= 16 {\n            let mapping = if quirks_mode == QuirksMode::Quirks {\n                QUIRKS_FONT_SIZE_MAPPING\n            } else {\n                FONT_SIZE_MAPPING\n            };\n            Length::new(mapping[(base_size_px - 9) as usize][html_size] as f32)\n        } else {\n            base_size * FONT_SIZE_FACTORS[html_size] as f32 / 100.0\n        })\n    }\n}\n\nimpl FontSize {\n    /// <https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-legacy-font-size>\n    pub fn from_html_size(size: u8) -> Self {\n        FontSize::Keyword(KeywordInfo::new(match size {\n            // If value is less than 1, let it be 1.\n            0 | 1 => FontSizeKeyword::XSmall,\n            2 => FontSizeKeyword::Small,\n            3 => FontSizeKeyword::Medium,\n            4 => FontSizeKeyword::Large,\n            5 => FontSizeKeyword::XLarge,\n            6 => FontSizeKeyword::XXLarge,\n            // If value is greater than 7, let it be 7.\n            _ => FontSizeKeyword::XXXLarge,\n        }))\n    }\n\n    /// Compute it against a given base font size\n    pub fn to_computed_value_against(\n        &self,\n        context: &Context,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> computed::FontSize {\n        let compose_keyword = |factor| {\n            context\n                .style()\n                .get_parent_font()\n                .clone_font_size()\n                .keyword_info\n                .compose(factor)\n        };\n        let mut info = KeywordInfo::none();\n        let size = match *self {\n            FontSize::Length(LengthPercentage::Length(ref l)) => {\n                if let NoCalcLength::FontRelative(ref value) = *l {\n                    if let FontRelativeLength::Em(em) = *value {\n                        // If the parent font was keyword-derived, this is\n                        // too. Tack the em unit onto the factor\n                        info = compose_keyword(em);\n                    }\n                }\n                let result =\n                    l.to_computed_value_with_base_size(context, base_size, line_height_base);\n                if l.should_zoom_text() {\n                    context.maybe_zoom_text(result)\n                } else {\n                    result\n                }\n            },\n            FontSize::Length(LengthPercentage::Percentage(pc)) => {\n                // If the parent font was keyword-derived, this is too.\n                // Tack the % onto the factor\n                info = compose_keyword(pc.0);\n                (base_size.resolve(context).computed_size() * pc.0).normalized()\n            },\n            FontSize::Length(LengthPercentage::Calc(ref calc)) => {\n                let calc = calc.to_computed_value_zoomed(context, base_size, line_height_base);\n                calc.resolve(base_size.resolve(context).computed_size())\n            },\n            FontSize::Keyword(i) => {\n                if i.kw.is_math() {\n                    // Scaling is done in recompute_math_font_size_if_needed().\n                    info = compose_keyword(1.);\n                    // i.kw will always be FontSizeKeyword::Math here. But writing it this\n                    // allows this code to compile for servo where the Math variant is cfg'd out.\n                    info.kw = i.kw;\n                    FontRelativeLength::Em(1.).to_computed_value(\n                        context,\n                        base_size,\n                        line_height_base,\n                    )\n                } else {\n                    // As a specified keyword, this is keyword derived\n                    info = i;\n                    i.to_computed_value(context).clamp_to_non_negative()\n                }\n            },\n            FontSize::Smaller => {\n                info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO);\n                FontRelativeLength::Em(1. / LARGER_FONT_SIZE_RATIO).to_computed_value(\n                    context,\n                    base_size,\n                    line_height_base,\n                )\n            },\n            FontSize::Larger => {\n                info = compose_keyword(LARGER_FONT_SIZE_RATIO);\n                FontRelativeLength::Em(LARGER_FONT_SIZE_RATIO).to_computed_value(\n                    context,\n                    base_size,\n                    line_height_base,\n                )\n            },\n\n            FontSize::System(_) => {\n                #[cfg(feature = \"servo\")]\n                {\n                    unreachable!()\n                }\n                #[cfg(feature = \"gecko\")]\n                {\n                    context\n                        .cached_system_font\n                        .as_ref()\n                        .unwrap()\n                        .font_size\n                        .computed_size()\n                }\n            },\n        };\n        computed::FontSize {\n            computed_size: NonNegative(size),\n            used_size: NonNegative(size),\n            keyword_info: info,\n        }\n    }\n}\n\nimpl ToComputedValue for FontSize {\n    type ComputedValue = computed::FontSize;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> computed::FontSize {\n        self.to_computed_value_against(\n            context,\n            FontBaseSize::InheritedStyle,\n            LineHeightBase::InheritedStyle,\n        )\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &computed::FontSize) -> Self {\n        FontSize::Length(LengthPercentage::Length(\n            ToComputedValue::from_computed_value(&computed.computed_size()),\n        ))\n    }\n}\n\nimpl FontSize {\n    system_font_methods!(FontSize);\n\n    /// Get initial value for specified font size.\n    #[inline]\n    pub fn medium() -> Self {\n        FontSize::Keyword(KeywordInfo::medium())\n    }\n\n    /// Parses a font-size, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<FontSize, ParseError<'i>> {\n        if let Ok(lp) = input\n            .try_parse(|i| LengthPercentage::parse_non_negative_quirky(context, i, allow_quirks))\n        {\n            return Ok(FontSize::Length(lp));\n        }\n\n        if let Ok(kw) = input.try_parse(|i| FontSizeKeyword::parse(i)) {\n            return Ok(FontSize::Keyword(KeywordInfo::new(kw)));\n        }\n\n        try_match_ident_ignore_ascii_case! { input,\n            \"smaller\" => Ok(FontSize::Smaller),\n            \"larger\" => Ok(FontSize::Larger),\n        }\n    }\n}\n\nimpl Parse for FontSize {\n    /// <length> | <percentage> | <absolute-size> | <relative-size>\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontSize, ParseError<'i>> {\n        FontSize::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nbitflags! {\n    #[derive(Clone, Copy)]\n    /// Flags of variant alternates in bit\n    struct VariantAlternatesParsingFlags: u8 {\n        /// None of variant alternates enabled\n        const NORMAL = 0;\n        /// Historical forms\n        const HISTORICAL_FORMS = 0x01;\n        /// Stylistic Alternates\n        const STYLISTIC = 0x02;\n        /// Stylistic Sets\n        const STYLESET = 0x04;\n        /// Character Variant\n        const CHARACTER_VARIANT = 0x08;\n        /// Swash glyphs\n        const SWASH = 0x10;\n        /// Ornaments glyphs\n        const ORNAMENTS = 0x20;\n        /// Annotation forms\n        const ANNOTATION = 0x40;\n    }\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\n/// Set of variant alternates\npub enum VariantAlternates {\n    /// Enables display of stylistic alternates\n    #[css(function)]\n    Stylistic(CustomIdent),\n    /// Enables display with stylistic sets\n    #[css(comma, function)]\n    Styleset(#[css(iterable)] crate::OwnedSlice<CustomIdent>),\n    /// Enables display of specific character variants\n    #[css(comma, function)]\n    CharacterVariant(#[css(iterable)] crate::OwnedSlice<CustomIdent>),\n    /// Enables display of swash glyphs\n    #[css(function)]\n    Swash(CustomIdent),\n    /// Enables replacement of default glyphs with ornaments\n    #[css(function)]\n    Ornaments(CustomIdent),\n    /// Enables display of alternate annotation forms\n    #[css(function)]\n    Annotation(CustomIdent),\n    /// Enables display of historical forms\n    HistoricalForms,\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\n/// List of Variant Alternates\npub struct FontVariantAlternates(\n    #[css(if_empty = \"normal\", iterable)] crate::OwnedSlice<VariantAlternates>,\n);\n\nimpl FontVariantAlternates {\n    /// Returns the length of all variant alternates.\n    pub fn len(&self) -> usize {\n        self.0.iter().fold(0, |acc, alternate| match *alternate {\n            VariantAlternates::Swash(_)\n            | VariantAlternates::Stylistic(_)\n            | VariantAlternates::Ornaments(_)\n            | VariantAlternates::Annotation(_) => acc + 1,\n            VariantAlternates::Styleset(ref slice)\n            | VariantAlternates::CharacterVariant(ref slice) => acc + slice.len(),\n            _ => acc,\n        })\n    }\n}\n\nimpl Parse for FontVariantAlternates {\n    /// normal |\n    ///  [ stylistic(<feature-value-name>)           ||\n    ///    historical-forms                          ||\n    ///    styleset(<feature-value-name> #)          ||\n    ///    character-variant(<feature-value-name> #) ||\n    ///    swash(<feature-value-name>)               ||\n    ///    ornaments(<feature-value-name>)           ||\n    ///    annotation(<feature-value-name>) ]\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontVariantAlternates, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(Default::default());\n        }\n\n        let mut stylistic = None;\n        let mut historical = None;\n        let mut styleset = None;\n        let mut character_variant = None;\n        let mut swash = None;\n        let mut ornaments = None;\n        let mut annotation = None;\n\n        // Parse values for the various alternate types in any order.\n        let mut parsed_alternates = VariantAlternatesParsingFlags::empty();\n        macro_rules! check_if_parsed(\n            ($input:expr, $flag:path) => (\n                if parsed_alternates.contains($flag) {\n                    return Err($input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n                }\n                parsed_alternates |= $flag;\n            )\n        );\n        while let Ok(_) = input.try_parse(|input| match *input.next()? {\n            Token::Ident(ref value) if value.eq_ignore_ascii_case(\"historical-forms\") => {\n                check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);\n                historical = Some(VariantAlternates::HistoricalForms);\n                Ok(())\n            },\n            Token::Function(ref name) => {\n                let name = name.clone();\n                input.parse_nested_block(|i| {\n                    match_ignore_ascii_case! { &name,\n                        \"swash\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);\n                            let ident = CustomIdent::parse(i, &[])?;\n                            swash = Some(VariantAlternates::Swash(ident));\n                            Ok(())\n                        },\n                        \"stylistic\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLISTIC);\n                            let ident = CustomIdent::parse(i, &[])?;\n                            stylistic = Some(VariantAlternates::Stylistic(ident));\n                            Ok(())\n                        },\n                        \"ornaments\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::ORNAMENTS);\n                            let ident = CustomIdent::parse(i, &[])?;\n                            ornaments = Some(VariantAlternates::Ornaments(ident));\n                            Ok(())\n                        },\n                        \"annotation\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::ANNOTATION);\n                            let ident = CustomIdent::parse(i, &[])?;\n                            annotation = Some(VariantAlternates::Annotation(ident));\n                            Ok(())\n                        },\n                        \"styleset\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::STYLESET);\n                            let idents = i.parse_comma_separated(|i| {\n                                CustomIdent::parse(i, &[])\n                            })?;\n                            styleset = Some(VariantAlternates::Styleset(idents.into()));\n                            Ok(())\n                        },\n                        \"character-variant\" => {\n                            check_if_parsed!(i, VariantAlternatesParsingFlags::CHARACTER_VARIANT);\n                            let idents = i.parse_comma_separated(|i| {\n                                CustomIdent::parse(i, &[])\n                            })?;\n                            character_variant = Some(VariantAlternates::CharacterVariant(idents.into()));\n                            Ok(())\n                        },\n                        _ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n                    }\n                })\n            },\n            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }) {}\n\n        if parsed_alternates.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // Collect the parsed values in canonical order, so that we'll serialize correctly.\n        let mut alternates = Vec::new();\n        macro_rules! push_if_some(\n            ($value:expr) => (\n                if let Some(v) = $value {\n                    alternates.push(v);\n                }\n            )\n        );\n        push_if_some!(stylistic);\n        push_if_some!(historical);\n        push_if_some!(styleset);\n        push_if_some!(character_variant);\n        push_if_some!(swash);\n        push_if_some!(ornaments);\n        push_if_some!(annotation);\n\n        Ok(FontVariantAlternates(alternates.into()))\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"normal\",\n    mixed = \"jis78,jis83,jis90,jis04,simplified,traditional,full-width,proportional-width,ruby\",\n    validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\n/// Variants for east asian variant\npub struct FontVariantEastAsian(u16);\nbitflags! {\n    impl FontVariantEastAsian: u16 {\n        /// None of the features\n        const NORMAL = 0;\n        /// Enables rendering of JIS78 forms (OpenType feature: jp78)\n        const JIS78  = 1 << 0;\n        /// Enables rendering of JIS83 forms (OpenType feature: jp83).\n        const JIS83 = 1 << 1;\n        /// Enables rendering of JIS90 forms (OpenType feature: jp90).\n        const JIS90 = 1 << 2;\n        /// Enables rendering of JIS2004 forms (OpenType feature: jp04).\n        const JIS04 = 1 << 3;\n        /// Enables rendering of simplified forms (OpenType feature: smpl).\n        const SIMPLIFIED = 1 << 4;\n        /// Enables rendering of traditional forms (OpenType feature: trad).\n        const TRADITIONAL = 1 << 5;\n\n        /// These values are exclusive with each other.\n        const JIS_GROUP = Self::JIS78.0 | Self::JIS83.0 | Self::JIS90.0 | Self::JIS04.0 | Self::SIMPLIFIED.0 | Self::TRADITIONAL.0;\n\n        /// Enables rendering of full-width variants (OpenType feature: fwid).\n        const FULL_WIDTH = 1 << 6;\n        /// Enables rendering of proportionally-spaced variants (OpenType feature: pwid).\n        const PROPORTIONAL_WIDTH = 1 << 7;\n        /// Enables display of ruby variant glyphs (OpenType feature: ruby).\n        const RUBY = 1 << 8;\n    }\n}\n\nimpl FontVariantEastAsian {\n    /// The number of variants.\n    pub const COUNT: usize = 9;\n\n    fn validate_mixed_flags(&self) -> bool {\n        if self.contains(Self::FULL_WIDTH | Self::PROPORTIONAL_WIDTH) {\n            // full-width and proportional-width are exclusive with each other.\n            return false;\n        }\n        let jis = self.intersection(Self::JIS_GROUP);\n        if !jis.is_empty() && !jis.bits().is_power_of_two() {\n            return false;\n        }\n        true\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"normal,none\",\n    mixed = \"common-ligatures,no-common-ligatures,discretionary-ligatures,no-discretionary-ligatures,historical-ligatures,no-historical-ligatures,contextual,no-contextual\",\n    validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\n/// Variants of ligatures\npub struct FontVariantLigatures(u16);\nbitflags! {\n    impl FontVariantLigatures: u16 {\n        /// Specifies that common default features are enabled\n        const NORMAL = 0;\n        /// Specifies that no features are enabled;\n        const NONE = 1;\n        /// Enables display of common ligatures\n        const COMMON_LIGATURES  = 1 << 1;\n        /// Disables display of common ligatures\n        const NO_COMMON_LIGATURES  = 1 << 2;\n        /// Enables display of discretionary ligatures\n        const DISCRETIONARY_LIGATURES = 1 << 3;\n        /// Disables display of discretionary ligatures\n        const NO_DISCRETIONARY_LIGATURES = 1 << 4;\n        /// Enables display of historical ligatures\n        const HISTORICAL_LIGATURES = 1 << 5;\n        /// Disables display of historical ligatures\n        const NO_HISTORICAL_LIGATURES = 1 << 6;\n        /// Enables display of contextual alternates\n        const CONTEXTUAL = 1 << 7;\n        /// Disables display of contextual alternates\n        const NO_CONTEXTUAL = 1 << 8;\n    }\n}\n\nimpl FontVariantLigatures {\n    /// The number of variants.\n    pub const COUNT: usize = 9;\n\n    fn validate_mixed_flags(&self) -> bool {\n        // Mixing a value and its disabling value is forbidden.\n        if self.contains(Self::COMMON_LIGATURES | Self::NO_COMMON_LIGATURES)\n            || self.contains(Self::DISCRETIONARY_LIGATURES | Self::NO_DISCRETIONARY_LIGATURES)\n            || self.contains(Self::HISTORICAL_LIGATURES | Self::NO_HISTORICAL_LIGATURES)\n            || self.contains(Self::CONTEXTUAL | Self::NO_CONTEXTUAL)\n        {\n            return false;\n        }\n        true\n    }\n}\n\n/// Variants of numeric values\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"normal\",\n    mixed = \"lining-nums,oldstyle-nums,proportional-nums,tabular-nums,diagonal-fractions,stacked-fractions,ordinal,slashed-zero\",\n    validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\npub struct FontVariantNumeric(u8);\nbitflags! {\n    impl FontVariantNumeric : u8 {\n        /// Specifies that common default features are enabled\n        const NORMAL = 0;\n        /// Enables display of lining numerals.\n        const LINING_NUMS = 1 << 0;\n        /// Enables display of old-style numerals.\n        const OLDSTYLE_NUMS = 1 << 1;\n        /// Enables display of proportional numerals.\n        const PROPORTIONAL_NUMS = 1 << 2;\n        /// Enables display of tabular numerals.\n        const TABULAR_NUMS = 1 << 3;\n        /// Enables display of lining diagonal fractions.\n        const DIAGONAL_FRACTIONS = 1 << 4;\n        /// Enables display of lining stacked fractions.\n        const STACKED_FRACTIONS = 1 << 5;\n        /// Enables display of slashed zeros.\n        const SLASHED_ZERO = 1 << 6;\n        /// Enables display of letter forms used with ordinal numbers.\n        const ORDINAL = 1 << 7;\n    }\n}\n\nimpl FontVariantNumeric {\n    /// The number of variants.\n    pub const COUNT: usize = 8;\n\n    /// normal |\n    ///  [ <numeric-figure-values>   ||\n    ///    <numeric-spacing-values>  ||\n    ///    <numeric-fraction-values> ||\n    ///    ordinal                   ||\n    ///    slashed-zero ]\n    /// <numeric-figure-values>   = [ lining-nums | oldstyle-nums ]\n    /// <numeric-spacing-values>  = [ proportional-nums | tabular-nums ]\n    /// <numeric-fraction-values> = [ diagonal-fractions | stacked-fractions ]\n    fn validate_mixed_flags(&self) -> bool {\n        if self.contains(Self::LINING_NUMS | Self::OLDSTYLE_NUMS)\n            || self.contains(Self::PROPORTIONAL_NUMS | Self::TABULAR_NUMS)\n            || self.contains(Self::DIAGONAL_FRACTIONS | Self::STACKED_FRACTIONS)\n        {\n            return false;\n        }\n        true\n    }\n}\n\n/// This property provides low-level control over OpenType or TrueType font features.\npub type FontFeatureSettings = FontSettings<FeatureTagValue<Integer>>;\n\n/// For font-language-override, use the same representation as the computed value.\npub use crate::values::computed::font::FontLanguageOverride;\n\nimpl Parse for FontLanguageOverride {\n    /// normal | <string>\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontLanguageOverride, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(FontLanguageOverride::normal());\n        }\n\n        let string = input.expect_string()?;\n\n        // The OpenType spec requires tags to be 1 to 4 ASCII characters:\n        // https://learn.microsoft.com/en-gb/typography/opentype/spec/otff#data-types\n        if string.is_empty() || string.len() > 4 || !string.is_ascii() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let mut bytes = [b' '; 4];\n        for (byte, str_byte) in bytes.iter_mut().zip(string.as_bytes()) {\n            *byte = *str_byte;\n        }\n\n        Ok(FontLanguageOverride(u32::from_be_bytes(bytes)))\n    }\n}\n\n/// A value for any of the font-synthesis-{weight,small-caps,position} properties.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\npub enum FontSynthesis {\n    /// This attribute may be synthesized if not supported by a face.\n    Auto,\n    /// Do not attempt to synthesis this style attribute.\n    None,\n}\n\n/// A value for the font-synthesis-style property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\npub enum FontSynthesisStyle {\n    /// This attribute may be synthesized if not supported by a face.\n    Auto,\n    /// Do not attempt to synthesis this style attribute.\n    None,\n    /// Allow synthesis for oblique, but not for italic.\n    ObliqueOnly,\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// Allows authors to choose a palette from those supported by a color font\n/// (and potentially @font-palette-values overrides).\npub struct FontPalette(Atom);\n\n#[allow(missing_docs)]\nimpl FontPalette {\n    pub fn normal() -> Self {\n        Self(atom!(\"normal\"))\n    }\n    pub fn light() -> Self {\n        Self(atom!(\"light\"))\n    }\n    pub fn dark() -> Self {\n        Self(atom!(\"dark\"))\n    }\n}\n\nimpl Parse for FontPalette {\n    /// normal | light | dark | dashed-ident\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<FontPalette, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n        match_ignore_ascii_case! { &ident,\n            \"normal\" => Ok(Self::normal()),\n            \"light\" => Ok(Self::light()),\n            \"dark\" => Ok(Self::dark()),\n            _ => if ident.starts_with(\"--\") {\n                Ok(Self(Atom::from(ident.as_ref())))\n            } else {\n                Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())))\n            },\n        }\n    }\n}\n\nimpl ToCss for FontPalette {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_atom_identifier(&self.0, dest)\n    }\n}\n\n/// This property provides low-level control over OpenType or TrueType font\n/// variations.\npub type FontVariationSettings = FontSettings<VariationValue<Number>>;\n\nfn parse_one_feature_value<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<Integer, ParseError<'i>> {\n    if let Ok(integer) = input.try_parse(|i| Integer::parse_non_negative(context, i)) {\n        return Ok(integer);\n    }\n\n    try_match_ident_ignore_ascii_case! { input,\n        \"on\" => Ok(Integer::new(1)),\n        \"off\" => Ok(Integer::new(0)),\n    }\n}\n\nimpl Parse for FeatureTagValue<Integer> {\n    /// https://drafts.csswg.org/css-fonts-4/#feature-tag-value\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let tag = FontTag::parse(context, input)?;\n        let value = input\n            .try_parse(|i| parse_one_feature_value(context, i))\n            .unwrap_or_else(|_| Integer::new(1));\n\n        Ok(Self { tag, value })\n    }\n}\n\nimpl Parse for VariationValue<Number> {\n    /// This is the `<string> <number>` part of the font-variation-settings\n    /// syntax.\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let tag = FontTag::parse(context, input)?;\n        let value = Number::parse(context, input)?;\n        Ok(Self { tag, value })\n    }\n}\n\n/// A metrics override value for a @font-face descriptor\n///\n/// https://drafts.csswg.org/css-fonts/#font-metrics-override-desc\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,\n)]\npub enum MetricsOverride {\n    /// A non-negative `<percentage>` of the computed font size\n    Override(NonNegativePercentage),\n    /// Normal metrics from the font.\n    Normal,\n}\n\nimpl MetricsOverride {\n    #[inline]\n    /// Get default value with `normal`\n    pub fn normal() -> MetricsOverride {\n        MetricsOverride::Normal\n    }\n\n    /// The ToComputedValue implementation, used for @font-face descriptors.\n    ///\n    /// Valid override percentages must be non-negative; we return -1.0 to indicate\n    /// the absence of an override (i.e. 'normal').\n    #[inline]\n    pub fn compute(&self) -> ComputedPercentage {\n        match *self {\n            MetricsOverride::Normal => ComputedPercentage(-1.0),\n            MetricsOverride::Override(percent) => ComputedPercentage(percent.0.get()),\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n/// How to do font-size scaling.\npub enum XTextScale {\n    /// Both min-font-size and text zoom are enabled.\n    All,\n    /// Text-only zoom is enabled, but min-font-size is not honored.\n    ZoomOnly,\n    /// Neither of them is enabled.\n    None,\n}\n\nimpl XTextScale {\n    /// Returns whether text zoom is enabled.\n    #[inline]\n    pub fn text_zoom_enabled(self) -> bool {\n        self != Self::None\n    }\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Eq, Hash, Serialize))]\n/// Internal property that reflects the lang attribute\npub struct XLang(#[css(skip)] pub Atom);\n\nimpl XLang {\n    #[inline]\n    /// Get default value for `-x-lang`\n    pub fn get_initial_value() -> XLang {\n        XLang(atom!(\"\"))\n    }\n}\n\nimpl Parse for XLang {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<XLang, ParseError<'i>> {\n        debug_assert!(\n            false,\n            \"Should be set directly by presentation attributes only.\"\n        );\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\n/// Specifies the minimum font size allowed due to changes in scriptlevel.\n/// Ref: https://wiki.mozilla.org/MathML:mstyle\npub struct MozScriptMinSize(pub NoCalcLength);\n\nimpl MozScriptMinSize {\n    #[inline]\n    /// Calculate initial value of -moz-script-min-size.\n    pub fn get_initial_value() -> Length {\n        Length::new(DEFAULT_SCRIPT_MIN_SIZE_PT as f32 * PX_PER_PT)\n    }\n}\n\nimpl Parse for MozScriptMinSize {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<MozScriptMinSize, ParseError<'i>> {\n        debug_assert!(\n            false,\n            \"Should be set directly by presentation attributes only.\"\n        );\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n/// A value for the `math-depth` property.\n/// https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub enum MathDepth {\n    /// Increment math-depth if math-style is compact.\n    AutoAdd,\n\n    /// Add the function's argument to math-depth.\n    #[css(function)]\n    Add(Integer),\n\n    /// Set math-depth to the specified value.\n    Absolute(Integer),\n}\n\nimpl Parse for MathDepth {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<MathDepth, ParseError<'i>> {\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"auto-add\"))\n            .is_ok()\n        {\n            return Ok(MathDepth::AutoAdd);\n        }\n        if let Ok(math_depth_value) = input.try_parse(|input| Integer::parse(context, input)) {\n            return Ok(MathDepth::Absolute(math_depth_value));\n        }\n        input.expect_function_matching(\"add\")?;\n        let math_depth_delta_value =\n            input.parse_nested_block(|input| Integer::parse(context, input))?;\n        Ok(MathDepth::Add(math_depth_delta_value))\n    }\n}\n\n#[cfg_attr(feature = \"gecko\", derive(MallocSizeOf))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n/// Specifies the multiplier to be used to adjust font size\n/// due to changes in scriptlevel.\n///\n/// Ref: https://www.w3.org/TR/MathML3/chapter3.html#presm.mstyle.attrs\npub struct MozScriptSizeMultiplier(pub f32);\n\nimpl MozScriptSizeMultiplier {\n    #[inline]\n    /// Get default value of `-moz-script-size-multiplier`\n    pub fn get_initial_value() -> MozScriptSizeMultiplier {\n        MozScriptSizeMultiplier(DEFAULT_SCRIPT_SIZE_MULTIPLIER as f32)\n    }\n}\n\nimpl Parse for MozScriptSizeMultiplier {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<MozScriptSizeMultiplier, ParseError<'i>> {\n        debug_assert!(\n            false,\n            \"Should be set directly by presentation attributes only.\"\n        );\n        Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\nimpl From<f32> for MozScriptSizeMultiplier {\n    fn from(v: f32) -> Self {\n        MozScriptSizeMultiplier(v)\n    }\n}\n\nimpl From<MozScriptSizeMultiplier> for f32 {\n    fn from(v: MozScriptSizeMultiplier) -> f32 {\n        v.0\n    }\n}\n\n/// A specified value for the `line-height` property.\npub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLengthPercentage>;\n\nimpl ToComputedValue for LineHeight {\n    type ComputedValue = computed::LineHeight;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            GenericLineHeight::Normal => GenericLineHeight::Normal,\n            #[cfg(feature = \"gecko\")]\n            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,\n            GenericLineHeight::Number(number) => {\n                GenericLineHeight::Number(number.to_computed_value(context))\n            },\n            GenericLineHeight::Length(ref non_negative_lp) => {\n                let result = match non_negative_lp.0 {\n                    LengthPercentage::Length(NoCalcLength::Absolute(ref abs)) => {\n                        context.maybe_zoom_text(abs.to_computed_value(context))\n                    },\n                    LengthPercentage::Length(ref length) => {\n                        // line-height units specifically resolve against parent's\n                        // font and line-height properties, while the rest of font\n                        // relative units still resolve against the element's own\n                        // properties.\n                        length.to_computed_value_with_base_size(\n                            context,\n                            FontBaseSize::CurrentStyle,\n                            LineHeightBase::InheritedStyle,\n                        )\n                    },\n                    LengthPercentage::Percentage(ref p) => FontRelativeLength::Em(p.0)\n                        .to_computed_value(\n                            context,\n                            FontBaseSize::CurrentStyle,\n                            LineHeightBase::InheritedStyle,\n                        ),\n                    LengthPercentage::Calc(ref calc) => {\n                        let computed_calc = calc.to_computed_value_zoomed(\n                            context,\n                            FontBaseSize::CurrentStyle,\n                            LineHeightBase::InheritedStyle,\n                        );\n                        let base = context.style().get_font().clone_font_size().computed_size();\n                        computed_calc.resolve(base)\n                    },\n                };\n                GenericLineHeight::Length(result.into())\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match *computed {\n            GenericLineHeight::Normal => GenericLineHeight::Normal,\n            #[cfg(feature = \"gecko\")]\n            GenericLineHeight::MozBlockHeight => GenericLineHeight::MozBlockHeight,\n            GenericLineHeight::Number(ref number) => {\n                GenericLineHeight::Number(NonNegativeNumber::from_computed_value(number))\n            },\n            GenericLineHeight::Length(ref length) => {\n                GenericLineHeight::Length(NoCalcLength::from_computed_value(&length.0).into())\n            },\n        }\n    }\n}\n\n/// Flags for the query_font_metrics() function.\n#[repr(C)]\npub struct QueryFontMetricsFlags(u8);\n\nbitflags! {\n    impl QueryFontMetricsFlags: u8 {\n        /// Should we use the user font set?\n        const USE_USER_FONT_SET = 1 << 0;\n        /// Does the caller need the `ch` unit (width of the ZERO glyph)?\n        const NEEDS_CH = 1 << 1;\n        /// Does the caller need the `ic` unit (width of the WATER ideograph)?\n        const NEEDS_IC = 1 << 2;\n        /// Does the caller need math scales to be retrieved?\n        const NEEDS_MATH_SCALES = 1 << 3;\n    }\n}\n"
  },
  {
    "path": "style/values/specified/grid.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the computed value of\n//! [grids](https://drafts.csswg.org/css-grid/)\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::grid::{GridTemplateComponent, ImplicitGridTracks, RepeatCount};\nuse crate::values::generics::grid::{LineNameList, LineNameListValue, NameRepeat, TrackBreadth};\nuse crate::values::generics::grid::{TrackList, TrackListValue, TrackRepeat, TrackSize};\nuse crate::values::specified::{Integer, LengthPercentage};\nuse crate::values::{CSSFloat, CustomIdent};\nuse cssparser::{Parser, Token};\nuse std::mem;\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// Parse a single flexible length.\npub fn parse_flex<'i, 't>(input: &mut Parser<'i, 't>) -> Result<CSSFloat, ParseError<'i>> {\n    let location = input.current_source_location();\n    match *input.next()? {\n        Token::Dimension {\n            value, ref unit, ..\n        } if unit.eq_ignore_ascii_case(\"fr\") && value.is_sign_positive() => Ok(value),\n        ref t => Err(location.new_unexpected_token_error(t.clone())),\n    }\n}\n\nimpl<L> TrackBreadth<L> {\n    fn parse_keyword<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {\n        #[derive(Parse)]\n        enum TrackKeyword {\n            Auto,\n            MaxContent,\n            MinContent,\n        }\n\n        Ok(match TrackKeyword::parse(input)? {\n            TrackKeyword::Auto => TrackBreadth::Auto,\n            TrackKeyword::MaxContent => TrackBreadth::MaxContent,\n            TrackKeyword::MinContent => TrackBreadth::MinContent,\n        })\n    }\n}\n\nimpl Parse for TrackBreadth<LengthPercentage> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // FIXME: This and other callers in this file should use\n        // NonNegativeLengthPercentage instead.\n        //\n        // Though it seems these cannot be animated so it's ~ok.\n        if let Ok(lp) = input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {\n            return Ok(TrackBreadth::Breadth(lp));\n        }\n\n        if let Ok(f) = input.try_parse(parse_flex) {\n            return Ok(TrackBreadth::Fr(f));\n        }\n\n        Self::parse_keyword(input)\n    }\n}\n\nimpl Parse for TrackSize<LengthPercentage> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(b) = input.try_parse(|i| TrackBreadth::parse(context, i)) {\n            return Ok(TrackSize::Breadth(b));\n        }\n\n        if input\n            .try_parse(|i| i.expect_function_matching(\"minmax\"))\n            .is_ok()\n        {\n            return input.parse_nested_block(|input| {\n                let inflexible_breadth =\n                    match input.try_parse(|i| LengthPercentage::parse_non_negative(context, i)) {\n                        Ok(lp) => TrackBreadth::Breadth(lp),\n                        Err(..) => TrackBreadth::parse_keyword(input)?,\n                    };\n\n                input.expect_comma()?;\n                Ok(TrackSize::Minmax(\n                    inflexible_breadth,\n                    TrackBreadth::parse(context, input)?,\n                ))\n            });\n        }\n\n        input.expect_function_matching(\"fit-content\")?;\n        let lp = input.parse_nested_block(|i| LengthPercentage::parse_non_negative(context, i))?;\n        Ok(TrackSize::FitContent(TrackBreadth::Breadth(lp)))\n    }\n}\n\nimpl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use style_traits::{Separator, Space};\n        let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;\n        if track_sizes.len() == 1 && track_sizes[0].is_initial() {\n            // A single track with the initial value is always represented by an empty slice.\n            return Ok(Default::default());\n        }\n        return Ok(ImplicitGridTracks(track_sizes.into()));\n    }\n}\n\n/// Parse the grid line names into a vector of owned strings.\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-line-names>\npub fn parse_line_names<'i, 't>(\n    input: &mut Parser<'i, 't>,\n) -> Result<crate::OwnedSlice<CustomIdent>, ParseError<'i>> {\n    input.expect_square_bracket_block()?;\n    input.parse_nested_block(|input| {\n        let mut values = vec![];\n        while let Ok(ident) = input.try_parse(|i| CustomIdent::parse(i, &[\"span\", \"auto\"])) {\n            values.push(ident);\n        }\n\n        Ok(values.into())\n    })\n}\n\n/// The type of `repeat` function (only used in parsing).\n///\n/// <https://drafts.csswg.org/css-grid/#typedef-track-repeat>\n#[derive(Clone, Copy, Debug, PartialEq, SpecifiedValueInfo)]\n#[cfg_attr(feature = \"servo\", derive(MallocSizeOf))]\nenum RepeatType {\n    /// [`<auto-repeat>`](https://drafts.csswg.org/css-grid/#typedef-auto-repeat)\n    Auto,\n    /// [`<track-repeat>`](https://drafts.csswg.org/css-grid/#typedef-track-repeat)\n    Normal,\n    /// [`<fixed-repeat>`](https://drafts.csswg.org/css-grid/#typedef-fixed-repeat)\n    Fixed,\n}\n\nimpl TrackRepeat<LengthPercentage, Integer> {\n    fn parse_with_repeat_type<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<(Self, RepeatType), ParseError<'i>> {\n        input\n            .try_parse(|i| i.expect_function_matching(\"repeat\").map_err(|e| e.into()))\n            .and_then(|_| {\n                input.parse_nested_block(|input| {\n                    let count = RepeatCount::parse(context, input)?;\n                    input.expect_comma()?;\n\n                    let is_auto = count == RepeatCount::AutoFit || count == RepeatCount::AutoFill;\n                    let mut repeat_type = if is_auto {\n                        RepeatType::Auto\n                    } else {\n                        // <fixed-size> is a subset of <track-size>, so it should work for both\n                        RepeatType::Fixed\n                    };\n\n                    let mut names = vec![];\n                    let mut values = vec![];\n                    let mut current_names;\n\n                    loop {\n                        current_names = input.try_parse(parse_line_names).unwrap_or_default();\n                        if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {\n                            if !track_size.is_fixed() {\n                                if is_auto {\n                                    // should be <fixed-size> for <auto-repeat>\n                                    return Err(input\n                                        .new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                                }\n\n                                if repeat_type == RepeatType::Fixed {\n                                    repeat_type = RepeatType::Normal // <track-size> for sure\n                                }\n                            }\n\n                            values.push(track_size);\n                            names.push(current_names);\n                        } else {\n                            if values.is_empty() {\n                                // expecting at least one <track-size>\n                                return Err(\n                                    input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                                );\n                            }\n\n                            names.push(current_names); // final `<line-names>`\n                            break; // no more <track-size>, breaking\n                        }\n                    }\n\n                    let repeat = TrackRepeat {\n                        count,\n                        track_sizes: values.into(),\n                        line_names: names.into(),\n                    };\n\n                    Ok((repeat, repeat_type))\n                })\n            })\n    }\n}\n\nimpl Parse for TrackList<LengthPercentage, Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut current_names = vec![];\n        let mut names = vec![];\n        let mut values = vec![];\n\n        // Whether we've parsed an `<auto-repeat>` value.\n        let mut auto_repeat_index = None;\n        // assume that everything is <fixed-size>. This flag is useful when we encounter <auto-repeat>\n        let mut at_least_one_not_fixed = false;\n        loop {\n            current_names\n                .extend_from_slice(&mut input.try_parse(parse_line_names).unwrap_or_default());\n            if let Ok(track_size) = input.try_parse(|i| TrackSize::parse(context, i)) {\n                if !track_size.is_fixed() {\n                    at_least_one_not_fixed = true;\n                    if auto_repeat_index.is_some() {\n                        // <auto-track-list> only accepts <fixed-size> and <fixed-repeat>\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                }\n\n                let vec = mem::replace(&mut current_names, vec![]);\n                names.push(vec.into());\n                values.push(TrackListValue::TrackSize(track_size));\n            } else if let Ok((repeat, type_)) =\n                input.try_parse(|i| TrackRepeat::parse_with_repeat_type(context, i))\n            {\n                match type_ {\n                    RepeatType::Normal => {\n                        at_least_one_not_fixed = true;\n                        if auto_repeat_index.is_some() {\n                            // only <fixed-repeat>\n                            return Err(\n                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                            );\n                        }\n                    },\n                    RepeatType::Auto => {\n                        if auto_repeat_index.is_some() || at_least_one_not_fixed {\n                            // We've either seen <auto-repeat> earlier, or there's at least one non-fixed value\n                            return Err(\n                                input.new_custom_error(StyleParseErrorKind::UnspecifiedError)\n                            );\n                        }\n                        auto_repeat_index = Some(values.len());\n                    },\n                    RepeatType::Fixed => {},\n                }\n\n                let vec = mem::replace(&mut current_names, vec![]);\n                names.push(vec.into());\n                values.push(TrackListValue::TrackRepeat(repeat));\n            } else {\n                if values.is_empty() && auto_repeat_index.is_none() {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n\n                names.push(current_names.into());\n                break;\n            }\n        }\n\n        Ok(TrackList {\n            auto_repeat_index: auto_repeat_index.unwrap_or(std::usize::MAX),\n            values: values.into(),\n            line_names: names.into(),\n        })\n    }\n}\n\n#[cfg(feature = \"gecko\")]\n#[inline]\nfn allow_grid_template_subgrids() -> bool {\n    true\n}\n\n#[cfg(feature = \"servo\")]\n#[inline]\nfn allow_grid_template_subgrids() -> bool {\n    false\n}\n\n#[cfg(feature = \"gecko\")]\n#[inline]\nfn allow_grid_template_masonry() -> bool {\n    static_prefs::pref!(\"layout.css.grid-template-masonry-value.enabled\")\n}\n\n#[cfg(feature = \"servo\")]\n#[inline]\nfn allow_grid_template_masonry() -> bool {\n    false\n}\n\nimpl Parse for GridTemplateComponent<LengthPercentage, Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(GridTemplateComponent::None);\n        }\n\n        Self::parse_without_none(context, input)\n    }\n}\n\nimpl GridTemplateComponent<LengthPercentage, Integer> {\n    /// Parses a `GridTemplateComponent<LengthPercentage>` except `none` keyword.\n    pub fn parse_without_none<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if allow_grid_template_subgrids() {\n            if let Ok(t) = input.try_parse(|i| LineNameList::parse(context, i)) {\n                return Ok(GridTemplateComponent::Subgrid(Box::new(t)));\n            }\n        }\n        if allow_grid_template_masonry() {\n            if input\n                .try_parse(|i| i.expect_ident_matching(\"masonry\"))\n                .is_ok()\n            {\n                return Ok(GridTemplateComponent::Masonry);\n            }\n        }\n        let track_list = TrackList::parse(context, input)?;\n        Ok(GridTemplateComponent::TrackList(Box::new(track_list)))\n    }\n}\n\nimpl Parse for NameRepeat<Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"repeat\")?;\n        input.parse_nested_block(|i| {\n            let count = RepeatCount::parse(context, i)?;\n            // NameRepeat doesn't accept `auto-fit`\n            // https://drafts.csswg.org/css-grid/#typedef-name-repeat\n            if matches!(count, RepeatCount::AutoFit) {\n                return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            i.expect_comma()?;\n            let mut names_list = vec![];\n            names_list.push(parse_line_names(i)?); // there should be at least one\n            while let Ok(names) = i.try_parse(parse_line_names) {\n                names_list.push(names);\n            }\n\n            Ok(NameRepeat {\n                count,\n                line_names: names_list.into(),\n            })\n        })\n    }\n}\n\nimpl Parse for LineNameListValue<Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(repeat) = input.try_parse(|i| NameRepeat::parse(context, i)) {\n            return Ok(LineNameListValue::Repeat(repeat));\n        }\n\n        parse_line_names(input).map(LineNameListValue::LineNames)\n    }\n}\n\nimpl LineNameListValue<Integer> {\n    /// Returns the length of `<line-names>` after expanding repeat(N, ...). This returns zero for\n    /// repeat(auto-fill, ...).\n    #[inline]\n    pub fn line_names_length(&self) -> usize {\n        match *self {\n            Self::LineNames(..) => 1,\n            Self::Repeat(ref r) => {\n                match r.count {\n                    // Note: RepeatCount is always >= 1.\n                    RepeatCount::Number(v) => r.line_names.len() * v.value() as usize,\n                    _ => 0,\n                }\n            },\n        }\n    }\n}\n\nimpl Parse for LineNameList<Integer> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_ident_matching(\"subgrid\")?;\n\n        let mut auto_repeat = false;\n        let mut expanded_line_names_length = 0;\n        let mut line_names = vec![];\n        while let Ok(value) = input.try_parse(|i| LineNameListValue::parse(context, i)) {\n            match value {\n                LineNameListValue::Repeat(ref r) if r.is_auto_fill() => {\n                    if auto_repeat {\n                        // On a subgridded axis, the auto-fill keyword is only valid once per\n                        // <line-name-list>.\n                        // https://drafts.csswg.org/css-grid/#auto-repeat\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n                    auto_repeat = true;\n                },\n                _ => (),\n            };\n\n            expanded_line_names_length += value.line_names_length();\n            line_names.push(value);\n        }\n\n        Ok(LineNameList {\n            expanded_line_names_length,\n            line_names: line_names.into(),\n        })\n    }\n}\n"
  },
  {
    "path": "style/values/specified/image.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the specified value of\n//! [`image`][image]s\n//!\n//! [image]: https://drafts.csswg.org/css-images/#image-values\n\nuse crate::color::mix::ColorInterpolationMethod;\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::stylesheets::CorsMode;\nuse crate::values::generics::color::{ColorMixFlags, GenericLightDark};\nuse crate::values::generics::image::{\n    self as generic, Circle, Ellipse, GradientCompatMode, ShapeExtent,\n};\nuse crate::values::generics::image::{GradientFlags, PaintWorklet};\nuse crate::values::generics::position::Position as GenericPosition;\nuse crate::values::generics::NonNegative;\nuse crate::values::specified::position::{HorizontalPositionKeyword, VerticalPositionKeyword};\nuse crate::values::specified::position::{Position, PositionComponent, Side};\nuse crate::values::specified::url::SpecifiedUrl;\nuse crate::values::specified::{\n    Angle, AngleOrPercentage, Color, Length, LengthPercentage, NonNegativeLength,\n    NonNegativeLengthPercentage, Resolution,\n};\nuse crate::values::specified::{Number, NumberOrPercentage, Percentage};\nuse crate::Atom;\nuse cssparser::{match_ignore_ascii_case, Delimiter, Parser, Token};\nuse selectors::parser::SelectorParseErrorKind;\nuse std::cmp::Ordering;\nuse std::fmt::{self, Write};\nuse style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError};\nuse style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};\n\n#[inline]\nfn gradient_color_interpolation_method_enabled() -> bool {\n    static_prefs::pref!(\"layout.css.gradient-color-interpolation-method.enabled\")\n}\n\n/// Specified values for an image according to CSS-IMAGES.\n/// <https://drafts.csswg.org/css-images/#image-values>\npub type Image = generic::Image<Gradient, SpecifiedUrl, Color, Percentage, Resolution>;\n\n// Images should remain small, see https://github.com/servo/servo/pull/18430\nsize_of_test!(Image, 16);\n\n/// Specified values for a CSS gradient.\n/// <https://drafts.csswg.org/css-images/#gradients>\npub type Gradient = generic::Gradient<\n    LineDirection,\n    Length,\n    LengthPercentage,\n    Position,\n    Angle,\n    AngleOrPercentage,\n    Color,\n>;\n\n/// Specified values for CSS cross-fade\n/// cross-fade( CrossFadeElement, ...)\n/// <https://drafts.csswg.org/css-images-4/#cross-fade-function>\npub type CrossFade = generic::CrossFade<Image, Color, Percentage>;\n/// CrossFadeElement = percent? CrossFadeImage\npub type CrossFadeElement = generic::CrossFadeElement<Image, Color, Percentage>;\n/// CrossFadeImage = image | color\npub type CrossFadeImage = generic::CrossFadeImage<Image, Color>;\n\n/// `image-set()`\npub type ImageSet = generic::ImageSet<Image, Resolution>;\n\n/// Each of the arguments to `image-set()`\npub type ImageSetItem = generic::ImageSetItem<Image, Resolution>;\n\ntype LengthPercentageItemList = crate::OwnedSlice<generic::GradientItem<Color, LengthPercentage>>;\n\nimpl Color {\n    fn has_modern_syntax(&self) -> bool {\n        match self {\n            Self::Absolute(absolute) => !absolute.color.is_legacy_syntax(),\n            Self::ColorMix(mix) => {\n                if mix.flags.contains(ColorMixFlags::RESULT_IN_MODERN_SYNTAX) {\n                    true\n                } else {\n                    mix.items.iter().any(|item| item.color.has_modern_syntax())\n                }\n            },\n            Self::LightDark(ld) => ld.light.has_modern_syntax() || ld.dark.has_modern_syntax(),\n\n            // The default is that this color doesn't have any modern syntax.\n            _ => false,\n        }\n    }\n}\n\nfn default_color_interpolation_method<T>(\n    items: &[generic::GradientItem<Color, T>],\n) -> ColorInterpolationMethod {\n    let has_modern_syntax_item = items.iter().any(|item| match item {\n        generic::GenericGradientItem::SimpleColorStop(color) => color.has_modern_syntax(),\n        generic::GenericGradientItem::ComplexColorStop { color, .. } => color.has_modern_syntax(),\n        generic::GenericGradientItem::InterpolationHint(_) => false,\n    });\n\n    if has_modern_syntax_item {\n        ColorInterpolationMethod::default()\n    } else {\n        ColorInterpolationMethod::srgb()\n    }\n}\n\nfn image_light_dark_enabled(context: &ParserContext) -> bool {\n    context.chrome_rules_enabled() || static_prefs::pref!(\"layout.css.light-dark.images.enabled\")\n}\n\n#[cfg(feature = \"gecko\")]\nfn cross_fade_enabled() -> bool {\n    static_prefs::pref!(\"layout.css.cross-fade.enabled\")\n}\n\n#[cfg(feature = \"servo\")]\nfn cross_fade_enabled() -> bool {\n    false\n}\n\nimpl SpecifiedValueInfo for Gradient {\n    const SUPPORTED_TYPES: u8 = CssType::GRADIENT;\n\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        // This list here should keep sync with that in Gradient::parse.\n        f(&[\n            \"linear-gradient\",\n            \"-webkit-linear-gradient\",\n            \"-moz-linear-gradient\",\n            \"repeating-linear-gradient\",\n            \"-webkit-repeating-linear-gradient\",\n            \"-moz-repeating-linear-gradient\",\n            \"radial-gradient\",\n            \"-webkit-radial-gradient\",\n            \"-moz-radial-gradient\",\n            \"repeating-radial-gradient\",\n            \"-webkit-repeating-radial-gradient\",\n            \"-moz-repeating-radial-gradient\",\n            \"-webkit-gradient\",\n            \"conic-gradient\",\n            \"repeating-conic-gradient\",\n        ]);\n    }\n}\n\n// Need to manually implement as whether or not cross-fade shows up in\n// completions & etc is dependent on it being enabled.\nimpl<Image, Color, Percentage> SpecifiedValueInfo for generic::CrossFade<Image, Color, Percentage> {\n    const SUPPORTED_TYPES: u8 = 0;\n\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        if cross_fade_enabled() {\n            f(&[\"cross-fade\"]);\n        }\n    }\n}\n\nimpl<Image, Resolution> SpecifiedValueInfo for generic::ImageSet<Image, Resolution> {\n    const SUPPORTED_TYPES: u8 = 0;\n\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        f(&[\"image-set\"]);\n    }\n}\n\n/// A specified gradient line direction.\n///\n/// FIXME(emilio): This should be generic over Angle.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub enum LineDirection {\n    /// An angular direction.\n    Angle(Angle),\n    /// A horizontal direction.\n    Horizontal(HorizontalPositionKeyword),\n    /// A vertical direction.\n    Vertical(VerticalPositionKeyword),\n    /// A direction towards a corner of a box.\n    Corner(HorizontalPositionKeyword, VerticalPositionKeyword),\n}\n\n/// A specified ending shape.\npub type EndingShape = generic::EndingShape<NonNegativeLength, NonNegativeLengthPercentage>;\n\nbitflags! {\n    #[derive(Clone, Copy)]\n    struct ParseImageFlags: u8 {\n        const FORBID_NONE = 1 << 0;\n        const FORBID_IMAGE_SET = 1 << 1;\n        const FORBID_NON_URL = 1 << 2;\n    }\n}\n\nimpl Parse for Image {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Image, ParseError<'i>> {\n        Image::parse_with_cors_mode(context, input, CorsMode::None, ParseImageFlags::empty())\n    }\n}\n\nimpl Image {\n    fn parse_with_cors_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Image, ParseError<'i>> {\n        if !flags.contains(ParseImageFlags::FORBID_NONE)\n            && input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok()\n        {\n            return Ok(generic::Image::None);\n        }\n\n        if let Ok(url) =\n            input.try_parse(|input| SpecifiedUrl::parse_with_cors_mode(context, input, cors_mode))\n        {\n            return Ok(generic::Image::Url(url));\n        }\n\n        if !flags.contains(ParseImageFlags::FORBID_IMAGE_SET) {\n            if let Ok(is) =\n                input.try_parse(|input| ImageSet::parse(context, input, cors_mode, flags))\n            {\n                return Ok(generic::Image::ImageSet(Box::new(is)));\n            }\n        }\n\n        if flags.contains(ParseImageFlags::FORBID_NON_URL) {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        if let Ok(gradient) = input.try_parse(|i| Gradient::parse(context, i)) {\n            return Ok(generic::Image::Gradient(Box::new(gradient)));\n        }\n\n        let function = input.expect_function()?.clone();\n        input.parse_nested_block(|input| Ok(match_ignore_ascii_case! { &function,\n            #[cfg(feature = \"servo\")]\n            \"paint\" => Self::PaintWorklet(Box::new(<PaintWorklet>::parse_args(context, input)?)),\n            \"cross-fade\" if cross_fade_enabled() => Self::CrossFade(Box::new(CrossFade::parse_args(context, input, cors_mode, flags)?)),\n            \"light-dark\" if image_light_dark_enabled(context) => Self::LightDark(Box::new(GenericLightDark::parse_args_with(input, |input| {\n                Self::parse_with_cors_mode(context, input, cors_mode, flags)\n            })?)),\n            #[cfg(feature = \"gecko\")]\n            \"-moz-element\" => Self::Element(Self::parse_element(input)?),\n            #[cfg(feature = \"gecko\")]\n            \"-moz-symbolic-icon\" if context.chrome_rules_enabled() => Self::MozSymbolicIcon(input.expect_ident()?.as_ref().into()),\n            _ => return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function))),\n        }))\n    }\n}\n\nimpl Image {\n    /// Creates an already specified image value from an already resolved URL\n    /// for insertion in the cascade.\n    #[cfg(feature = \"servo\")]\n    pub fn for_cascade(url: ::servo_arc::Arc<::url::Url>) -> Self {\n        use crate::values::CssUrl;\n        generic::Image::Url(CssUrl::for_cascade(url))\n    }\n\n    /// Parses a `-moz-element(# <element-id>)`.\n    #[cfg(feature = \"gecko\")]\n    fn parse_element<'i>(input: &mut Parser<'i, '_>) -> Result<Atom, ParseError<'i>> {\n        let location = input.current_source_location();\n        Ok(match *input.next()? {\n            Token::IDHash(ref id) => Atom::from(id.as_ref()),\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        })\n    }\n\n    /// Provides an alternate method for parsing that associates the URL with\n    /// anonymous CORS headers.\n    pub fn parse_with_cors_anonymous<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Image, ParseError<'i>> {\n        Self::parse_with_cors_mode(\n            context,\n            input,\n            CorsMode::Anonymous,\n            ParseImageFlags::empty(),\n        )\n    }\n\n    /// Provides an alternate method for parsing, but forbidding `none`\n    pub fn parse_forbid_none<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Image, ParseError<'i>> {\n        Self::parse_with_cors_mode(context, input, CorsMode::None, ParseImageFlags::FORBID_NONE)\n    }\n\n    /// Provides an alternate method for parsing, but only for urls.\n    pub fn parse_only_url<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Image, ParseError<'i>> {\n        Self::parse_with_cors_mode(\n            context,\n            input,\n            CorsMode::None,\n            ParseImageFlags::FORBID_NONE | ParseImageFlags::FORBID_NON_URL,\n        )\n    }\n}\n\nimpl CrossFade {\n    /// cross-fade() = cross-fade( <cf-image># )\n    fn parse_args<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        let elements = crate::OwnedSlice::from(input.parse_comma_separated(|input| {\n            CrossFadeElement::parse(context, input, cors_mode, flags)\n        })?);\n        Ok(Self { elements })\n    }\n}\n\nimpl CrossFadeElement {\n    fn parse_percentage<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Option<Percentage> {\n        // We clamp our values here as this is the way that Safari and Chrome's\n        // implementation handle out-of-bounds percentages but whether or not\n        // this behavior follows the specification is still being discussed.\n        // See: <https://github.com/w3c/csswg-drafts/issues/5333>\n        input\n            .try_parse(|input| Percentage::parse_non_negative(context, input))\n            .ok()\n            .map(|p| p.clamp_to_hundred())\n    }\n\n    /// <cf-image> = <percentage>? && [ <image> | <color> ]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        // Try and parse a leading percent sign.\n        let mut percent = Self::parse_percentage(context, input);\n        // Parse the image\n        let image = CrossFadeImage::parse(context, input, cors_mode, flags)?;\n        // Try and parse a trailing percent sign.\n        if percent.is_none() {\n            percent = Self::parse_percentage(context, input);\n        }\n        Ok(Self {\n            percent: percent.into(),\n            image,\n        })\n    }\n}\n\nimpl CrossFadeImage {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(image) = input.try_parse(|input| {\n            Image::parse_with_cors_mode(\n                context,\n                input,\n                cors_mode,\n                flags | ParseImageFlags::FORBID_NONE,\n            )\n        }) {\n            return Ok(Self::Image(image));\n        }\n        Ok(Self::Color(Color::parse(context, input)?))\n    }\n}\n\nimpl ImageSet {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        let function = input.expect_function()?;\n        match_ignore_ascii_case! { &function,\n            \"-webkit-image-set\" | \"image-set\" => {},\n            _ => {\n                let func = function.clone();\n                return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));\n            }\n        }\n        let items = input.parse_nested_block(|input| {\n            input.parse_comma_separated(|input| {\n                ImageSetItem::parse(context, input, cors_mode, flags)\n            })\n        })?;\n        Ok(Self {\n            selected_index: std::usize::MAX,\n            items: items.into(),\n        })\n    }\n}\n\nimpl ImageSetItem {\n    fn parse_type<'i>(p: &mut Parser<'i, '_>) -> Result<crate::OwnedStr, ParseError<'i>> {\n        p.expect_function_matching(\"type\")?;\n        p.parse_nested_block(|input| Ok(input.expect_string()?.as_ref().to_owned().into()))\n    }\n\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        cors_mode: CorsMode,\n        flags: ParseImageFlags,\n    ) -> Result<Self, ParseError<'i>> {\n        let start = input.position().byte_index();\n        let location = input.current_source_location();\n        let image = match input.try_parse(|i| i.expect_url_or_string()) {\n            Ok(url) => {\n                let end = input.position().byte_index();\n                Image::Url(SpecifiedUrl::parse_from_string(\n                    url.as_ref().into(),\n                    start,\n                    end,\n                    context,\n                    cors_mode,\n                    location,\n                )?)\n            },\n            Err(..) => Image::parse_with_cors_mode(\n                context,\n                input,\n                cors_mode,\n                flags | ParseImageFlags::FORBID_NONE | ParseImageFlags::FORBID_IMAGE_SET,\n            )?,\n        };\n\n        let mut resolution = input\n            .try_parse(|input| Resolution::parse(context, input))\n            .ok();\n        let mime_type = input.try_parse(Self::parse_type).ok();\n\n        // Try to parse resolution after type().\n        if mime_type.is_some() && resolution.is_none() {\n            resolution = input\n                .try_parse(|input| Resolution::parse(context, input))\n                .ok();\n        }\n\n        let resolution = resolution.unwrap_or_else(|| Resolution::from_x(1.0));\n        let has_mime_type = mime_type.is_some();\n        let mime_type = mime_type.unwrap_or_default();\n\n        Ok(Self {\n            image,\n            resolution,\n            has_mime_type,\n            mime_type,\n        })\n    }\n}\n\nimpl Parse for Gradient {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        enum Shape {\n            Linear,\n            Radial,\n            Conic,\n        }\n\n        let func = input.expect_function()?;\n        let (shape, repeating, compat_mode) = match_ignore_ascii_case! { &func,\n            \"linear-gradient\" => {\n                (Shape::Linear, false, GradientCompatMode::Modern)\n            },\n            \"-webkit-linear-gradient\" => {\n                (Shape::Linear, false, GradientCompatMode::WebKit)\n            },\n            #[cfg(feature = \"gecko\")]\n            \"-moz-linear-gradient\" => {\n                (Shape::Linear, false, GradientCompatMode::Moz)\n            },\n            \"repeating-linear-gradient\" => {\n                (Shape::Linear, true, GradientCompatMode::Modern)\n            },\n            \"-webkit-repeating-linear-gradient\" => {\n                (Shape::Linear, true, GradientCompatMode::WebKit)\n            },\n            #[cfg(feature = \"gecko\")]\n            \"-moz-repeating-linear-gradient\" => {\n                (Shape::Linear, true, GradientCompatMode::Moz)\n            },\n            \"radial-gradient\" => {\n                (Shape::Radial, false, GradientCompatMode::Modern)\n            },\n            \"-webkit-radial-gradient\" => {\n                (Shape::Radial, false, GradientCompatMode::WebKit)\n            },\n            #[cfg(feature = \"gecko\")]\n            \"-moz-radial-gradient\" => {\n                (Shape::Radial, false, GradientCompatMode::Moz)\n            },\n            \"repeating-radial-gradient\" => {\n                (Shape::Radial, true, GradientCompatMode::Modern)\n            },\n            \"-webkit-repeating-radial-gradient\" => {\n                (Shape::Radial, true, GradientCompatMode::WebKit)\n            },\n            #[cfg(feature = \"gecko\")]\n            \"-moz-repeating-radial-gradient\" => {\n                (Shape::Radial, true, GradientCompatMode::Moz)\n            },\n            \"conic-gradient\" => {\n                (Shape::Conic, false, GradientCompatMode::Modern)\n            },\n            \"repeating-conic-gradient\" => {\n                (Shape::Conic, true, GradientCompatMode::Modern)\n            },\n            \"-webkit-gradient\" => {\n                return input.parse_nested_block(|i| {\n                    Self::parse_webkit_gradient_argument(context, i)\n                });\n            },\n            _ => {\n                let func = func.clone();\n                return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));\n            }\n        };\n\n        Ok(input.parse_nested_block(|i| {\n            Ok(match shape {\n                Shape::Linear => Self::parse_linear(context, i, repeating, compat_mode)?,\n                Shape::Radial => Self::parse_radial(context, i, repeating, compat_mode)?,\n                Shape::Conic => Self::parse_conic(context, i, repeating)?,\n            })\n        })?)\n    }\n}\n\nimpl Gradient {\n    fn parse_webkit_gradient_argument<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::specified::position::{\n            HorizontalPositionKeyword as X, VerticalPositionKeyword as Y,\n        };\n        type Point = GenericPosition<Component<X>, Component<Y>>;\n\n        #[derive(Clone, Copy, Parse)]\n        enum Component<S> {\n            Center,\n            Number(NumberOrPercentage),\n            Side(S),\n        }\n\n        fn line_direction_from_points(first: Point, second: Point) -> LineDirection {\n            let h_ord = first.horizontal.partial_cmp(&second.horizontal);\n            let v_ord = first.vertical.partial_cmp(&second.vertical);\n            let (h, v) = match (h_ord, v_ord) {\n                (Some(h), Some(v)) => (h, v),\n                _ => return LineDirection::Vertical(Y::Bottom),\n            };\n            match (h, v) {\n                (Ordering::Less, Ordering::Less) => LineDirection::Corner(X::Right, Y::Bottom),\n                (Ordering::Less, Ordering::Equal) => LineDirection::Horizontal(X::Right),\n                (Ordering::Less, Ordering::Greater) => LineDirection::Corner(X::Right, Y::Top),\n                (Ordering::Equal, Ordering::Greater) => LineDirection::Vertical(Y::Top),\n                (Ordering::Equal, Ordering::Equal) | (Ordering::Equal, Ordering::Less) => {\n                    LineDirection::Vertical(Y::Bottom)\n                },\n                (Ordering::Greater, Ordering::Less) => LineDirection::Corner(X::Left, Y::Bottom),\n                (Ordering::Greater, Ordering::Equal) => LineDirection::Horizontal(X::Left),\n                (Ordering::Greater, Ordering::Greater) => LineDirection::Corner(X::Left, Y::Top),\n            }\n        }\n\n        impl Parse for Point {\n            fn parse<'i, 't>(\n                context: &ParserContext,\n                input: &mut Parser<'i, 't>,\n            ) -> Result<Self, ParseError<'i>> {\n                input.try_parse(|i| {\n                    let x = Component::parse(context, i)?;\n                    let y = Component::parse(context, i)?;\n\n                    Ok(Self::new(x, y))\n                })\n            }\n        }\n\n        impl<S: Side> Into<NumberOrPercentage> for Component<S> {\n            fn into(self) -> NumberOrPercentage {\n                match self {\n                    Component::Center => NumberOrPercentage::Percentage(Percentage::new(0.5)),\n                    Component::Number(number) => number,\n                    Component::Side(side) => {\n                        let p = if side.is_start() {\n                            Percentage::zero()\n                        } else {\n                            Percentage::hundred()\n                        };\n                        NumberOrPercentage::Percentage(p)\n                    },\n                }\n            }\n        }\n\n        impl<S: Side> Into<PositionComponent<S>> for Component<S> {\n            fn into(self) -> PositionComponent<S> {\n                match self {\n                    Component::Center => PositionComponent::Center,\n                    Component::Number(NumberOrPercentage::Number(number)) => {\n                        PositionComponent::Length(Length::from_px(number.value).into())\n                    },\n                    Component::Number(NumberOrPercentage::Percentage(p)) => {\n                        PositionComponent::Length(p.into())\n                    },\n                    Component::Side(side) => PositionComponent::Side(side, None),\n                }\n            }\n        }\n\n        impl<S: Copy + Side> Component<S> {\n            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n                match ((*self).into(), (*other).into()) {\n                    (NumberOrPercentage::Percentage(a), NumberOrPercentage::Percentage(b)) => {\n                        a.get().partial_cmp(&b.get())\n                    },\n                    (NumberOrPercentage::Number(a), NumberOrPercentage::Number(b)) => {\n                        a.value.partial_cmp(&b.value)\n                    },\n                    (_, _) => None,\n                }\n            }\n        }\n\n        let ident = input.expect_ident_cloned()?;\n        input.expect_comma()?;\n\n        Ok(match_ignore_ascii_case! { &ident,\n            \"linear\" => {\n                let first = Point::parse(context, input)?;\n                input.expect_comma()?;\n                let second = Point::parse(context, input)?;\n\n                let direction = line_direction_from_points(first, second);\n                let items = Gradient::parse_webkit_gradient_stops(context, input, false)?;\n\n                generic::Gradient::Linear {\n                    direction,\n                    color_interpolation_method: ColorInterpolationMethod::srgb(),\n                    items,\n                    // Legacy gradients always use srgb as a default.\n                    flags: generic::GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD,\n                    compat_mode: GradientCompatMode::Modern,\n                }\n            },\n            \"radial\" => {\n                let first_point = Point::parse(context, input)?;\n                input.expect_comma()?;\n                let first_radius = Number::parse_non_negative(context, input)?;\n                input.expect_comma()?;\n                let second_point = Point::parse(context, input)?;\n                input.expect_comma()?;\n                let second_radius = Number::parse_non_negative(context, input)?;\n\n                let (reverse_stops, point, radius) = if second_radius.value >= first_radius.value {\n                    (false, second_point, second_radius)\n                } else {\n                    (true, first_point, first_radius)\n                };\n\n                let rad = Circle::Radius(NonNegative(Length::from_px(radius.value)));\n                let shape = generic::EndingShape::Circle(rad);\n                let position = Position::new(point.horizontal.into(), point.vertical.into());\n                let items = Gradient::parse_webkit_gradient_stops(context, input, reverse_stops)?;\n\n                generic::Gradient::Radial {\n                    shape,\n                    position,\n                    color_interpolation_method: ColorInterpolationMethod::srgb(),\n                    items,\n                    // Legacy gradients always use srgb as a default.\n                    flags: generic::GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD,\n                    compat_mode: GradientCompatMode::Modern,\n                }\n            },\n            _ => {\n                let e = SelectorParseErrorKind::UnexpectedIdent(ident.clone());\n                return Err(input.new_custom_error(e));\n            },\n        })\n    }\n\n    fn parse_webkit_gradient_stops<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        reverse_stops: bool,\n    ) -> Result<LengthPercentageItemList, ParseError<'i>> {\n        let mut items = input\n            .try_parse(|i| {\n                i.expect_comma()?;\n                i.parse_comma_separated(|i| {\n                    let function = i.expect_function()?.clone();\n                    let (color, mut p) = i.parse_nested_block(|i| {\n                        let p = match_ignore_ascii_case! { &function,\n                            \"color-stop\" => {\n                                let p = NumberOrPercentage::parse(context, i)?.to_percentage();\n                                i.expect_comma()?;\n                                p\n                            },\n                            \"from\" => Percentage::zero(),\n                            \"to\" => Percentage::hundred(),\n                            _ => {\n                                return Err(i.new_custom_error(\n                                    StyleParseErrorKind::UnexpectedFunction(function.clone())\n                                ))\n                            },\n                        };\n                        let color = Color::parse(context, i)?;\n                        if color == Color::CurrentColor {\n                            return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                        }\n                        Ok((color.into(), p))\n                    })?;\n                    if reverse_stops {\n                        p.reverse();\n                    }\n                    Ok(generic::GradientItem::ComplexColorStop {\n                        color,\n                        position: p.into(),\n                    })\n                })\n            })\n            .unwrap_or(vec![]);\n\n        if items.is_empty() {\n            items = vec![\n                generic::GradientItem::ComplexColorStop {\n                    color: Color::transparent(),\n                    position: LengthPercentage::zero_percent(),\n                },\n                generic::GradientItem::ComplexColorStop {\n                    color: Color::transparent(),\n                    position: LengthPercentage::hundred_percent(),\n                },\n            ];\n        } else if items.len() == 1 {\n            let first = items[0].clone();\n            items.push(first);\n        } else {\n            items.sort_by(|a, b| {\n                match (a, b) {\n                    (\n                        &generic::GradientItem::ComplexColorStop {\n                            position: ref a_position,\n                            ..\n                        },\n                        &generic::GradientItem::ComplexColorStop {\n                            position: ref b_position,\n                            ..\n                        },\n                    ) => match (a_position, b_position) {\n                        (&LengthPercentage::Percentage(a), &LengthPercentage::Percentage(b)) => {\n                            return a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal);\n                        },\n                        _ => {},\n                    },\n                    _ => {},\n                }\n                if reverse_stops {\n                    Ordering::Greater\n                } else {\n                    Ordering::Less\n                }\n            })\n        }\n        Ok(items.into())\n    }\n\n    /// Not used for -webkit-gradient syntax and conic-gradient\n    fn parse_stops<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<LengthPercentageItemList, ParseError<'i>> {\n        let items =\n            generic::GradientItem::parse_comma_separated(context, input, LengthPercentage::parse)?;\n        if items.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(items)\n    }\n\n    /// Try to parse a color interpolation method.\n    fn try_parse_color_interpolation_method<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Option<ColorInterpolationMethod> {\n        if gradient_color_interpolation_method_enabled() {\n            input\n                .try_parse(|i| ColorInterpolationMethod::parse(context, i))\n                .ok()\n        } else {\n            None\n        }\n    }\n\n    /// Parses a linear gradient.\n    /// GradientCompatMode can change during `-moz-` prefixed gradient parsing if it come across a `to` keyword.\n    fn parse_linear<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        repeating: bool,\n        mut compat_mode: GradientCompatMode,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut flags = GradientFlags::empty();\n        flags.set(GradientFlags::REPEATING, repeating);\n\n        let mut color_interpolation_method =\n            Self::try_parse_color_interpolation_method(context, input);\n\n        let direction = input\n            .try_parse(|p| LineDirection::parse(context, p, &mut compat_mode))\n            .ok();\n\n        if direction.is_some() && color_interpolation_method.is_none() {\n            color_interpolation_method = Self::try_parse_color_interpolation_method(context, input);\n        }\n\n        // If either of the 2 options were specified, we require a comma.\n        if color_interpolation_method.is_some() || direction.is_some() {\n            input.expect_comma()?;\n        }\n\n        let items = Gradient::parse_stops(context, input)?;\n\n        let default = default_color_interpolation_method(&items);\n        let color_interpolation_method = color_interpolation_method.unwrap_or(default);\n        flags.set(\n            GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD,\n            default == color_interpolation_method,\n        );\n\n        let direction = direction.unwrap_or(match compat_mode {\n            GradientCompatMode::Modern => LineDirection::Vertical(VerticalPositionKeyword::Bottom),\n            _ => LineDirection::Vertical(VerticalPositionKeyword::Top),\n        });\n\n        Ok(Gradient::Linear {\n            direction,\n            color_interpolation_method,\n            items,\n            flags,\n            compat_mode,\n        })\n    }\n\n    /// Parses a radial gradient.\n    fn parse_radial<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        repeating: bool,\n        compat_mode: GradientCompatMode,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut flags = GradientFlags::empty();\n        flags.set(GradientFlags::REPEATING, repeating);\n\n        let mut color_interpolation_method =\n            Self::try_parse_color_interpolation_method(context, input);\n\n        let (shape, position) = match compat_mode {\n            GradientCompatMode::Modern => {\n                let shape = input.try_parse(|i| EndingShape::parse(context, i, compat_mode));\n                let position = input.try_parse(|i| {\n                    i.expect_ident_matching(\"at\")?;\n                    Position::parse(context, i)\n                });\n                (shape, position.ok())\n            },\n            _ => {\n                let position = input.try_parse(|i| Position::parse(context, i));\n                let shape = input.try_parse(|i| {\n                    if position.is_ok() {\n                        i.expect_comma()?;\n                    }\n                    EndingShape::parse(context, i, compat_mode)\n                });\n                (shape, position.ok())\n            },\n        };\n\n        let has_shape_or_position = shape.is_ok() || position.is_some();\n        if has_shape_or_position && color_interpolation_method.is_none() {\n            color_interpolation_method = Self::try_parse_color_interpolation_method(context, input);\n        }\n\n        if has_shape_or_position || color_interpolation_method.is_some() {\n            input.expect_comma()?;\n        }\n\n        let shape = shape.unwrap_or({\n            generic::EndingShape::Ellipse(Ellipse::Extent(ShapeExtent::FarthestCorner))\n        });\n\n        let position = position.unwrap_or(Position::center());\n\n        let items = Gradient::parse_stops(context, input)?;\n\n        let default = default_color_interpolation_method(&items);\n        let color_interpolation_method = color_interpolation_method.unwrap_or(default);\n        flags.set(\n            GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD,\n            default == color_interpolation_method,\n        );\n\n        Ok(Gradient::Radial {\n            shape,\n            position,\n            color_interpolation_method,\n            items,\n            flags,\n            compat_mode,\n        })\n    }\n\n    /// Parse a conic gradient.\n    fn parse_conic<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        repeating: bool,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut flags = GradientFlags::empty();\n        flags.set(GradientFlags::REPEATING, repeating);\n\n        let mut color_interpolation_method =\n            Self::try_parse_color_interpolation_method(context, input);\n\n        let angle = input.try_parse(|i| {\n            i.expect_ident_matching(\"from\")?;\n            // Spec allows unitless zero start angles\n            // https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle\n            Angle::parse_with_unitless(context, i)\n        });\n        let position = input.try_parse(|i| {\n            i.expect_ident_matching(\"at\")?;\n            Position::parse(context, i)\n        });\n\n        let has_angle_or_position = angle.is_ok() || position.is_ok();\n        if has_angle_or_position && color_interpolation_method.is_none() {\n            color_interpolation_method = Self::try_parse_color_interpolation_method(context, input);\n        }\n\n        if has_angle_or_position || color_interpolation_method.is_some() {\n            input.expect_comma()?;\n        }\n\n        let angle = angle.unwrap_or(Angle::zero());\n\n        let position = position.unwrap_or(Position::center());\n\n        let items = generic::GradientItem::parse_comma_separated(\n            context,\n            input,\n            AngleOrPercentage::parse_with_unitless,\n        )?;\n\n        if items.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        let default = default_color_interpolation_method(&items);\n        let color_interpolation_method = color_interpolation_method.unwrap_or(default);\n        flags.set(\n            GradientFlags::HAS_DEFAULT_COLOR_INTERPOLATION_METHOD,\n            default == color_interpolation_method,\n        );\n\n        Ok(Gradient::Conic {\n            angle,\n            position,\n            color_interpolation_method,\n            items,\n            flags,\n        })\n    }\n}\n\nimpl generic::LineDirection for LineDirection {\n    fn points_downwards(&self, compat_mode: GradientCompatMode) -> bool {\n        match *self {\n            LineDirection::Angle(ref angle) => angle.degrees() == 180.0,\n            LineDirection::Vertical(VerticalPositionKeyword::Bottom) => {\n                compat_mode == GradientCompatMode::Modern\n            },\n            LineDirection::Vertical(VerticalPositionKeyword::Top) => {\n                compat_mode != GradientCompatMode::Modern\n            },\n            _ => false,\n        }\n    }\n\n    fn to_css<W>(&self, dest: &mut CssWriter<W>, compat_mode: GradientCompatMode) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            LineDirection::Angle(angle) => angle.to_css(dest),\n            LineDirection::Horizontal(x) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                x.to_css(dest)\n            },\n            LineDirection::Vertical(y) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                y.to_css(dest)\n            },\n            LineDirection::Corner(x, y) => {\n                if compat_mode == GradientCompatMode::Modern {\n                    dest.write_str(\"to \")?;\n                }\n                x.to_css(dest)?;\n                dest.write_char(' ')?;\n                y.to_css(dest)\n            },\n        }\n    }\n}\n\nimpl LineDirection {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        compat_mode: &mut GradientCompatMode,\n    ) -> Result<Self, ParseError<'i>> {\n        // Gradients allow unitless zero angles as an exception, see:\n        // https://github.com/w3c/csswg-drafts/issues/1162\n        if let Ok(angle) = input.try_parse(|i| Angle::parse_with_unitless(context, i)) {\n            return Ok(LineDirection::Angle(angle));\n        }\n\n        input.try_parse(|i| {\n            let to_ident = i.try_parse(|i| i.expect_ident_matching(\"to\"));\n            match *compat_mode {\n                // `to` keyword is mandatory in modern syntax.\n                GradientCompatMode::Modern => to_ident?,\n                // Fall back to Modern compatibility mode in case there is a `to` keyword.\n                // According to Gecko, `-moz-linear-gradient(to ...)` should serialize like\n                // `linear-gradient(to ...)`.\n                GradientCompatMode::Moz if to_ident.is_ok() => {\n                    *compat_mode = GradientCompatMode::Modern\n                },\n                // There is no `to` keyword in webkit prefixed syntax. If it's consumed,\n                // parsing should throw an error.\n                GradientCompatMode::WebKit if to_ident.is_ok() => {\n                    return Err(\n                        i.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(\"to\".into()))\n                    );\n                },\n                _ => {},\n            }\n\n            if let Ok(x) = i.try_parse(HorizontalPositionKeyword::parse) {\n                if let Ok(y) = i.try_parse(VerticalPositionKeyword::parse) {\n                    return Ok(LineDirection::Corner(x, y));\n                }\n                return Ok(LineDirection::Horizontal(x));\n            }\n            let y = VerticalPositionKeyword::parse(i)?;\n            if let Ok(x) = i.try_parse(HorizontalPositionKeyword::parse) {\n                return Ok(LineDirection::Corner(x, y));\n            }\n            Ok(LineDirection::Vertical(y))\n        })\n    }\n}\n\nimpl EndingShape {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        compat_mode: GradientCompatMode,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(extent) = input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))\n        {\n            if input\n                .try_parse(|i| i.expect_ident_matching(\"circle\"))\n                .is_ok()\n            {\n                return Ok(generic::EndingShape::Circle(Circle::Extent(extent)));\n            }\n            let _ = input.try_parse(|i| i.expect_ident_matching(\"ellipse\"));\n            return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(extent)));\n        }\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"circle\"))\n            .is_ok()\n        {\n            if let Ok(extent) =\n                input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))\n            {\n                return Ok(generic::EndingShape::Circle(Circle::Extent(extent)));\n            }\n            if compat_mode == GradientCompatMode::Modern {\n                if let Ok(length) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {\n                    return Ok(generic::EndingShape::Circle(Circle::Radius(length)));\n                }\n            }\n            return Ok(generic::EndingShape::Circle(Circle::Extent(\n                ShapeExtent::FarthestCorner,\n            )));\n        }\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"ellipse\"))\n            .is_ok()\n        {\n            if let Ok(extent) =\n                input.try_parse(|i| ShapeExtent::parse_with_compat_mode(i, compat_mode))\n            {\n                return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(extent)));\n            }\n            if compat_mode == GradientCompatMode::Modern {\n                let pair: Result<_, ParseError> = input.try_parse(|i| {\n                    let x = NonNegativeLengthPercentage::parse(context, i)?;\n                    let y = NonNegativeLengthPercentage::parse(context, i)?;\n                    Ok((x, y))\n                });\n                if let Ok((x, y)) = pair {\n                    return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(x, y)));\n                }\n            }\n            return Ok(generic::EndingShape::Ellipse(Ellipse::Extent(\n                ShapeExtent::FarthestCorner,\n            )));\n        }\n        if let Ok(length) = input.try_parse(|i| NonNegativeLength::parse(context, i)) {\n            if let Ok(y) = input.try_parse(|i| NonNegativeLengthPercentage::parse(context, i)) {\n                if compat_mode == GradientCompatMode::Modern {\n                    let _ = input.try_parse(|i| i.expect_ident_matching(\"ellipse\"));\n                }\n                return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(\n                    NonNegative(LengthPercentage::from(length.0)),\n                    y,\n                )));\n            }\n            if compat_mode == GradientCompatMode::Modern {\n                let y = input.try_parse(|i| {\n                    i.expect_ident_matching(\"ellipse\")?;\n                    NonNegativeLengthPercentage::parse(context, i)\n                });\n                if let Ok(y) = y {\n                    return Ok(generic::EndingShape::Ellipse(Ellipse::Radii(\n                        NonNegative(LengthPercentage::from(length.0)),\n                        y,\n                    )));\n                }\n                let _ = input.try_parse(|i| i.expect_ident_matching(\"circle\"));\n            }\n\n            return Ok(generic::EndingShape::Circle(Circle::Radius(length)));\n        }\n        input.try_parse(|i| {\n            let x = Percentage::parse_non_negative(context, i)?;\n            let y = if let Ok(y) = i.try_parse(|i| NonNegativeLengthPercentage::parse(context, i)) {\n                if compat_mode == GradientCompatMode::Modern {\n                    let _ = i.try_parse(|i| i.expect_ident_matching(\"ellipse\"));\n                }\n                y\n            } else {\n                if compat_mode == GradientCompatMode::Modern {\n                    i.expect_ident_matching(\"ellipse\")?;\n                }\n                NonNegativeLengthPercentage::parse(context, i)?\n            };\n            Ok(generic::EndingShape::Ellipse(Ellipse::Radii(\n                NonNegative(LengthPercentage::from(x)),\n                y,\n            )))\n        })\n    }\n}\n\nimpl ShapeExtent {\n    fn parse_with_compat_mode<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        compat_mode: GradientCompatMode,\n    ) -> Result<Self, ParseError<'i>> {\n        match Self::parse(input)? {\n            ShapeExtent::Contain | ShapeExtent::Cover\n                if compat_mode == GradientCompatMode::Modern =>\n            {\n                Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n            ShapeExtent::Contain => Ok(ShapeExtent::ClosestSide),\n            ShapeExtent::Cover => Ok(ShapeExtent::FarthestCorner),\n            keyword => Ok(keyword),\n        }\n    }\n}\n\nimpl<T> generic::GradientItem<Color, T> {\n    fn parse_comma_separated<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parse_position: impl for<'i1, 't1> Fn(&ParserContext, &mut Parser<'i1, 't1>) -> Result<T, ParseError<'i1>>\n            + Copy,\n    ) -> Result<crate::OwnedSlice<Self>, ParseError<'i>> {\n        let mut items = Vec::new();\n        let mut seen_stop = false;\n\n        loop {\n            input.parse_until_before(Delimiter::Comma, |input| {\n                if seen_stop {\n                    if let Ok(hint) = input.try_parse(|i| parse_position(context, i)) {\n                        seen_stop = false;\n                        items.push(generic::GradientItem::InterpolationHint(hint));\n                        return Ok(());\n                    }\n                }\n\n                let stop = generic::ColorStop::parse(context, input, parse_position)?;\n\n                if let Ok(multi_position) = input.try_parse(|i| parse_position(context, i)) {\n                    let stop_color = stop.color.clone();\n                    items.push(stop.into_item());\n                    items.push(\n                        generic::ColorStop {\n                            color: stop_color,\n                            position: Some(multi_position),\n                        }\n                        .into_item(),\n                    );\n                } else {\n                    items.push(stop.into_item());\n                }\n\n                seen_stop = true;\n                Ok(())\n            })?;\n\n            match input.next() {\n                Err(_) => break,\n                Ok(&Token::Comma) => continue,\n                Ok(_) => unreachable!(),\n            }\n        }\n\n        if !seen_stop || items.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(items.into())\n    }\n}\n\nimpl<T> generic::ColorStop<Color, T> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        parse_position: impl for<'i1, 't1> Fn(\n            &ParserContext,\n            &mut Parser<'i1, 't1>,\n        ) -> Result<T, ParseError<'i1>>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(generic::ColorStop {\n            color: Color::parse(context, input)?,\n            position: input.try_parse(|i| parse_position(context, i)).ok(),\n        })\n    }\n}\n\nimpl PaintWorklet {\n    #[cfg(feature = \"servo\")]\n    fn parse_args<'i>(\n        context: &ParserContext,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::custom_properties::SpecifiedValue;\n        use servo_arc::Arc;\n        let name = Atom::from(&**input.expect_ident()?);\n        let arguments = input\n            .try_parse(|input| {\n                input.expect_comma()?;\n                input.parse_comma_separated(|input| {\n                    SpecifiedValue::parse(\n                        input,\n                        Some(&context.namespaces.prefixes),\n                        &context.url_data,\n                    )\n                    .map(Arc::new)\n                })\n            })\n            .unwrap_or_default();\n        Ok(Self { name, arguments })\n    }\n}\n\n/// https://drafts.csswg.org/css-images/#propdef-image-rendering\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum ImageRendering {\n    Auto,\n    #[cfg(feature = \"gecko\")]\n    Smooth,\n    #[parse(aliases = \"-moz-crisp-edges\")]\n    CrispEdges,\n    Pixelated,\n    // From the spec:\n    //\n    //     This property previously accepted the values optimizeSpeed and\n    //     optimizeQuality. These are now deprecated; a user agent must accept\n    //     them as valid values but must treat them as having the same behavior\n    //     as crisp-edges and smooth respectively, and authors must not use\n    //     them.\n    //\n    #[cfg(feature = \"gecko\")]\n    Optimizespeed,\n    #[cfg(feature = \"gecko\")]\n    Optimizequality,\n}\n"
  },
  {
    "path": "style/values/specified/intersection_observer.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for intersection observer that utilizes style parser.\n//!\n//! <https://w3c.github.io/IntersectionObserver/#intersection-observer-api>\n\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::{self, Length, LengthPercentage};\nuse crate::values::generics::rect::Rect;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse std::fmt;\nuse style_traits::values::SequenceWriter;\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\nfn parse_pixel_or_percent<'i, 't>(\n    _context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n) -> Result<LengthPercentage, ParseError<'i>> {\n    let location = input.current_source_location();\n    let token = input.next()?;\n    let value = match *token {\n        Token::Dimension {\n            value, ref unit, ..\n        } => {\n            match_ignore_ascii_case! { unit,\n                \"px\" => Ok(LengthPercentage::new_length(Length::new(value))),\n                _ => Err(()),\n            }\n        },\n        Token::Percentage { unit_value, .. } => Ok(LengthPercentage::new_percent(\n            computed::Percentage(unit_value),\n        )),\n        _ => Err(()),\n    };\n    value.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n}\n\n/// The value of an IntersectionObserver's (root or scroll) margin property.\n///\n/// Only bare px or percentage values are allowed. Other length units and\n/// calc() values are not allowed.\n///\n/// <https://w3c.github.io/IntersectionObserver/#parse-a-margin>\n#[repr(transparent)]\npub struct IntersectionObserverMargin(pub Rect<LengthPercentage>);\n\nimpl Parse for IntersectionObserverMargin {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::Zero;\n        if input.is_exhausted() {\n            // If there are zero elements in tokens, set tokens to [\"0px\"].\n            return Ok(IntersectionObserverMargin(Rect::all(\n                LengthPercentage::zero(),\n            )));\n        }\n        let rect = Rect::parse_with(context, input, parse_pixel_or_percent)?;\n        Ok(IntersectionObserverMargin(rect))\n    }\n}\n\n// Strictly speaking this is not ToCss. It's serializing for DOM. But\n// we can just reuse the infrastructure of this.\n//\n// <https://w3c.github.io/IntersectionObserver/#dom-intersectionobserver-rootmargin>\nimpl ToCss for IntersectionObserverMargin {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        // We cannot use the ToCss impl of Rect, because that would\n        // merge items when they are equal. We want to list them all.\n        let mut writer = SequenceWriter::new(dest, \" \");\n        let rect = &self.0;\n        writer.item(&rect.0)?;\n        writer.item(&rect.1)?;\n        writer.item(&rect.2)?;\n        writer.item(&rect.3)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/length.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! [Length values][length].\n//!\n//! [length]: https://drafts.csswg.org/css-values/#lengths\n\nuse super::{AllowQuirks, Number, Percentage, ToComputedValue};\nuse crate::computed_value_flags::ComputedValueFlags;\nuse crate::derives::*;\nuse crate::font_metrics::{FontMetrics, FontMetricsOrientation};\n#[cfg(feature = \"gecko\")]\nuse crate::gecko_bindings::structs::GeckoFontMetrics;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::{self, CSSPixelLength, Context, FontSize};\nuse crate::values::generics::length as generics;\nuse crate::values::generics::length::{\n    GenericAnchorSizeFunction, GenericLengthOrNumber, GenericLengthPercentageOrNormal,\n    GenericMargin, GenericMaxSize, GenericSize,\n};\nuse crate::values::generics::NonNegative;\nuse crate::values::specified::calc::{self, AllowAnchorPositioningFunctions, CalcNode};\nuse crate::values::specified::font::QueryFontMetricsFlags;\nuse crate::values::specified::NonNegativeNumber;\nuse crate::values::CSSFloat;\nuse crate::{Zero, ZeroNoPercent};\nuse app_units::AU_PER_PX;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse debug_unreachable::debug_unreachable;\nuse std::cmp;\nuse std::fmt::{self, Write};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{\n    CssString, CssWriter, NumericValue, ParseError, ParsingMode, SpecifiedValueInfo,\n    StyleParseErrorKind, ToCss, ToTyped, TypedValue, UnitValue,\n};\nuse thin_vec::ThinVec;\n\npub use super::image::Image;\npub use super::image::{EndingShape as GradientEndingShape, Gradient};\npub use crate::values::specified::calc::CalcLengthPercentage;\n\n/// Number of pixels per inch\npub const PX_PER_IN: CSSFloat = 96.;\n/// Number of pixels per centimeter\npub const PX_PER_CM: CSSFloat = PX_PER_IN / 2.54;\n/// Number of pixels per millimeter\npub const PX_PER_MM: CSSFloat = PX_PER_IN / 25.4;\n/// Number of pixels per quarter\npub const PX_PER_Q: CSSFloat = PX_PER_MM / 4.;\n/// Number of pixels per point\npub const PX_PER_PT: CSSFloat = PX_PER_IN / 72.;\n/// Number of pixels per pica\npub const PX_PER_PC: CSSFloat = PX_PER_PT * 12.;\n\n/// A font relative length. Note that if any new value is\n/// added here, `custom_properties::NonCustomReferences::from_unit`\n/// must also be updated. Consult the comment in that function as to why.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum FontRelativeLength {\n    /// A \"em\" value: https://drafts.csswg.org/css-values/#em\n    #[css(dimension)]\n    Em(CSSFloat),\n    /// A \"ex\" value: https://drafts.csswg.org/css-values/#ex\n    #[css(dimension)]\n    Ex(CSSFloat),\n    /// A \"rex\" value: https://drafts.csswg.org/css-values/#rex\n    #[css(dimension)]\n    Rex(CSSFloat),\n    /// A \"ch\" value: https://drafts.csswg.org/css-values/#ch\n    #[css(dimension)]\n    Ch(CSSFloat),\n    /// A \"rch\" value: https://drafts.csswg.org/css-values/#rch\n    #[css(dimension)]\n    Rch(CSSFloat),\n    /// A \"cap\" value: https://drafts.csswg.org/css-values/#cap\n    #[css(dimension)]\n    Cap(CSSFloat),\n    /// A \"rcap\" value: https://drafts.csswg.org/css-values/#rcap\n    #[css(dimension)]\n    Rcap(CSSFloat),\n    /// An \"ic\" value: https://drafts.csswg.org/css-values/#ic\n    #[css(dimension)]\n    Ic(CSSFloat),\n    /// A \"ric\" value: https://drafts.csswg.org/css-values/#ric\n    #[css(dimension)]\n    Ric(CSSFloat),\n    /// A \"rem\" value: https://drafts.csswg.org/css-values/#rem\n    #[css(dimension)]\n    Rem(CSSFloat),\n    /// A \"lh\" value: https://drafts.csswg.org/css-values/#lh\n    #[css(dimension)]\n    Lh(CSSFloat),\n    /// A \"rlh\" value: https://drafts.csswg.org/css-values/#rlh\n    #[css(dimension)]\n    Rlh(CSSFloat),\n}\n\n/// A source to resolve font-relative units against\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum FontBaseSize {\n    /// Use the font-size of the current element.\n    CurrentStyle,\n    /// Use the inherited font-size.\n    InheritedStyle,\n}\n\n/// A source to resolve font-relative line-height units against.\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum LineHeightBase {\n    /// Use the line-height of the current element.\n    CurrentStyle,\n    /// Use the inherited line-height.\n    InheritedStyle,\n}\n\nimpl FontBaseSize {\n    /// Calculate the actual size for a given context\n    pub fn resolve(&self, context: &Context) -> computed::FontSize {\n        let style = context.style();\n        match *self {\n            Self::CurrentStyle => style.get_font().clone_font_size(),\n            Self::InheritedStyle => {\n                // If we're using the size from our inherited style, we still need to apply our\n                // own zoom.\n                let zoom = style.effective_zoom_for_inheritance;\n                style.get_parent_font().clone_font_size().zoom(zoom)\n            },\n        }\n    }\n}\n\nimpl FontRelativeLength {\n    /// Unit identifier for `em`.\n    pub const EM: &'static str = \"em\";\n    /// Unit identifier for `ex`.\n    pub const EX: &'static str = \"ex\";\n    /// Unit identifier for `rex`.\n    pub const REX: &'static str = \"rex\";\n    /// Unit identifier for `ch`.\n    pub const CH: &'static str = \"ch\";\n    /// Unit identifier for `rch`.\n    pub const RCH: &'static str = \"rch\";\n    /// Unit identifier for `cap`.\n    pub const CAP: &'static str = \"cap\";\n    /// Unit identifier for `rcap`.\n    pub const RCAP: &'static str = \"rcap\";\n    /// Unit identifier for `ic`.\n    pub const IC: &'static str = \"ic\";\n    /// Unit identifier for `ric`.\n    pub const RIC: &'static str = \"ric\";\n    /// Unit identifier for `rem`.\n    pub const REM: &'static str = \"rem\";\n    /// Unit identifier for `lh`.\n    pub const LH: &'static str = \"lh\";\n    /// Unit identifier for `rlh`.\n    pub const RLH: &'static str = \"rlh\";\n\n    /// Return the unitless, raw value.\n    fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            Self::Em(v)\n            | Self::Ex(v)\n            | Self::Rex(v)\n            | Self::Ch(v)\n            | Self::Rch(v)\n            | Self::Cap(v)\n            | Self::Rcap(v)\n            | Self::Ic(v)\n            | Self::Ric(v)\n            | Self::Rem(v)\n            | Self::Lh(v)\n            | Self::Rlh(v) => v,\n        }\n    }\n\n    // Return the unit, as a string.\n    fn unit(&self) -> &'static str {\n        match *self {\n            Self::Em(_) => Self::EM,\n            Self::Ex(_) => Self::EX,\n            Self::Rex(_) => Self::REX,\n            Self::Ch(_) => Self::CH,\n            Self::Rch(_) => Self::RCH,\n            Self::Cap(_) => Self::CAP,\n            Self::Rcap(_) => Self::RCAP,\n            Self::Ic(_) => Self::IC,\n            Self::Ric(_) => Self::RIC,\n            Self::Rem(_) => Self::REM,\n            Self::Lh(_) => Self::LH,\n            Self::Rlh(_) => Self::RLH,\n        }\n    }\n\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::FontRelativeLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        Ok(match (self, other) {\n            (&Em(one), &Em(other)) => Em(op(one, other)),\n            (&Ex(one), &Ex(other)) => Ex(op(one, other)),\n            (&Rex(one), &Rex(other)) => Rex(op(one, other)),\n            (&Ch(one), &Ch(other)) => Ch(op(one, other)),\n            (&Rch(one), &Rch(other)) => Rch(op(one, other)),\n            (&Cap(one), &Cap(other)) => Cap(op(one, other)),\n            (&Rcap(one), &Rcap(other)) => Rcap(op(one, other)),\n            (&Ic(one), &Ic(other)) => Ic(op(one, other)),\n            (&Ric(one), &Ric(other)) => Ric(op(one, other)),\n            (&Rem(one), &Rem(other)) => Rem(op(one, other)),\n            (&Lh(one), &Lh(other)) => Lh(op(one, other)),\n            (&Rlh(one), &Rlh(other)) => Rlh(op(one, other)),\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Em(..) | Rem(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..)\n                    | Ic(..) | Ric(..) | Lh(..) | Rlh(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_op()\")\n            },\n        })\n    }\n\n    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {\n        match self {\n            Self::Em(x) => Self::Em(op(*x)),\n            Self::Ex(x) => Self::Ex(op(*x)),\n            Self::Rex(x) => Self::Rex(op(*x)),\n            Self::Ch(x) => Self::Ch(op(*x)),\n            Self::Rch(x) => Self::Rch(op(*x)),\n            Self::Cap(x) => Self::Cap(op(*x)),\n            Self::Rcap(x) => Self::Rcap(op(*x)),\n            Self::Ic(x) => Self::Ic(op(*x)),\n            Self::Ric(x) => Self::Ric(op(*x)),\n            Self::Rem(x) => Self::Rem(op(*x)),\n            Self::Lh(x) => Self::Lh(op(*x)),\n            Self::Rlh(x) => Self::Rlh(op(*x)),\n        }\n    }\n\n    /// Computes the font-relative length.\n    pub fn to_computed_value(\n        &self,\n        context: &Context,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> computed::Length {\n        let (reference_size, length) =\n            self.reference_font_size_and_length(context, base_size, line_height_base);\n        (reference_size * length).finite()\n    }\n\n    /// Computes the length, given a GeckoFontMetrics getter to resolve font-relative units.\n    #[cfg(feature = \"gecko\")]\n    pub fn to_computed_pixel_length_with_font_metrics(\n        &self,\n        get_font_metrics: impl Fn() -> GeckoFontMetrics,\n    ) -> Result<CSSFloat, ()> {\n        let metrics = get_font_metrics();\n        Ok(match *self {\n            Self::Em(v) => v * metrics.mComputedEmSize.px(),\n            Self::Ex(v) => v * metrics.mXSize.px(),\n            Self::Ch(v) => v * metrics.mChSize.px(),\n            Self::Cap(v) => v * metrics.mCapHeight.px(),\n            Self::Ic(v) => v * metrics.mIcWidth.px(),\n            // `lh`, `rlh` are unsupported as we have no line-height context\n            // `rem`, `rex`, `rch`, `rcap`, and `ric` are unsupported as we have no root font context.\n            Self::Lh(_)\n            | Self::Rlh(_)\n            | Self::Rem(_)\n            | Self::Rex(_)\n            | Self::Rch(_)\n            | Self::Rcap(_)\n            | Self::Ric(_) => return Err(()),\n        })\n    }\n\n    /// Return reference font size.\n    ///\n    /// We use the base_size flag to pass a different size for computing\n    /// font-size and unconstrained font-size.\n    ///\n    /// This returns a pair, the first one is the reference font size, and the\n    /// second one is the unpacked relative length.\n    fn reference_font_size_and_length(\n        &self,\n        context: &Context,\n        base_size: FontBaseSize,\n        line_height_base: LineHeightBase,\n    ) -> (computed::Length, CSSFloat) {\n        fn query_font_metrics(\n            context: &Context,\n            base_size: FontBaseSize,\n            orientation: FontMetricsOrientation,\n            flags: QueryFontMetricsFlags,\n        ) -> FontMetrics {\n            context.query_font_metrics(base_size, orientation, flags)\n        }\n\n        fn ex_size(\n            context: &Context,\n            base_size: FontBaseSize,\n            reference_font_size: &FontSize,\n        ) -> computed::Length {\n            // The x-height is an intrinsically horizontal metric.\n            let metrics = query_font_metrics(\n                context,\n                base_size,\n                FontMetricsOrientation::Horizontal,\n                QueryFontMetricsFlags::empty(),\n            );\n            metrics.x_height_or_default(reference_font_size.used_size())\n        }\n\n        fn ch_size(\n            context: &Context,\n            base_size: FontBaseSize,\n            reference_font_size: &FontSize,\n        ) -> computed::Length {\n            // https://drafts.csswg.org/css-values/#ch:\n            //\n            //     Equal to the used advance measure of the “0” (ZERO,\n            //     U+0030) glyph in the font used to render it. (The advance\n            //     measure of a glyph is its advance width or height,\n            //     whichever is in the inline axis of the element.)\n            //\n            let metrics = query_font_metrics(\n                context,\n                base_size,\n                FontMetricsOrientation::MatchContextPreferHorizontal,\n                QueryFontMetricsFlags::NEEDS_CH,\n            );\n            metrics.zero_advance_measure_or_default(\n                reference_font_size.used_size(),\n                context.style().writing_mode.is_upright(),\n            )\n        }\n\n        fn cap_size(context: &Context, base_size: FontBaseSize) -> computed::Length {\n            let metrics = query_font_metrics(\n                context,\n                base_size,\n                FontMetricsOrientation::Horizontal,\n                QueryFontMetricsFlags::empty(),\n            );\n            metrics.cap_height_or_default()\n        }\n\n        fn ic_size(\n            context: &Context,\n            base_size: FontBaseSize,\n            reference_font_size: &FontSize,\n        ) -> computed::Length {\n            let metrics = query_font_metrics(\n                context,\n                base_size,\n                FontMetricsOrientation::MatchContextPreferVertical,\n                QueryFontMetricsFlags::NEEDS_IC,\n            );\n            metrics.ic_width_or_default(reference_font_size.used_size())\n        }\n\n        if context.in_container_query {\n            context\n                .builder\n                .add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS_IN_CONTAINER_QUERY);\n        }\n\n        let reference_font_size = base_size.resolve(context);\n        match *self {\n            // Local font-relative units\n            Self::Em(length) => {\n                if context.for_non_inherited_property && base_size == FontBaseSize::CurrentStyle {\n                    context\n                        .rule_cache_conditions\n                        .borrow_mut()\n                        .set_font_size_dependency(reference_font_size.computed_size);\n                }\n\n                (reference_font_size.computed_size(), length)\n            },\n            Self::Lh(length) => {\n                // https://drafts.csswg.org/css-values-4/#lh\n                //\n                //     When specified in media-query, the lh units refer to the\n                //     initial values of font and line-height properties.\n                //\n                let reference_size = if context.in_media_query {\n                    context\n                        .device()\n                        .calc_line_height(\n                            &context.default_style().get_font(),\n                            context.style().writing_mode,\n                            None,\n                        )\n                        .0\n                } else {\n                    let line_height = context.builder.calc_line_height(\n                        context.device(),\n                        line_height_base,\n                        context.style().writing_mode,\n                    );\n                    if context.for_non_inherited_property\n                        && line_height_base == LineHeightBase::CurrentStyle\n                    {\n                        context\n                            .rule_cache_conditions\n                            .borrow_mut()\n                            .set_line_height_dependency(line_height)\n                    }\n                    line_height.0\n                };\n                (reference_size, length)\n            },\n            Self::Ex(length) => (ex_size(context, base_size, &reference_font_size), length),\n            Self::Ch(length) => (ch_size(context, base_size, &reference_font_size), length),\n            Self::Cap(length) => (cap_size(context, base_size), length),\n            Self::Ic(length) => (ic_size(context, base_size, &reference_font_size), length),\n\n            // Root font relative units\n            Self::Rex(length) => {\n                let reference_size = if context.builder.is_root_element || context.in_media_query {\n                    ex_size(context, base_size, &reference_font_size)\n                } else {\n                    context\n                        .device()\n                        .root_font_metrics_ex()\n                        .zoom(context.builder.effective_zoom)\n                };\n                (reference_size, length)\n            },\n            Self::Rch(length) => {\n                let reference_size = if context.builder.is_root_element || context.in_media_query {\n                    ch_size(context, base_size, &reference_font_size)\n                } else {\n                    context\n                        .device()\n                        .root_font_metrics_ch()\n                        .zoom(context.builder.effective_zoom)\n                };\n                (reference_size, length)\n            },\n            Self::Rcap(length) => {\n                let reference_size = if context.builder.is_root_element || context.in_media_query {\n                    cap_size(context, base_size)\n                } else {\n                    context\n                        .device()\n                        .root_font_metrics_cap()\n                        .zoom(context.builder.effective_zoom)\n                };\n                (reference_size, length)\n            },\n            Self::Ric(length) => {\n                let reference_size = if context.builder.is_root_element || context.in_media_query {\n                    ic_size(context, base_size, &reference_font_size)\n                } else {\n                    context\n                        .device()\n                        .root_font_metrics_ic()\n                        .zoom(context.builder.effective_zoom)\n                };\n                (reference_size, length)\n            },\n            Self::Rem(length) => {\n                // https://drafts.csswg.org/css-values/#rem:\n                //\n                //     When specified on the font-size property of the root\n                //     element, the rem units refer to the property's initial\n                //     value.\n                //\n                let reference_size = if context.builder.is_root_element || context.in_media_query {\n                    reference_font_size.computed_size()\n                } else {\n                    context\n                        .device()\n                        .root_font_size()\n                        .zoom(context.builder.effective_zoom)\n                };\n                (reference_size, length)\n            },\n            Self::Rlh(length) => {\n                // https://drafts.csswg.org/css-values-4/#rlh\n                //\n                //     When specified on the root element, the rlh units refer\n                //     to the initial values of font and line-height properties.\n                //\n                let reference_size = if context.builder.is_root_element {\n                    context\n                        .builder\n                        .calc_line_height(\n                            context.device(),\n                            line_height_base,\n                            context.style().writing_mode,\n                        )\n                        .0\n                } else if context.in_media_query {\n                    context\n                        .device()\n                        .calc_line_height(\n                            &context.default_style().get_font(),\n                            context.style().writing_mode,\n                            None,\n                        )\n                        .0\n                } else {\n                    context.device().root_line_height()\n                };\n                let reference_size = reference_size.zoom(context.builder.effective_zoom);\n                (reference_size, length)\n            },\n        }\n    }\n}\n\n/// https://drafts.csswg.org/css-values/#viewport-variants\npub enum ViewportVariant {\n    /// https://drafts.csswg.org/css-values/#ua-default-viewport-size\n    UADefault,\n    /// https://drafts.csswg.org/css-values/#small-viewport-percentage-units\n    Small,\n    /// https://drafts.csswg.org/css-values/#large-viewport-percentage-units\n    Large,\n    /// https://drafts.csswg.org/css-values/#dynamic-viewport-percentage-units\n    Dynamic,\n}\n\n/// https://drafts.csswg.org/css-values/#viewport-relative-units\n#[derive(PartialEq)]\nenum ViewportUnit {\n    /// *vw units.\n    Vw,\n    /// *vh units.\n    Vh,\n    /// *vmin units.\n    Vmin,\n    /// *vmax units.\n    Vmax,\n    /// *vb units.\n    Vb,\n    /// *vi units.\n    Vi,\n}\n\n/// A viewport-relative length.\n///\n/// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum ViewportPercentageLength {\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vw>\n    #[css(dimension)]\n    Vw(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svw>\n    #[css(dimension)]\n    Svw(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvw>\n    #[css(dimension)]\n    Lvw(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvw>\n    #[css(dimension)]\n    Dvw(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vh>\n    #[css(dimension)]\n    Vh(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svh>\n    #[css(dimension)]\n    Svh(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvh>\n    #[css(dimension)]\n    Lvh(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvh>\n    #[css(dimension)]\n    Dvh(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vmin>\n    #[css(dimension)]\n    Vmin(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svmin>\n    #[css(dimension)]\n    Svmin(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmin>\n    #[css(dimension)]\n    Lvmin(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmin>\n    #[css(dimension)]\n    Dvmin(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vmax>\n    #[css(dimension)]\n    Vmax(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svmax>\n    #[css(dimension)]\n    Svmax(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvmax>\n    #[css(dimension)]\n    Lvmax(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvmax>\n    #[css(dimension)]\n    Dvmax(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vb>\n    #[css(dimension)]\n    Vb(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svb>\n    #[css(dimension)]\n    Svb(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvb>\n    #[css(dimension)]\n    Lvb(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvb>\n    #[css(dimension)]\n    Dvb(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-vi>\n    #[css(dimension)]\n    Vi(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-svi>\n    #[css(dimension)]\n    Svi(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-lvi>\n    #[css(dimension)]\n    Lvi(CSSFloat),\n    /// <https://drafts.csswg.org/css-values/#valdef-length-dvi>\n    #[css(dimension)]\n    Dvi(CSSFloat),\n}\n\nimpl ViewportPercentageLength {\n    /// Return the unitless, raw value.\n    fn unitless_value(&self) -> CSSFloat {\n        self.unpack().2\n    }\n\n    // Return the unit, as a string.\n    fn unit(&self) -> &'static str {\n        match *self {\n            Self::Vw(_) => \"vw\",\n            Self::Lvw(_) => \"lvw\",\n            Self::Svw(_) => \"svw\",\n            Self::Dvw(_) => \"dvw\",\n            Self::Vh(_) => \"vh\",\n            Self::Svh(_) => \"svh\",\n            Self::Lvh(_) => \"lvh\",\n            Self::Dvh(_) => \"dvh\",\n            Self::Vmin(_) => \"vmin\",\n            Self::Svmin(_) => \"svmin\",\n            Self::Lvmin(_) => \"lvmin\",\n            Self::Dvmin(_) => \"dvmin\",\n            Self::Vmax(_) => \"vmax\",\n            Self::Svmax(_) => \"svmax\",\n            Self::Lvmax(_) => \"lvmax\",\n            Self::Dvmax(_) => \"dvmax\",\n            Self::Vb(_) => \"vb\",\n            Self::Svb(_) => \"svb\",\n            Self::Lvb(_) => \"lvb\",\n            Self::Dvb(_) => \"dvb\",\n            Self::Vi(_) => \"vi\",\n            Self::Svi(_) => \"svi\",\n            Self::Lvi(_) => \"lvi\",\n            Self::Dvi(_) => \"dvi\",\n        }\n    }\n\n    fn unpack(&self) -> (ViewportVariant, ViewportUnit, CSSFloat) {\n        match *self {\n            Self::Vw(v) => (ViewportVariant::UADefault, ViewportUnit::Vw, v),\n            Self::Svw(v) => (ViewportVariant::Small, ViewportUnit::Vw, v),\n            Self::Lvw(v) => (ViewportVariant::Large, ViewportUnit::Vw, v),\n            Self::Dvw(v) => (ViewportVariant::Dynamic, ViewportUnit::Vw, v),\n            Self::Vh(v) => (ViewportVariant::UADefault, ViewportUnit::Vh, v),\n            Self::Svh(v) => (ViewportVariant::Small, ViewportUnit::Vh, v),\n            Self::Lvh(v) => (ViewportVariant::Large, ViewportUnit::Vh, v),\n            Self::Dvh(v) => (ViewportVariant::Dynamic, ViewportUnit::Vh, v),\n            Self::Vmin(v) => (ViewportVariant::UADefault, ViewportUnit::Vmin, v),\n            Self::Svmin(v) => (ViewportVariant::Small, ViewportUnit::Vmin, v),\n            Self::Lvmin(v) => (ViewportVariant::Large, ViewportUnit::Vmin, v),\n            Self::Dvmin(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmin, v),\n            Self::Vmax(v) => (ViewportVariant::UADefault, ViewportUnit::Vmax, v),\n            Self::Svmax(v) => (ViewportVariant::Small, ViewportUnit::Vmax, v),\n            Self::Lvmax(v) => (ViewportVariant::Large, ViewportUnit::Vmax, v),\n            Self::Dvmax(v) => (ViewportVariant::Dynamic, ViewportUnit::Vmax, v),\n            Self::Vb(v) => (ViewportVariant::UADefault, ViewportUnit::Vb, v),\n            Self::Svb(v) => (ViewportVariant::Small, ViewportUnit::Vb, v),\n            Self::Lvb(v) => (ViewportVariant::Large, ViewportUnit::Vb, v),\n            Self::Dvb(v) => (ViewportVariant::Dynamic, ViewportUnit::Vb, v),\n            Self::Vi(v) => (ViewportVariant::UADefault, ViewportUnit::Vi, v),\n            Self::Svi(v) => (ViewportVariant::Small, ViewportUnit::Vi, v),\n            Self::Lvi(v) => (ViewportVariant::Large, ViewportUnit::Vi, v),\n            Self::Dvi(v) => (ViewportVariant::Dynamic, ViewportUnit::Vi, v),\n        }\n    }\n\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::ViewportPercentageLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        Ok(match (self, other) {\n            (&Vw(one), &Vw(other)) => Vw(op(one, other)),\n            (&Svw(one), &Svw(other)) => Svw(op(one, other)),\n            (&Lvw(one), &Lvw(other)) => Lvw(op(one, other)),\n            (&Dvw(one), &Dvw(other)) => Dvw(op(one, other)),\n            (&Vh(one), &Vh(other)) => Vh(op(one, other)),\n            (&Svh(one), &Svh(other)) => Svh(op(one, other)),\n            (&Lvh(one), &Lvh(other)) => Lvh(op(one, other)),\n            (&Dvh(one), &Dvh(other)) => Dvh(op(one, other)),\n            (&Vmin(one), &Vmin(other)) => Vmin(op(one, other)),\n            (&Svmin(one), &Svmin(other)) => Svmin(op(one, other)),\n            (&Lvmin(one), &Lvmin(other)) => Lvmin(op(one, other)),\n            (&Dvmin(one), &Dvmin(other)) => Dvmin(op(one, other)),\n            (&Vmax(one), &Vmax(other)) => Vmax(op(one, other)),\n            (&Svmax(one), &Svmax(other)) => Svmax(op(one, other)),\n            (&Lvmax(one), &Lvmax(other)) => Lvmax(op(one, other)),\n            (&Dvmax(one), &Dvmax(other)) => Dvmax(op(one, other)),\n            (&Vb(one), &Vb(other)) => Vb(op(one, other)),\n            (&Svb(one), &Svb(other)) => Svb(op(one, other)),\n            (&Lvb(one), &Lvb(other)) => Lvb(op(one, other)),\n            (&Dvb(one), &Dvb(other)) => Dvb(op(one, other)),\n            (&Vi(one), &Vi(other)) => Vi(op(one, other)),\n            (&Svi(one), &Svi(other)) => Svi(op(one, other)),\n            (&Lvi(one), &Lvi(other)) => Lvi(op(one, other)),\n            (&Dvi(one), &Dvi(other)) => Dvi(op(one, other)),\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)\n                    | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)\n                    | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)\n                    | Svi(..) | Lvi(..) | Dvi(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_op()\")\n            },\n        })\n    }\n\n    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {\n        match self {\n            Self::Vw(x) => Self::Vw(op(*x)),\n            Self::Svw(x) => Self::Svw(op(*x)),\n            Self::Lvw(x) => Self::Lvw(op(*x)),\n            Self::Dvw(x) => Self::Dvw(op(*x)),\n            Self::Vh(x) => Self::Vh(op(*x)),\n            Self::Svh(x) => Self::Svh(op(*x)),\n            Self::Lvh(x) => Self::Lvh(op(*x)),\n            Self::Dvh(x) => Self::Dvh(op(*x)),\n            Self::Vmin(x) => Self::Vmin(op(*x)),\n            Self::Svmin(x) => Self::Svmin(op(*x)),\n            Self::Lvmin(x) => Self::Lvmin(op(*x)),\n            Self::Dvmin(x) => Self::Dvmin(op(*x)),\n            Self::Vmax(x) => Self::Vmax(op(*x)),\n            Self::Svmax(x) => Self::Svmax(op(*x)),\n            Self::Lvmax(x) => Self::Lvmax(op(*x)),\n            Self::Dvmax(x) => Self::Dvmax(op(*x)),\n            Self::Vb(x) => Self::Vb(op(*x)),\n            Self::Svb(x) => Self::Svb(op(*x)),\n            Self::Lvb(x) => Self::Lvb(op(*x)),\n            Self::Dvb(x) => Self::Dvb(op(*x)),\n            Self::Vi(x) => Self::Vi(op(*x)),\n            Self::Svi(x) => Self::Svi(op(*x)),\n            Self::Lvi(x) => Self::Lvi(op(*x)),\n            Self::Dvi(x) => Self::Dvi(op(*x)),\n        }\n    }\n\n    /// Computes the given viewport-relative length for the given viewport size.\n    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {\n        let (variant, unit, factor) = self.unpack();\n        let size = context.viewport_size_for_viewport_unit_resolution(variant);\n        let length: app_units::Au = match unit {\n            ViewportUnit::Vw => size.width,\n            ViewportUnit::Vh => size.height,\n            ViewportUnit::Vmin => cmp::min(size.width, size.height),\n            ViewportUnit::Vmax => cmp::max(size.width, size.height),\n            ViewportUnit::Vi | ViewportUnit::Vb => {\n                context\n                    .rule_cache_conditions\n                    .borrow_mut()\n                    .set_writing_mode_dependency(context.builder.writing_mode);\n                if (unit == ViewportUnit::Vb) == context.style().writing_mode.is_vertical() {\n                    size.width\n                } else {\n                    size.height\n                }\n            },\n        };\n\n        // NOTE: This is in app units!\n        let length = context.builder.effective_zoom.zoom(length.0 as f32);\n\n        // FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.\n        // See bug 989802. We truncate so that adding multiple viewport units that add up to 100\n        // does not overflow due to rounding differences. We convert appUnits to CSS px manually\n        // here to avoid premature clamping by going through the Au type.\n        let trunc_scaled =\n            ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;\n        CSSPixelLength::new(crate::values::normalize(trunc_scaled))\n    }\n}\n\n/// HTML5 \"character width\", as defined in HTML5 § 14.5.4.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(C)]\npub struct CharacterWidth(pub i32);\n\nimpl CharacterWidth {\n    /// Computes the given character width.\n    pub fn to_computed_value(&self, reference_font_size: computed::Length) -> computed::Length {\n        // This applies the *converting a character width to pixels* algorithm\n        // as specified in HTML5 § 14.5.4.\n        //\n        // TODO(pcwalton): Find these from the font.\n        let average_advance = reference_font_size * 0.5;\n        let max_advance = reference_font_size;\n        (average_advance * (self.0 as CSSFloat - 1.0) + max_advance).finite()\n    }\n}\n\n/// Represents an absolute length with its unit\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum AbsoluteLength {\n    /// An absolute length in pixels (px)\n    #[css(dimension)]\n    Px(CSSFloat),\n    /// An absolute length in inches (in)\n    #[css(dimension)]\n    In(CSSFloat),\n    /// An absolute length in centimeters (cm)\n    #[css(dimension)]\n    Cm(CSSFloat),\n    /// An absolute length in millimeters (mm)\n    #[css(dimension)]\n    Mm(CSSFloat),\n    /// An absolute length in quarter-millimeters (q)\n    #[css(dimension)]\n    Q(CSSFloat),\n    /// An absolute length in points (pt)\n    #[css(dimension)]\n    Pt(CSSFloat),\n    /// An absolute length in pica (pc)\n    #[css(dimension)]\n    Pc(CSSFloat),\n}\n\nimpl AbsoluteLength {\n    /// Return the unitless, raw value.\n    fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            Self::Px(v)\n            | Self::In(v)\n            | Self::Cm(v)\n            | Self::Mm(v)\n            | Self::Q(v)\n            | Self::Pt(v)\n            | Self::Pc(v) => v,\n        }\n    }\n\n    // Return the unit, as a string.\n    fn unit(&self) -> &'static str {\n        match *self {\n            Self::Px(_) => \"px\",\n            Self::In(_) => \"in\",\n            Self::Cm(_) => \"cm\",\n            Self::Mm(_) => \"mm\",\n            Self::Q(_) => \"q\",\n            Self::Pt(_) => \"pt\",\n            Self::Pc(_) => \"pc\",\n        }\n    }\n\n    // Return the canonical unit for this value.\n    fn canonical_unit(&self) -> Option<&'static str> {\n        Some(\"px\")\n    }\n\n    // Convert this value to the specified unit, if possible.\n    fn to(&self, unit: &str) -> Result<Self, ()> {\n        let px = self.to_px();\n\n        Ok(match_ignore_ascii_case! { unit,\n            \"px\" => Self::Px(px),\n            \"in\" => Self::In(px / PX_PER_IN),\n            \"cm\" => Self::Cm(px / PX_PER_CM),\n            \"mm\" => Self::Mm(px / PX_PER_MM),\n            \"q\" => Self::Q(px / PX_PER_Q),\n            \"pt\" => Self::Pt(px / PX_PER_PT),\n            \"pc\" => Self::Pc(px / PX_PER_PC),\n             _ => return Err(()),\n        })\n    }\n\n    /// Convert this into a pixel value.\n    #[inline]\n    pub fn to_px(&self) -> CSSFloat {\n        match *self {\n            Self::Px(value) => value,\n            Self::In(value) => value * PX_PER_IN,\n            Self::Cm(value) => value * PX_PER_CM,\n            Self::Mm(value) => value * PX_PER_MM,\n            Self::Q(value) => value * PX_PER_Q,\n            Self::Pt(value) => value * PX_PER_PT,\n            Self::Pc(value) => value * PX_PER_PC,\n        }\n    }\n\n    fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        Ok(Self::Px(op(self.to_px(), other.to_px())))\n    }\n\n    fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {\n        Self::Px(op(self.to_px()))\n    }\n}\n\nimpl ToComputedValue for AbsoluteLength {\n    type ComputedValue = CSSPixelLength;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        CSSPixelLength::new(self.to_px())\n            .zoom(context.builder.effective_zoom)\n            .finite()\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Self::Px(computed.px())\n    }\n}\n\nimpl PartialOrd for AbsoluteLength {\n    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {\n        self.to_px().partial_cmp(&other.to_px())\n    }\n}\n\n/// A container query length.\n///\n/// <https://drafts.csswg.org/css-contain-3/#container-lengths>\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToShmem)]\n#[repr(u8)]\npub enum ContainerRelativeLength {\n    /// 1% of query container's width\n    #[css(dimension)]\n    Cqw(CSSFloat),\n    /// 1% of query container's height\n    #[css(dimension)]\n    Cqh(CSSFloat),\n    /// 1% of query container's inline size\n    #[css(dimension)]\n    Cqi(CSSFloat),\n    /// 1% of query container's block size\n    #[css(dimension)]\n    Cqb(CSSFloat),\n    /// The smaller value of `cqi` or `cqb`\n    #[css(dimension)]\n    Cqmin(CSSFloat),\n    /// The larger value of `cqi` or `cqb`\n    #[css(dimension)]\n    Cqmax(CSSFloat),\n}\n\nimpl ContainerRelativeLength {\n    fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            Self::Cqw(v)\n            | Self::Cqh(v)\n            | Self::Cqi(v)\n            | Self::Cqb(v)\n            | Self::Cqmin(v)\n            | Self::Cqmax(v) => v,\n        }\n    }\n\n    // Return the unit, as a string.\n    fn unit(&self) -> &'static str {\n        match *self {\n            Self::Cqw(_) => \"cqw\",\n            Self::Cqh(_) => \"cqh\",\n            Self::Cqi(_) => \"cqi\",\n            Self::Cqb(_) => \"cqb\",\n            Self::Cqmin(_) => \"cqmin\",\n            Self::Cqmax(_) => \"cqmax\",\n        }\n    }\n\n    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::ContainerRelativeLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        Ok(match (self, other) {\n            (&Cqw(one), &Cqw(other)) => Cqw(op(one, other)),\n            (&Cqh(one), &Cqh(other)) => Cqh(op(one, other)),\n            (&Cqi(one), &Cqi(other)) => Cqi(op(one, other)),\n            (&Cqb(one), &Cqb(other)) => Cqb(op(one, other)),\n            (&Cqmin(one), &Cqmin(other)) => Cqmin(op(one, other)),\n            (&Cqmax(one), &Cqmax(other)) => Cqmax(op(one, other)),\n\n            // See https://github.com/rust-lang/rust/issues/68867, then\n            // https://github.com/rust-lang/rust/pull/95161. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_op()\")\n            },\n        })\n    }\n\n    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {\n        match self {\n            Self::Cqw(x) => Self::Cqw(op(*x)),\n            Self::Cqh(x) => Self::Cqh(op(*x)),\n            Self::Cqi(x) => Self::Cqi(op(*x)),\n            Self::Cqb(x) => Self::Cqb(op(*x)),\n            Self::Cqmin(x) => Self::Cqmin(op(*x)),\n            Self::Cqmax(x) => Self::Cqmax(op(*x)),\n        }\n    }\n\n    /// Computes the given container-relative length.\n    pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {\n        if context.for_non_inherited_property {\n            context.rule_cache_conditions.borrow_mut().set_uncacheable();\n        }\n        context\n            .builder\n            .add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);\n\n        // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like\n        // container_zoom - effective_zoom or so. See\n        // https://github.com/w3c/csswg-drafts/issues/10268\n        let size = context.get_container_size_query();\n        let (factor, container_length) = match *self {\n            Self::Cqw(v) => (v, size.get_container_width(context)),\n            Self::Cqh(v) => (v, size.get_container_height(context)),\n            Self::Cqi(v) => (v, size.get_container_inline_size(context)),\n            Self::Cqb(v) => (v, size.get_container_block_size(context)),\n            Self::Cqmin(v) => (\n                v,\n                cmp::min(\n                    size.get_container_inline_size(context),\n                    size.get_container_block_size(context),\n                ),\n            ),\n            Self::Cqmax(v) => (\n                v,\n                cmp::max(\n                    size.get_container_inline_size(context),\n                    size.get_container_block_size(context),\n                ),\n            ),\n        };\n        CSSPixelLength::new((container_length.to_f64_px() * factor as f64 / 100.0) as f32).finite()\n    }\n}\n\n/// A `<length>` without taking `calc` expressions into account\n///\n/// <https://drafts.csswg.org/css-values/#lengths>\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]\n#[repr(u8)]\npub enum NoCalcLength {\n    /// An absolute length\n    ///\n    /// <https://drafts.csswg.org/css-values/#absolute-length>\n    Absolute(AbsoluteLength),\n\n    /// A font-relative length:\n    ///\n    /// <https://drafts.csswg.org/css-values/#font-relative-lengths>\n    FontRelative(FontRelativeLength),\n\n    /// A viewport-relative length.\n    ///\n    /// <https://drafts.csswg.org/css-values/#viewport-relative-lengths>\n    ViewportPercentage(ViewportPercentageLength),\n\n    /// A container query length.\n    ///\n    /// <https://drafts.csswg.org/css-contain-3/#container-lengths>\n    ContainerRelative(ContainerRelativeLength),\n    /// HTML5 \"character width\", as defined in HTML5 § 14.5.4.\n    ///\n    /// This cannot be specified by the user directly and is only generated by\n    /// `Stylist::synthesize_rules_for_legacy_attributes()`.\n    ServoCharacterWidth(CharacterWidth),\n}\n\nimpl NoCalcLength {\n    /// Return the unitless, raw value.\n    pub fn unitless_value(&self) -> CSSFloat {\n        match *self {\n            Self::Absolute(v) => v.unitless_value(),\n            Self::FontRelative(v) => v.unitless_value(),\n            Self::ViewportPercentage(v) => v.unitless_value(),\n            Self::ContainerRelative(v) => v.unitless_value(),\n            Self::ServoCharacterWidth(c) => c.0 as f32,\n        }\n    }\n\n    /// Return the unit, as a string.\n    pub fn unit(&self) -> &'static str {\n        match *self {\n            Self::Absolute(v) => v.unit(),\n            Self::FontRelative(v) => v.unit(),\n            Self::ViewportPercentage(v) => v.unit(),\n            Self::ContainerRelative(v) => v.unit(),\n            Self::ServoCharacterWidth(_) => \"\",\n        }\n    }\n\n    /// Return the canonical unit for this value, if one exists.\n    pub fn canonical_unit(&self) -> Option<&'static str> {\n        match *self {\n            Self::Absolute(v) => v.canonical_unit(),\n            _ => None,\n        }\n    }\n\n    /// Convert this value to the specified unit, if possible.\n    pub fn to(&self, unit: &str) -> Result<Self, ()> {\n        match self {\n            Self::Absolute(v) => Ok(Self::Absolute(v.to(unit)?)),\n            _ => Err(()),\n        }\n    }\n\n    /// Returns whether the value of this length without unit is less than zero.\n    pub fn is_negative(&self) -> bool {\n        self.unitless_value().is_sign_negative()\n    }\n\n    /// Returns whether the value of this length without unit is equal to zero.\n    pub fn is_zero(&self) -> bool {\n        self.unitless_value() == 0.0\n    }\n\n    /// Returns whether the value of this length without unit is infinite.\n    pub fn is_infinite(&self) -> bool {\n        self.unitless_value().is_infinite()\n    }\n\n    /// Returns whether the value of this length without unit is NaN.\n    pub fn is_nan(&self) -> bool {\n        self.unitless_value().is_nan()\n    }\n\n    /// Whether text-only zoom should be applied to this length.\n    ///\n    /// Generally, font-dependent/relative units don't get text-only-zoomed,\n    /// because the font they're relative to should be zoomed already.\n    pub fn should_zoom_text(&self) -> bool {\n        match *self {\n            Self::Absolute(..) | Self::ViewportPercentage(..) | Self::ContainerRelative(..) => true,\n            Self::ServoCharacterWidth(..) | Self::FontRelative(..) => false,\n        }\n    }\n\n    /// Parse a given absolute or relative dimension.\n    pub fn parse_dimension_with_flags(\n        parsing_mode: ParsingMode,\n        in_page_rule: bool,\n        value: CSSFloat,\n        unit: &str,\n    ) -> Result<Self, ()> {\n        let allows_computational_dependence = parsing_mode.allows_computational_dependence();\n\n        Ok(match_ignore_ascii_case! { unit,\n            \"px\" => Self::Absolute(AbsoluteLength::Px(value)),\n            \"in\" => Self::Absolute(AbsoluteLength::In(value)),\n            \"cm\" => Self::Absolute(AbsoluteLength::Cm(value)),\n            \"mm\" => Self::Absolute(AbsoluteLength::Mm(value)),\n            \"q\" => Self::Absolute(AbsoluteLength::Q(value)),\n            \"pt\" => Self::Absolute(AbsoluteLength::Pt(value)),\n            \"pc\" => Self::Absolute(AbsoluteLength::Pc(value)),\n            // font-relative\n            \"em\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Em(value)),\n            \"ex\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ex(value)),\n            \"rex\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rex(value)),\n            \"ch\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ch(value)),\n            \"rch\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rch(value)),\n            \"cap\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Cap(value)),\n            \"rcap\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rcap(value)),\n            \"ic\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ic(value)),\n            \"ric\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Ric(value)),\n            \"rem\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rem(value)),\n            \"lh\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Lh(value)),\n            \"rlh\" if allows_computational_dependence => Self::FontRelative(FontRelativeLength::Rlh(value)),\n            // viewport percentages\n            \"vw\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vw(value))\n            },\n            \"svw\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svw(value))\n            },\n            \"lvw\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvw(value))\n            },\n            \"dvw\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvw(value))\n            },\n            \"vh\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vh(value))\n            },\n            \"svh\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svh(value))\n            },\n            \"lvh\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvh(value))\n            },\n            \"dvh\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvh(value))\n            },\n            \"vmin\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vmin(value))\n            },\n            \"svmin\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svmin(value))\n            },\n            \"lvmin\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvmin(value))\n            },\n            \"dvmin\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvmin(value))\n            },\n            \"vmax\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vmax(value))\n            },\n            \"svmax\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svmax(value))\n            },\n            \"lvmax\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvmax(value))\n            },\n            \"dvmax\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvmax(value))\n            },\n            \"vb\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vb(value))\n            },\n            \"svb\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svb(value))\n            },\n            \"lvb\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvb(value))\n            },\n            \"dvb\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvb(value))\n            },\n            \"vi\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Vi(value))\n            },\n            \"svi\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Svi(value))\n            },\n            \"lvi\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Lvi(value))\n            },\n            \"dvi\" if !in_page_rule => {\n                Self::ViewportPercentage(ViewportPercentageLength::Dvi(value))\n            },\n            // Container query lengths. Inherit the limitation from viewport units since\n            // we may fall back to them.\n            \"cqw\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqw(value))\n            },\n            \"cqh\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqh(value))\n            },\n            \"cqi\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqi(value))\n            },\n            \"cqb\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqb(value))\n            },\n            \"cqmin\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqmin(value))\n            },\n            \"cqmax\" if !in_page_rule && cfg!(feature = \"gecko\") => {\n                Self::ContainerRelative(ContainerRelativeLength::Cqmax(value))\n            },\n            _ => return Err(()),\n        })\n    }\n\n    /// Parse a given absolute or relative dimension.\n    pub fn parse_dimension_with_context(\n        context: &ParserContext,\n        value: CSSFloat,\n        unit: &str,\n    ) -> Result<Self, ()> {\n        Self::parse_dimension_with_flags(context.parsing_mode, context.in_page_rule(), value, unit)\n    }\n\n    pub(crate) fn try_op<O>(&self, other: &Self, op: O) -> Result<Self, ()>\n    where\n        O: Fn(f32, f32) -> f32,\n    {\n        use self::NoCalcLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return Err(());\n        }\n\n        Ok(match (self, other) {\n            (&Absolute(ref one), &Absolute(ref other)) => Absolute(one.try_op(other, op)?),\n            (&FontRelative(ref one), &FontRelative(ref other)) => {\n                FontRelative(one.try_op(other, op)?)\n            },\n            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {\n                ViewportPercentage(one.try_op(other, op)?)\n            },\n            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => {\n                ContainerRelative(one.try_op(other, op)?)\n            },\n            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {\n                ServoCharacterWidth(CharacterWidth(op(one.0 as f32, other.0 as f32) as i32))\n            },\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Absolute(..)\n                    | FontRelative(..)\n                    | ViewportPercentage(..)\n                    | ContainerRelative(..)\n                    | ServoCharacterWidth(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in try_op()\")\n            },\n        })\n    }\n\n    pub(crate) fn map(&self, mut op: impl FnMut(f32) -> f32) -> Self {\n        use self::NoCalcLength::*;\n\n        match self {\n            Absolute(ref one) => Absolute(one.map(op)),\n            FontRelative(ref one) => FontRelative(one.map(op)),\n            ViewportPercentage(ref one) => ViewportPercentage(one.map(op)),\n            ContainerRelative(ref one) => ContainerRelative(one.map(op)),\n            ServoCharacterWidth(ref one) => {\n                ServoCharacterWidth(CharacterWidth(op(one.0 as f32) as i32))\n            },\n        }\n    }\n\n    /// Get a px value without context (so only absolute units can be handled).\n    #[inline]\n    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {\n        match *self {\n            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),\n            _ => Err(()),\n        }\n    }\n\n    /// Get a px value without a full style context; this can handle either\n    /// absolute or (if a font metrics getter is provided) font-relative units.\n    #[cfg(feature = \"gecko\")]\n    #[inline]\n    pub fn to_computed_pixel_length_with_font_metrics(\n        &self,\n        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,\n    ) -> Result<CSSFloat, ()> {\n        match *self {\n            Self::Absolute(len) => Ok(CSSPixelLength::new(len.to_px()).finite().px()),\n            Self::FontRelative(fr) => {\n                if let Some(getter) = get_font_metrics {\n                    fr.to_computed_pixel_length_with_font_metrics(getter)\n                } else {\n                    Err(())\n                }\n            },\n            _ => Err(()),\n        }\n    }\n\n    /// Get an absolute length from a px value.\n    #[inline]\n    pub fn from_px(px_value: CSSFloat) -> NoCalcLength {\n        NoCalcLength::Absolute(AbsoluteLength::Px(px_value))\n    }\n}\n\nimpl ToCss for NoCalcLength {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        crate::values::serialize_specified_dimension(\n            self.unitless_value(),\n            self.unit(),\n            false,\n            dest,\n        )\n    }\n}\n\nimpl ToTyped for NoCalcLength {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        let value = self.unitless_value();\n        let unit = CssString::from(self.unit());\n        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {\n            value,\n            unit,\n        })));\n        Ok(())\n    }\n}\n\nimpl SpecifiedValueInfo for NoCalcLength {}\n\nimpl PartialOrd for NoCalcLength {\n    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {\n        use self::NoCalcLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        match (self, other) {\n            (&Absolute(ref one), &Absolute(ref other)) => one.to_px().partial_cmp(&other.to_px()),\n            (&FontRelative(ref one), &FontRelative(ref other)) => one.partial_cmp(other),\n            (&ViewportPercentage(ref one), &ViewportPercentage(ref other)) => {\n                one.partial_cmp(other)\n            },\n            (&ContainerRelative(ref one), &ContainerRelative(ref other)) => one.partial_cmp(other),\n            (&ServoCharacterWidth(ref one), &ServoCharacterWidth(ref other)) => {\n                one.0.partial_cmp(&other.0)\n            },\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Absolute(..)\n                    | FontRelative(..)\n                    | ViewportPercentage(..)\n                    | ContainerRelative(..)\n                    | ServoCharacterWidth(..) => {},\n                }\n                debug_unreachable!(\"Forgot an arm in partial_cmp?\")\n            },\n        }\n    }\n}\n\nimpl Zero for NoCalcLength {\n    fn zero() -> Self {\n        NoCalcLength::Absolute(AbsoluteLength::Px(0.))\n    }\n\n    fn is_zero(&self) -> bool {\n        NoCalcLength::is_zero(self)\n    }\n}\n\n/// An extension to `NoCalcLength` to parse `calc` expressions.\n/// This is commonly used for the `<length>` values.\n///\n/// <https://drafts.csswg.org/css-values/#lengths>\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub enum Length {\n    /// The internal length type that cannot parse `calc`\n    NoCalc(NoCalcLength),\n    /// A calc expression.\n    ///\n    /// <https://drafts.csswg.org/css-values/#calc-notation>\n    Calc(Box<CalcLengthPercentage>),\n}\n\nimpl From<NoCalcLength> for Length {\n    #[inline]\n    fn from(len: NoCalcLength) -> Self {\n        Length::NoCalc(len)\n    }\n}\n\nimpl PartialOrd for FontRelativeLength {\n    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {\n        use self::FontRelativeLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        match (self, other) {\n            (&Em(ref one), &Em(ref other)) => one.partial_cmp(other),\n            (&Ex(ref one), &Ex(ref other)) => one.partial_cmp(other),\n            (&Rex(ref one), &Rex(ref other)) => one.partial_cmp(other),\n            (&Ch(ref one), &Ch(ref other)) => one.partial_cmp(other),\n            (&Rch(ref one), &Rch(ref other)) => one.partial_cmp(other),\n            (&Cap(ref one), &Cap(ref other)) => one.partial_cmp(other),\n            (&Rcap(ref one), &Rcap(ref other)) => one.partial_cmp(other),\n            (&Ic(ref one), &Ic(ref other)) => one.partial_cmp(other),\n            (&Ric(ref one), &Ric(ref other)) => one.partial_cmp(other),\n            (&Rem(ref one), &Rem(ref other)) => one.partial_cmp(other),\n            (&Lh(ref one), &Lh(ref other)) => one.partial_cmp(other),\n            (&Rlh(ref one), &Rlh(ref other)) => one.partial_cmp(other),\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Em(..) | Ex(..) | Rex(..) | Ch(..) | Rch(..) | Cap(..) | Rcap(..) | Ic(..)\n                    | Ric(..) | Rem(..) | Lh(..) | Rlh(..) => {},\n                }\n                debug_unreachable!(\"Forgot an arm in partial_cmp?\")\n            },\n        }\n    }\n}\n\nimpl PartialOrd for ContainerRelativeLength {\n    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {\n        use self::ContainerRelativeLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        match (self, other) {\n            (&Cqw(ref one), &Cqw(ref other)) => one.partial_cmp(other),\n            (&Cqh(ref one), &Cqh(ref other)) => one.partial_cmp(other),\n            (&Cqi(ref one), &Cqi(ref other)) => one.partial_cmp(other),\n            (&Cqb(ref one), &Cqb(ref other)) => one.partial_cmp(other),\n            (&Cqmin(ref one), &Cqmin(ref other)) => one.partial_cmp(other),\n            (&Cqmax(ref one), &Cqmax(ref other)) => one.partial_cmp(other),\n\n            // See https://github.com/rust-lang/rust/issues/68867, then\n            // https://github.com/rust-lang/rust/pull/95161. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Cqw(..) | Cqh(..) | Cqi(..) | Cqb(..) | Cqmin(..) | Cqmax(..) => {},\n                }\n                debug_unreachable!(\"Forgot to handle unit in partial_cmp()\")\n            },\n        }\n    }\n}\n\nimpl PartialOrd for ViewportPercentageLength {\n    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {\n        use self::ViewportPercentageLength::*;\n\n        if std::mem::discriminant(self) != std::mem::discriminant(other) {\n            return None;\n        }\n\n        match (self, other) {\n            (&Vw(ref one), &Vw(ref other)) => one.partial_cmp(other),\n            (&Svw(ref one), &Svw(ref other)) => one.partial_cmp(other),\n            (&Lvw(ref one), &Lvw(ref other)) => one.partial_cmp(other),\n            (&Dvw(ref one), &Dvw(ref other)) => one.partial_cmp(other),\n            (&Vh(ref one), &Vh(ref other)) => one.partial_cmp(other),\n            (&Svh(ref one), &Svh(ref other)) => one.partial_cmp(other),\n            (&Lvh(ref one), &Lvh(ref other)) => one.partial_cmp(other),\n            (&Dvh(ref one), &Dvh(ref other)) => one.partial_cmp(other),\n            (&Vmin(ref one), &Vmin(ref other)) => one.partial_cmp(other),\n            (&Svmin(ref one), &Svmin(ref other)) => one.partial_cmp(other),\n            (&Lvmin(ref one), &Lvmin(ref other)) => one.partial_cmp(other),\n            (&Dvmin(ref one), &Dvmin(ref other)) => one.partial_cmp(other),\n            (&Vmax(ref one), &Vmax(ref other)) => one.partial_cmp(other),\n            (&Svmax(ref one), &Svmax(ref other)) => one.partial_cmp(other),\n            (&Lvmax(ref one), &Lvmax(ref other)) => one.partial_cmp(other),\n            (&Dvmax(ref one), &Dvmax(ref other)) => one.partial_cmp(other),\n            (&Vb(ref one), &Vb(ref other)) => one.partial_cmp(other),\n            (&Svb(ref one), &Svb(ref other)) => one.partial_cmp(other),\n            (&Lvb(ref one), &Lvb(ref other)) => one.partial_cmp(other),\n            (&Dvb(ref one), &Dvb(ref other)) => one.partial_cmp(other),\n            (&Vi(ref one), &Vi(ref other)) => one.partial_cmp(other),\n            (&Svi(ref one), &Svi(ref other)) => one.partial_cmp(other),\n            (&Lvi(ref one), &Lvi(ref other)) => one.partial_cmp(other),\n            (&Dvi(ref one), &Dvi(ref other)) => one.partial_cmp(other),\n            // See https://github.com/rust-lang/rust/issues/68867. rustc isn't\n            // able to figure it own on its own so we help.\n            _ => unsafe {\n                match *self {\n                    Vw(..) | Svw(..) | Lvw(..) | Dvw(..) | Vh(..) | Svh(..) | Lvh(..) | Dvh(..)\n                    | Vmin(..) | Svmin(..) | Lvmin(..) | Dvmin(..) | Vmax(..) | Svmax(..)\n                    | Lvmax(..) | Dvmax(..) | Vb(..) | Svb(..) | Lvb(..) | Dvb(..) | Vi(..)\n                    | Svi(..) | Lvi(..) | Dvi(..) => {},\n                }\n                debug_unreachable!(\"Forgot an arm in partial_cmp?\")\n            },\n        }\n    }\n}\n\nimpl Length {\n    #[inline]\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        num_context: AllowedNumericType,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let token = input.next()?;\n        match *token {\n            Token::Dimension {\n                value, ref unit, ..\n            } if num_context.is_ok(context.parsing_mode, value) => {\n                NoCalcLength::parse_dimension_with_context(context, value, unit)\n                    .map(Length::NoCalc)\n                    .map_err(|()| location.new_unexpected_token_error(token.clone()))\n            },\n            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {\n                if value != 0.\n                    && !context.parsing_mode.allows_unitless_lengths()\n                    && !allow_quirks.allowed(context.quirks_mode)\n                {\n                    return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(\n                    value,\n                ))))\n            },\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                let calc = CalcNode::parse_length(context, input, num_context, function)?;\n                Ok(Length::Calc(Box::new(calc)))\n            },\n            ref token => return Err(location.new_unexpected_token_error(token.clone())),\n        }\n    }\n\n    /// Parse a non-negative length\n    #[inline]\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)\n    }\n\n    /// Parse a non-negative length, allowing quirks.\n    #[inline]\n    pub fn parse_non_negative_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::NonNegative,\n            allow_quirks,\n        )\n    }\n\n    /// Get an absolute length from a px value.\n    #[inline]\n    pub fn from_px(px_value: CSSFloat) -> Length {\n        Length::NoCalc(NoCalcLength::from_px(px_value))\n    }\n\n    /// Get a px value without context.\n    pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {\n        match *self {\n            Self::NoCalc(ref l) => l.to_computed_pixel_length_without_context(),\n            Self::Calc(ref l) => l.to_computed_pixel_length_without_context(),\n        }\n    }\n\n    /// Get a px value, with an optional GeckoFontMetrics getter to resolve font-relative units.\n    #[cfg(feature = \"gecko\")]\n    pub fn to_computed_pixel_length_with_font_metrics(\n        &self,\n        get_font_metrics: Option<impl Fn() -> GeckoFontMetrics>,\n    ) -> Result<CSSFloat, ()> {\n        match *self {\n            Self::NoCalc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),\n            Self::Calc(ref l) => l.to_computed_pixel_length_with_font_metrics(get_font_metrics),\n        }\n    }\n}\n\nimpl Parse for Length {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl Zero for Length {\n    fn zero() -> Self {\n        Length::NoCalc(NoCalcLength::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        // FIXME(emilio): Seems a bit weird to treat calc() unconditionally as\n        // non-zero here?\n        match *self {\n            Length::NoCalc(ref l) => l.is_zero(),\n            Length::Calc(..) => false,\n        }\n    }\n}\n\nimpl Length {\n    /// Parses a length, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, AllowedNumericType::All, allow_quirks)\n    }\n}\n\n/// A wrapper of Length, whose value must be >= 0.\npub type NonNegativeLength = NonNegative<Length>;\n\nimpl Parse for NonNegativeLength {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(NonNegative(Length::parse_non_negative(context, input)?))\n    }\n}\n\nimpl From<NoCalcLength> for NonNegativeLength {\n    #[inline]\n    fn from(len: NoCalcLength) -> Self {\n        NonNegative(Length::NoCalc(len))\n    }\n}\n\nimpl From<Length> for NonNegativeLength {\n    #[inline]\n    fn from(len: Length) -> Self {\n        NonNegative(len)\n    }\n}\n\nimpl NonNegativeLength {\n    /// Get an absolute length from a px value.\n    #[inline]\n    pub fn from_px(px_value: CSSFloat) -> Self {\n        Length::from_px(px_value.max(0.)).into()\n    }\n\n    /// Parses a non-negative length, optionally with quirks.\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(NonNegative(Length::parse_non_negative_quirky(\n            context,\n            input,\n            allow_quirks,\n        )?))\n    }\n}\n\n/// A `<length-percentage>` value. This can be either a `<length>`, a\n/// `<percentage>`, or a combination of both via `calc()`.\n///\n/// https://drafts.csswg.org/css-values-4/#typedef-length-percentage\n#[allow(missing_docs)]\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub enum LengthPercentage {\n    Length(NoCalcLength),\n    Percentage(computed::Percentage),\n    Calc(Box<CalcLengthPercentage>),\n}\n\nimpl From<Length> for LengthPercentage {\n    fn from(len: Length) -> LengthPercentage {\n        match len {\n            Length::NoCalc(l) => LengthPercentage::Length(l),\n            Length::Calc(l) => LengthPercentage::Calc(l),\n        }\n    }\n}\n\nimpl From<NoCalcLength> for LengthPercentage {\n    #[inline]\n    fn from(len: NoCalcLength) -> Self {\n        LengthPercentage::Length(len)\n    }\n}\n\nimpl From<Percentage> for LengthPercentage {\n    #[inline]\n    fn from(pc: Percentage) -> Self {\n        if let Some(clamping_mode) = pc.calc_clamping_mode() {\n            LengthPercentage::Calc(Box::new(CalcLengthPercentage {\n                clamping_mode,\n                node: CalcNode::Leaf(calc::Leaf::Percentage(pc.get())),\n            }))\n        } else {\n            LengthPercentage::Percentage(computed::Percentage(pc.get()))\n        }\n    }\n}\n\nimpl From<computed::Percentage> for LengthPercentage {\n    #[inline]\n    fn from(pc: computed::Percentage) -> Self {\n        LengthPercentage::Percentage(pc)\n    }\n}\n\nimpl Parse for LengthPercentage {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl LengthPercentage {\n    #[inline]\n    /// Returns a `0%` value.\n    pub fn zero_percent() -> LengthPercentage {\n        LengthPercentage::Percentage(computed::Percentage::zero())\n    }\n\n    #[inline]\n    /// Returns a `100%` value.\n    pub fn hundred_percent() -> LengthPercentage {\n        LengthPercentage::Percentage(computed::Percentage::hundred())\n    }\n\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        num_context: AllowedNumericType,\n        allow_quirks: AllowQuirks,\n        allow_anchor: AllowAnchorPositioningFunctions,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let token = input.next()?;\n        match *token {\n            Token::Dimension {\n                value, ref unit, ..\n            } if num_context.is_ok(context.parsing_mode, value) => {\n                return NoCalcLength::parse_dimension_with_context(context, value, unit)\n                    .map(LengthPercentage::Length)\n                    .map_err(|()| location.new_unexpected_token_error(token.clone()));\n            },\n            Token::Percentage { unit_value, .. }\n                if num_context.is_ok(context.parsing_mode, unit_value) =>\n            {\n                return Ok(LengthPercentage::Percentage(computed::Percentage(\n                    unit_value,\n                )));\n            },\n            Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {\n                if value != 0.\n                    && !context.parsing_mode.allows_unitless_lengths()\n                    && !allow_quirks.allowed(context.quirks_mode)\n                {\n                    return Err(location.new_unexpected_token_error(token.clone()));\n                } else {\n                    return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));\n                }\n            },\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                let calc = CalcNode::parse_length_or_percentage(\n                    context,\n                    input,\n                    num_context,\n                    function,\n                    allow_anchor,\n                )?;\n                Ok(LengthPercentage::Calc(Box::new(calc)))\n            },\n            _ => return Err(location.new_unexpected_token_error(token.clone())),\n        }\n    }\n\n    /// Parses allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::All,\n            allow_quirks,\n            AllowAnchorPositioningFunctions::No,\n        )\n    }\n\n    /// Parses allowing the unitless length quirk, as well as allowing\n    /// anchor-positioning related function, `anchor-size()`.\n    #[inline]\n    fn parse_quirky_with_anchor_size_function<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::All,\n            allow_quirks,\n            AllowAnchorPositioningFunctions::AllowAnchorSize,\n        )\n    }\n\n    /// Parses allowing the unitless length quirk, as well as allowing\n    /// anchor-positioning related functions, `anchor()` and `anchor-size()`.\n    #[inline]\n    pub fn parse_quirky_with_anchor_functions<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::All,\n            allow_quirks,\n            AllowAnchorPositioningFunctions::AllowAnchorAndAnchorSize,\n        )\n    }\n\n    /// Parses non-negative length, allowing the unitless length quirk,\n    /// as well as allowing `anchor-size()`.\n    pub fn parse_non_negative_with_anchor_size<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::NonNegative,\n            allow_quirks,\n            AllowAnchorPositioningFunctions::AllowAnchorSize,\n        )\n    }\n\n    /// Parse a non-negative length.\n    ///\n    /// FIXME(emilio): This should be not public and we should use\n    /// NonNegativeLengthPercentage instead.\n    #[inline]\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_non_negative_quirky(context, input, AllowQuirks::No)\n    }\n\n    /// Parse a non-negative length, with quirks.\n    #[inline]\n    pub fn parse_non_negative_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(\n            context,\n            input,\n            AllowedNumericType::NonNegative,\n            allow_quirks,\n            AllowAnchorPositioningFunctions::No,\n        )\n    }\n}\n\nimpl Zero for LengthPercentage {\n    fn zero() -> Self {\n        LengthPercentage::Length(NoCalcLength::zero())\n    }\n\n    fn is_zero(&self) -> bool {\n        match *self {\n            LengthPercentage::Length(l) => l.is_zero(),\n            LengthPercentage::Percentage(p) => p.0 == 0.0,\n            LengthPercentage::Calc(_) => false,\n        }\n    }\n}\n\nimpl ZeroNoPercent for LengthPercentage {\n    fn is_zero_no_percent(&self) -> bool {\n        match *self {\n            LengthPercentage::Percentage(_) => false,\n            _ => self.is_zero(),\n        }\n    }\n}\n\n/// Check if this equal to a specific percentage.\npub trait EqualsPercentage {\n    /// Returns true if this is a specific percentage value. This should exclude calc() even if it\n    /// only contains percentage component.\n    fn equals_percentage(&self, v: CSSFloat) -> bool;\n}\n\nimpl EqualsPercentage for LengthPercentage {\n    fn equals_percentage(&self, v: CSSFloat) -> bool {\n        match *self {\n            LengthPercentage::Percentage(p) => p.0 == v,\n            _ => false,\n        }\n    }\n}\n\n/// A specified type for `<length-percentage> | auto`.\npub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;\n\nimpl LengthPercentageOrAuto {\n    /// Returns a value representing `0%`.\n    #[inline]\n    pub fn zero_percent() -> Self {\n        generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero_percent())\n    }\n\n    /// Parses a length or a percentage, allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with(context, input, |context, input| {\n            LengthPercentage::parse_quirky(context, input, allow_quirks)\n        })\n    }\n}\n\n/// A wrapper of LengthPercentageOrAuto, whose value must be >= 0.\npub type NonNegativeLengthPercentageOrAuto =\n    generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;\n\nimpl NonNegativeLengthPercentageOrAuto {\n    /// Returns a value representing `0%`.\n    #[inline]\n    pub fn zero_percent() -> Self {\n        generics::LengthPercentageOrAuto::LengthPercentage(\n            NonNegativeLengthPercentage::zero_percent(),\n        )\n    }\n\n    /// Parses a non-negative length-percentage, allowing the unitless length\n    /// quirk.\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with(context, input, |context, input| {\n            NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)\n        })\n    }\n}\n\n/// A wrapper of LengthPercentage, whose value must be >= 0.\npub type NonNegativeLengthPercentage = NonNegative<LengthPercentage>;\n\n/// Either a NonNegativeLengthPercentage or the `normal` keyword.\npub type NonNegativeLengthPercentageOrNormal =\n    GenericLengthPercentageOrNormal<NonNegativeLengthPercentage>;\n\nimpl From<NoCalcLength> for NonNegativeLengthPercentage {\n    #[inline]\n    fn from(len: NoCalcLength) -> Self {\n        NonNegative(LengthPercentage::from(len))\n    }\n}\n\nimpl Parse for NonNegativeLengthPercentage {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl NonNegativeLengthPercentage {\n    #[inline]\n    /// Returns a `0%` value.\n    pub fn zero_percent() -> Self {\n        NonNegative(LengthPercentage::zero_percent())\n    }\n\n    /// Parses a length or a percentage, allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        LengthPercentage::parse_non_negative_quirky(context, input, allow_quirks).map(NonNegative)\n    }\n\n    /// Parses a length or a percentage, allowing the unitless length quirk,\n    /// as well as allowing `anchor-size()`.\n    #[inline]\n    pub fn parse_non_negative_with_anchor_size<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        LengthPercentage::parse_non_negative_with_anchor_size(context, input, allow_quirks)\n            .map(NonNegative)\n    }\n}\n\n/// Either a `<length>` or the `auto` keyword.\n///\n/// Note that we use LengthPercentage just for convenience, since it pretty much\n/// is everything we care about, but we could just add a similar LengthOrAuto\n/// instead if we think getting rid of this weirdness is worth it.\npub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;\n\nimpl LengthOrAuto {\n    /// Parses a length, allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with(context, input, |context, input| {\n            Length::parse_quirky(context, input, allow_quirks)\n        })\n    }\n}\n\n/// Either a non-negative `<length>` or the `auto` keyword.\npub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;\n\n/// Either a `<length>` or a `<number>`.\npub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;\n\n/// A specified value for `min-width`, `min-height`, `width` or `height` property.\npub type Size = GenericSize<NonNegativeLengthPercentage>;\n\nimpl Parse for Size {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Size::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nmacro_rules! parse_size_non_length {\n    ($size:ident, $input:expr, $allow_webkit_fill_available:expr,\n     $auto_or_none:expr => $auto_or_none_ident:ident) => {{\n        let size = $input.try_parse(|input| {\n            Ok(try_match_ident_ignore_ascii_case! { input,\n                \"min-content\" | \"-moz-min-content\" => $size::MinContent,\n                \"max-content\" | \"-moz-max-content\" => $size::MaxContent,\n                \"fit-content\" | \"-moz-fit-content\" => $size::FitContent,\n                #[cfg(feature = \"gecko\")]\n                \"-moz-available\" => $size::MozAvailable,\n                \"-webkit-fill-available\" if $allow_webkit_fill_available => $size::WebkitFillAvailable,\n                \"stretch\" if is_stretch_enabled() => $size::Stretch,\n                $auto_or_none => $size::$auto_or_none_ident,\n            })\n        });\n        if size.is_ok() {\n            return size;\n        }\n    }};\n}\n\nfn is_webkit_fill_available_enabled_in_width_and_height() -> bool {\n    static_prefs::pref!(\"layout.css.webkit-fill-available.enabled\")\n}\n\nfn is_webkit_fill_available_enabled_in_all_size_properties() -> bool {\n    // For convenience at the callsites, we check both prefs here,\n    // since both must be 'true' in order for the keyword to be\n    // enabled in all size properties.\n    static_prefs::pref!(\"layout.css.webkit-fill-available.enabled\")\n        && static_prefs::pref!(\"layout.css.webkit-fill-available.all-size-properties.enabled\")\n}\n\nfn is_stretch_enabled() -> bool {\n    static_prefs::pref!(\"layout.css.stretch-size-keyword.enabled\")\n}\n\nfn is_fit_content_function_enabled() -> bool {\n    static_prefs::pref!(\"layout.css.fit-content-function.enabled\")\n}\n\nmacro_rules! parse_fit_content_function {\n    ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => {\n        if is_fit_content_function_enabled() {\n            if let Ok(length) = $input.try_parse(|input| {\n                input.expect_function_matching(\"fit-content\")?;\n                input.parse_nested_block(|i| {\n                    NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks)\n                })\n            }) {\n                return Ok($size::FitContentFunction(length));\n            }\n        }\n    };\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum ParseAnchorFunctions {\n    Yes,\n    No,\n}\n\nimpl Size {\n    /// Parses, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();\n        Self::parse_quirky_internal(\n            context,\n            input,\n            allow_quirks,\n            allow_webkit_fill_available,\n            ParseAnchorFunctions::Yes,\n        )\n    }\n\n    /// Parses for flex-basis: <width>\n    pub fn parse_size_for_flex_basis_width<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky_internal(\n            context,\n            input,\n            AllowQuirks::No,\n            true,\n            ParseAnchorFunctions::No,\n        )\n    }\n\n    /// Parses, with quirks and configurable support for\n    /// whether the '-webkit-fill-available' keyword is allowed.\n    /// TODO(dholbert) Fold this function into callsites in bug 1989073 when\n    /// removing 'layout.css.webkit-fill-available.all-size-properties.enabled'.\n    fn parse_quirky_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n        allow_webkit_fill_available: bool,\n        allow_anchor_functions: ParseAnchorFunctions,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_size_non_length!(Size, input, allow_webkit_fill_available,\n                               \"auto\" => Auto);\n        parse_fit_content_function!(Size, input, context, allow_quirks);\n\n        let allow_anchor = allow_anchor_functions == ParseAnchorFunctions::Yes\n            && static_prefs::pref!(\"layout.css.anchor-positioning.enabled\");\n        match input\n            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))\n        {\n            Ok(length) => return Ok(GenericSize::LengthPercentage(length)),\n            Err(e) if !allow_anchor => return Err(e.into()),\n            Err(_) => (),\n        };\n        if let Ok(length) = input.try_parse(|i| {\n            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(\n                context,\n                i,\n                allow_quirks,\n            )\n        }) {\n            return Ok(GenericSize::AnchorContainingCalcFunction(length));\n        }\n        Ok(Self::AnchorSizeFunction(Box::new(\n            GenericAnchorSizeFunction::parse(context, input)?,\n        )))\n    }\n\n    /// Parse a size for width or height, where -webkit-fill-available\n    /// support is only controlled by one pref (vs. other properties where\n    /// there's an additional pref check):\n    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with\n    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.\n    pub fn parse_size_for_width_or_height_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();\n        Self::parse_quirky_internal(\n            context,\n            input,\n            allow_quirks,\n            allow_webkit_fill_available,\n            ParseAnchorFunctions::Yes,\n        )\n    }\n\n    /// Parse a size for width or height, where -webkit-fill-available\n    /// support is only controlled by one pref (vs. other properties where\n    /// there's an additional pref check):\n    /// TODO(dholbert) Remove this custom parse func in bug 1989073, along with\n    /// 'layout.css.webkit-fill-available.all-size-properties.enabled'.\n    pub fn parse_size_for_width_or_height<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_width_and_height();\n        Self::parse_quirky_internal(\n            context,\n            input,\n            AllowQuirks::No,\n            allow_webkit_fill_available,\n            ParseAnchorFunctions::Yes,\n        )\n    }\n\n    /// Returns `0%`.\n    #[inline]\n    pub fn zero_percent() -> Self {\n        GenericSize::LengthPercentage(NonNegativeLengthPercentage::zero_percent())\n    }\n}\n\n/// A specified value for `max-width` or `max-height` property.\npub type MaxSize = GenericMaxSize<NonNegativeLengthPercentage>;\n\nimpl Parse for MaxSize {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        MaxSize::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl MaxSize {\n    /// Parses, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        let allow_webkit_fill_available = is_webkit_fill_available_enabled_in_all_size_properties();\n        parse_size_non_length!(MaxSize, input, allow_webkit_fill_available,\n                               \"none\" => None);\n        parse_fit_content_function!(MaxSize, input, context, allow_quirks);\n\n        match input\n            .try_parse(|i| NonNegativeLengthPercentage::parse_quirky(context, i, allow_quirks))\n        {\n            Ok(length) => return Ok(GenericMaxSize::LengthPercentage(length)),\n            Err(e) if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") => {\n                return Err(e.into())\n            },\n            Err(_) => (),\n        };\n        if let Ok(length) = input.try_parse(|i| {\n            NonNegativeLengthPercentage::parse_non_negative_with_anchor_size(\n                context,\n                i,\n                allow_quirks,\n            )\n        }) {\n            return Ok(GenericMaxSize::AnchorContainingCalcFunction(length));\n        }\n        Ok(Self::AnchorSizeFunction(Box::new(\n            GenericAnchorSizeFunction::parse(context, input)?,\n        )))\n    }\n}\n\n/// A specified non-negative `<length>` | `<number>`.\npub type NonNegativeLengthOrNumber = GenericLengthOrNumber<NonNegativeLength, NonNegativeNumber>;\n\n/// A specified value for `margin` properties.\npub type Margin = GenericMargin<LengthPercentage>;\n\nimpl Margin {\n    /// Parses a margin type, allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n        {\n            return Ok(Self::LengthPercentage(l));\n        }\n        match input.try_parse(|i| i.expect_ident_matching(\"auto\")) {\n            Ok(_) => return Ok(Self::Auto),\n            Err(e) if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") => {\n                return Err(e.into())\n            },\n            Err(_) => (),\n        };\n        if let Ok(l) = input.try_parse(|i| {\n            LengthPercentage::parse_quirky_with_anchor_size_function(context, i, allow_quirks)\n        }) {\n            return Ok(Self::AnchorContainingCalcFunction(l));\n        }\n        let inner = GenericAnchorSizeFunction::<Margin>::parse(context, input)?;\n        Ok(Self::AnchorSizeFunction(Box::new(inner)))\n    }\n}\n\nimpl Parse for Margin {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! `list` specified values.\n\nuse crate::counter_style::{CounterStyle, CounterStyleParsingFlags};\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse cssparser::{Parser, Token};\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// Specified and computed `list-style-type` property.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct ListStyleType(pub CounterStyle);\n\nimpl ListStyleType {\n    /// Initial specified value for `list-style-type`.\n    #[inline]\n    pub fn disc() -> Self {\n        Self(CounterStyle::disc())\n    }\n\n    /// none value.\n    #[inline]\n    pub fn none() -> Self {\n        Self(CounterStyle::None)\n    }\n\n    /// Convert from gecko keyword to list-style-type.\n    ///\n    /// This should only be used for mapping type attribute to list-style-type, and thus only\n    /// values possible in that attribute is considered here.\n    #[cfg(feature = \"gecko\")]\n    pub fn from_gecko_keyword(value: u32) -> Self {\n        use crate::gecko_bindings::structs;\n        use crate::values::CustomIdent;\n        let v8 = value as u8;\n        if v8 == structs::ListStyle_None {\n            return Self::none();\n        }\n\n        Self(CounterStyle::Name(CustomIdent(match v8 {\n            structs::ListStyle_Disc => atom!(\"disc\"),\n            structs::ListStyle_Circle => atom!(\"circle\"),\n            structs::ListStyle_Square => atom!(\"square\"),\n            structs::ListStyle_Decimal => atom!(\"decimal\"),\n            structs::ListStyle_LowerRoman => atom!(\"lower-roman\"),\n            structs::ListStyle_UpperRoman => atom!(\"upper-roman\"),\n            structs::ListStyle_LowerAlpha => atom!(\"lower-alpha\"),\n            structs::ListStyle_UpperAlpha => atom!(\"upper-alpha\"),\n            _ => unreachable!(\"Unknown counter style keyword value\"),\n        })))\n    }\n\n    /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`)\n    #[inline]\n    pub fn is_bullet(&self) -> bool {\n        self.0.is_bullet()\n    }\n}\n\nimpl Parse for ListStyleType {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let flags = CounterStyleParsingFlags::ALLOW_NONE | CounterStyleParsingFlags::ALLOW_STRING;\n        Ok(Self(CounterStyle::parse(context, input, flags)?))\n    }\n}\n\n/// A quote pair.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct QuotePair {\n    /// The opening quote.\n    pub opening: crate::OwnedStr,\n\n    /// The closing quote.\n    pub closing: crate::OwnedStr,\n}\n\n/// List of quote pairs for the specified/computed value of `quotes` property.\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct QuoteList(\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub crate::ArcSlice<QuotePair>,\n);\n\n/// Specified and computed `quotes` property: `auto`, `none`, or a list\n/// of characters.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\npub enum Quotes {\n    /// list of quote pairs\n    QuoteList(QuoteList),\n    /// auto (use lang-dependent quote marks)\n    Auto,\n}\n\nimpl Parse for Quotes {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Quotes, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"auto\"))\n            .is_ok()\n        {\n            return Ok(Quotes::Auto);\n        }\n\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(Quotes::QuoteList(QuoteList::default()));\n        }\n\n        let mut quotes = Vec::new();\n        loop {\n            let location = input.current_source_location();\n            let opening = match input.next() {\n                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned().into(),\n                Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),\n                Err(_) => break,\n            };\n\n            let closing = input.expect_string()?.as_ref().to_owned().into();\n            quotes.push(QuotePair { opening, closing });\n        }\n\n        if !quotes.is_empty() {\n            Ok(Quotes::QuoteList(QuoteList(crate::ArcSlice::from_iter(\n                quotes.into_iter(),\n            ))))\n        } else {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/mod.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified values.\n//!\n//! TODO(emilio): Enhance docs.\n\nuse super::computed::transform::DirectionVector;\nuse super::computed::{Context, ToComputedValue};\nuse super::generics::grid::ImplicitGridTracks as GenericImplicitGridTracks;\nuse super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};\nuse super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};\nuse super::generics::transform::IsParallelTo;\nuse super::generics::{self, GreaterThanOrEqualToOne, NonNegative};\nuse super::{CSSFloat, CSSInteger};\nuse crate::context::QuirksMode;\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::specified::calc::CalcNode;\nuse crate::values::{reify_number, serialize_atom_identifier, serialize_number, AtomString};\nuse crate::{Atom, Namespace, One, Prefix, Zero};\nuse cssparser::{Parser, Token};\nuse rustc_hash::FxHashMap;\nuse std::fmt::{self, Write};\nuse std::ops::Add;\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{\n    CssWriter, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss, ToTyped, TypedValue,\n};\nuse thin_vec::ThinVec;\n\npub use self::align::{ContentDistribution, ItemPlacement, JustifyItems, SelfAlignment};\npub use self::angle::{AllowUnitlessZeroAngle, Angle};\npub use self::animation::{\n    AnimationComposition, AnimationDirection, AnimationDuration, AnimationFillMode,\n    AnimationIterationCount, AnimationName, AnimationPlayState, AnimationRangeEnd,\n    AnimationRangeStart, AnimationTimeline, ScrollAxis, TimelineName, TransitionBehavior,\n    TransitionProperty, ViewTimelineInset, ViewTransitionClass, ViewTransitionName,\n};\npub use self::background::{BackgroundRepeat, BackgroundSize};\npub use self::basic_shape::FillRule;\npub use self::border::{\n    BorderCornerRadius, BorderImageRepeat, BorderImageSideWidth, BorderImageSlice,\n    BorderImageWidth, BorderRadius, BorderSideOffset, BorderSideWidth, BorderSpacing, BorderStyle,\n    LineWidth,\n};\npub use self::box_::{\n    AlignmentBaseline, Appearance, BaselineShift, BaselineSource, BreakBetween, BreakWithin, Clear,\n    Contain, ContainIntrinsicSize, ContainerName, ContainerType, ContentVisibility, Display,\n    DominantBaseline, Float, LineClamp, Overflow, OverflowAnchor, OverflowClipMargin,\n    OverscrollBehavior, Perspective, PositionProperty, Resize, ScrollSnapAlign, ScrollSnapAxis,\n    ScrollSnapStop, ScrollSnapStrictness, ScrollSnapType, ScrollbarGutter, TouchAction, WillChange,\n    WillChangeBits, WritingModeProperty, Zoom,\n};\npub use self::color::{\n    Color, ColorOrAuto, ColorPropertyValue, ColorScheme, ForcedColorAdjust, PrintColorAdjust,\n};\npub use self::column::ColumnCount;\npub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset, CounterSet};\npub use self::easing::TimingFunction;\npub use self::effects::{BoxShadow, Filter, SimpleShadow};\npub use self::flex::FlexBasis;\npub use self::font::{FontFamily, FontLanguageOverride, FontPalette, FontStyle};\npub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric};\npub use self::font::{\n    FontSize, FontSizeAdjust, FontSizeAdjustFactor, FontSizeKeyword, FontStretch, FontSynthesis,\n    FontSynthesisStyle,\n};\npub use self::font::{FontVariantAlternates, FontWeight};\npub use self::font::{FontVariantEastAsian, FontVariationSettings, LineHeight};\npub use self::font::{MathDepth, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextScale};\npub use self::image::{EndingShape as GradientEndingShape, Gradient, Image, ImageRendering};\npub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};\npub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};\npub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};\npub use self::length::{Margin, MaxSize, Size};\npub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant};\npub use self::length::{\n    NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto,\n};\npub use self::list::ListStyleType;\npub use self::list::Quotes;\npub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate};\npub use self::outline::OutlineStyle;\npub use self::page::{PageName, PageOrientation, PageSize, PageSizeOrientation, PaperSize};\npub use self::percentage::{NonNegativePercentage, Percentage};\npub use self::position::{\n    AnchorFunction, AnchorName, AnchorNameIdent, AspectRatio, GridAutoFlow, GridTemplateAreas,\n    Inset, MasonryAutoFlow, MasonryItemOrder, MasonryPlacement, Position, PositionAnchor,\n    PositionAnchorKeyword, PositionArea, PositionAreaKeyword, PositionComponent, PositionOrAuto,\n    PositionTryFallbacks, PositionTryOrder, PositionVisibility, ScopedName, ZIndex,\n};\npub use self::ratio::Ratio;\npub use self::rect::NonNegativeLengthOrNumberRect;\npub use self::resolution::Resolution;\npub use self::svg::{DProperty, MozContextProperties};\npub use self::svg::{SVGLength, SVGOpacity, SVGPaint};\npub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};\npub use self::svg_path::SVGPathData;\npub use self::text::RubyPosition;\npub use self::text::{HyphenateCharacter, HyphenateLimitChars};\npub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextAlign, TextIndent};\npub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};\npub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};\npub use self::text::{TextAlignLast, TextAutospace, TextUnderlinePosition};\npub use self::text::{TextBoxEdge, TextBoxTrim};\npub use self::text::{\n    TextDecorationInset, TextDecorationLength, TextDecorationSkipInk, TextJustify, TextTransform,\n};\npub use self::time::Time;\npub use self::transform::{Rotate, Scale, Transform};\npub use self::transform::{TransformBox, TransformOrigin, TransformStyle, Translate};\n#[cfg(feature = \"gecko\")]\npub use self::ui::CursorImage;\npub use self::ui::{\n    BoolInteger, Cursor, Inert, MozTheme, PointerEvents, ScrollbarColor, UserFocus, UserSelect,\n};\npub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;\n\npub mod align;\npub mod angle;\npub mod animation;\npub mod background;\npub mod basic_shape;\npub mod border;\n#[path = \"box.rs\"]\npub mod box_;\npub mod calc;\npub mod color;\npub mod column;\npub mod counters;\npub mod easing;\npub mod effects;\npub mod flex;\npub mod font;\npub mod grid;\npub mod image;\npub mod intersection_observer;\npub mod length;\npub mod list;\npub mod motion;\npub mod outline;\npub mod page;\npub mod percentage;\npub mod position;\npub mod ratio;\npub mod rect;\npub mod resolution;\npub mod source_size_list;\npub mod svg;\npub mod svg_path;\npub mod table;\npub mod text;\npub mod time;\npub mod transform;\npub mod ui;\npub mod url;\n\n/// <angle> | <percentage>\n/// https://drafts.csswg.org/css-values/#typedef-angle-percentage\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\npub enum AngleOrPercentage {\n    Percentage(Percentage),\n    Angle(Angle),\n}\n\nimpl AngleOrPercentage {\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_unitless_zero: AllowUnitlessZeroAngle,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(per) = input.try_parse(|i| Percentage::parse(context, i)) {\n            return Ok(AngleOrPercentage::Percentage(per));\n        }\n\n        Angle::parse_internal(context, input, allow_unitless_zero).map(AngleOrPercentage::Angle)\n    }\n\n    /// Allow unitless angles, used for conic-gradients as specified by the spec.\n    /// https://drafts.csswg.org/css-images-4/#valdef-conic-gradient-angle\n    pub fn parse_with_unitless<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::Yes)\n    }\n}\n\nimpl Parse for AngleOrPercentage {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        AngleOrPercentage::parse_internal(context, input, AllowUnitlessZeroAngle::No)\n    }\n}\n\n/// Parse a `<number>` value, with a given clamping mode.\nfn parse_number_with_clamping_mode<'i, 't>(\n    context: &ParserContext,\n    input: &mut Parser<'i, 't>,\n    clamping_mode: AllowedNumericType,\n) -> Result<Number, ParseError<'i>> {\n    let location = input.current_source_location();\n    match *input.next()? {\n        Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {\n            Ok(Number {\n                value,\n                calc_clamping_mode: None,\n            })\n        },\n        Token::Function(ref name) => {\n            let function = CalcNode::math_function(context, name, location)?;\n            let value = CalcNode::parse_number(context, input, function)?;\n            Ok(Number {\n                value,\n                calc_clamping_mode: Some(clamping_mode),\n            })\n        },\n        ref t => Err(location.new_unexpected_token_error(t.clone())),\n    }\n}\n\n/// A CSS `<number>` specified value.\n///\n/// https://drafts.csswg.org/css-values-3/#number-value\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, ToShmem)]\npub struct Number {\n    /// The numeric value itself.\n    value: CSSFloat,\n    /// If this number came from a calc() expression, this tells how clamping\n    /// should be done on the value.\n    calc_clamping_mode: Option<AllowedNumericType>,\n}\n\nimpl Parse for Number {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_number_with_clamping_mode(context, input, AllowedNumericType::All)\n    }\n}\n\nimpl PartialEq<Number> for Number {\n    fn eq(&self, other: &Number) -> bool {\n        if self.calc_clamping_mode != other.calc_clamping_mode {\n            return false;\n        }\n\n        self.value == other.value || (self.value.is_nan() && other.value.is_nan())\n    }\n}\n\nimpl Number {\n    /// Returns a new number with the value `val`.\n    #[inline]\n    fn new_with_clamping_mode(\n        value: CSSFloat,\n        calc_clamping_mode: Option<AllowedNumericType>,\n    ) -> Self {\n        Self {\n            value,\n            calc_clamping_mode,\n        }\n    }\n\n    /// Returns this percentage as a number.\n    pub fn to_percentage(&self) -> Percentage {\n        Percentage::new_with_clamping_mode(self.value, self.calc_clamping_mode)\n    }\n\n    /// Returns a new number with the value `val`.\n    #[inline]\n    pub fn new(val: CSSFloat) -> Self {\n        Self::new_with_clamping_mode(val, None)\n    }\n\n    /// Returns whether this number came from a `calc()` expression.\n    #[inline]\n    pub fn was_calc(&self) -> bool {\n        self.calc_clamping_mode.is_some()\n    }\n\n    /// Returns the numeric value, clamped if needed.\n    #[inline]\n    pub fn get(&self) -> f32 {\n        crate::values::normalize(\n            self.calc_clamping_mode\n                .map_or(self.value, |mode| mode.clamp(self.value)),\n        )\n        .min(f32::MAX)\n        .max(f32::MIN)\n    }\n\n    /// Return the unit, as a string.\n    pub fn unit(&self) -> &'static str {\n        \"number\"\n    }\n\n    /// Return no canonical unit (number values do not have one).\n    pub fn canonical_unit(&self) -> Option<&'static str> {\n        None\n    }\n\n    /// Convert only if the unit is the same (conversion to other units does\n    /// not make sense).\n    pub fn to(&self, unit: &str) -> Result<Self, ()> {\n        if !unit.eq_ignore_ascii_case(\"number\") {\n            return Err(());\n        }\n        Ok(Self {\n            value: self.value,\n            calc_clamping_mode: self.calc_clamping_mode,\n        })\n    }\n\n    #[allow(missing_docs)]\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Number, ParseError<'i>> {\n        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)\n    }\n\n    #[allow(missing_docs)]\n    pub fn parse_at_least_one<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Number, ParseError<'i>> {\n        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)\n    }\n\n    /// Clamp to 1.0 if the value is over 1.0.\n    #[inline]\n    pub fn clamp_to_one(self) -> Self {\n        Number {\n            value: self.value.min(1.),\n            calc_clamping_mode: self.calc_clamping_mode,\n        }\n    }\n}\n\nimpl ToComputedValue for Number {\n    type ComputedValue = CSSFloat;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> CSSFloat {\n        self.get()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &CSSFloat) -> Self {\n        Number {\n            value: *computed,\n            calc_clamping_mode: None,\n        }\n    }\n}\n\nimpl ToCss for Number {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_number(self.value, self.calc_clamping_mode.is_some(), dest)\n    }\n}\n\nimpl ToTyped for Number {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        reify_number(self.value, self.calc_clamping_mode.is_some(), dest)\n    }\n}\n\nimpl IsParallelTo for (Number, Number, Number) {\n    fn is_parallel_to(&self, vector: &DirectionVector) -> bool {\n        use euclid::approxeq::ApproxEq;\n        // If a and b is parallel, the angle between them is 0deg, so\n        // a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.\n        let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());\n        self_vector\n            .cross(*vector)\n            .square_length()\n            .approx_eq(&0.0f32)\n    }\n}\n\nimpl SpecifiedValueInfo for Number {}\n\nimpl Add for Number {\n    type Output = Self;\n\n    fn add(self, other: Self) -> Self {\n        Self::new(self.get() + other.get())\n    }\n}\n\nimpl Zero for Number {\n    #[inline]\n    fn zero() -> Self {\n        Self::new(0.)\n    }\n\n    #[inline]\n    fn is_zero(&self) -> bool {\n        self.get() == 0.\n    }\n}\n\nimpl From<Number> for f32 {\n    #[inline]\n    fn from(n: Number) -> Self {\n        n.get()\n    }\n}\n\nimpl From<Number> for f64 {\n    #[inline]\n    fn from(n: Number) -> Self {\n        n.get() as f64\n    }\n}\n\n/// A Number which is >= 0.0.\npub type NonNegativeNumber = NonNegative<Number>;\n\nimpl Parse for NonNegativeNumber {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_number_with_clamping_mode(context, input, AllowedNumericType::NonNegative)\n            .map(NonNegative::<Number>)\n    }\n}\n\nimpl One for NonNegativeNumber {\n    #[inline]\n    fn one() -> Self {\n        NonNegativeNumber::new(1.0)\n    }\n\n    #[inline]\n    fn is_one(&self) -> bool {\n        self.get() == 1.0\n    }\n}\n\nimpl NonNegativeNumber {\n    /// Returns a new non-negative number with the value `val`.\n    pub fn new(val: CSSFloat) -> Self {\n        NonNegative::<Number>(Number::new(val.max(0.)))\n    }\n\n    /// Returns the numeric value.\n    #[inline]\n    pub fn get(&self) -> f32 {\n        self.0.get()\n    }\n}\n\n/// An Integer which is >= 0.\npub type NonNegativeInteger = NonNegative<Integer>;\n\nimpl Parse for NonNegativeInteger {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(NonNegative(Integer::parse_non_negative(context, input)?))\n    }\n}\n\n/// A Number which is >= 1.0.\npub type GreaterThanOrEqualToOneNumber = GreaterThanOrEqualToOne<Number>;\n\nimpl Parse for GreaterThanOrEqualToOneNumber {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        parse_number_with_clamping_mode(context, input, AllowedNumericType::AtLeastOne)\n            .map(GreaterThanOrEqualToOne::<Number>)\n    }\n}\n\n/// <number> | <percentage>\n///\n/// Accepts only non-negative numbers.\n#[allow(missing_docs)]\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\npub enum NumberOrPercentage {\n    Percentage(Percentage),\n    Number(Number),\n}\n\nimpl NumberOrPercentage {\n    fn parse_with_clamping_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        type_: AllowedNumericType,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(per) =\n            input.try_parse(|i| Percentage::parse_with_clamping_mode(context, i, type_))\n        {\n            return Ok(NumberOrPercentage::Percentage(per));\n        }\n\n        parse_number_with_clamping_mode(context, input, type_).map(NumberOrPercentage::Number)\n    }\n\n    /// Parse a non-negative number or percentage.\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)\n    }\n\n    /// Convert the number or the percentage to a number.\n    pub fn to_percentage(self) -> Percentage {\n        match self {\n            Self::Percentage(p) => p,\n            Self::Number(n) => n.to_percentage(),\n        }\n    }\n\n    /// Convert the number or the percentage to a number.\n    pub fn to_number(self) -> Number {\n        match self {\n            Self::Percentage(p) => p.to_number(),\n            Self::Number(n) => n,\n        }\n    }\n}\n\nimpl Parse for NumberOrPercentage {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)\n    }\n}\n\n/// A non-negative <number> | <percentage>.\npub type NonNegativeNumberOrPercentage = NonNegative<NumberOrPercentage>;\n\nimpl NonNegativeNumberOrPercentage {\n    /// Returns the `100%` value.\n    #[inline]\n    pub fn hundred_percent() -> Self {\n        NonNegative(NumberOrPercentage::Percentage(Percentage::hundred()))\n    }\n\n    /// Return a particular number.\n    #[inline]\n    pub fn new_number(n: f32) -> Self {\n        NonNegative(NumberOrPercentage::Number(Number::new(n)))\n    }\n}\n\nimpl Parse for NonNegativeNumberOrPercentage {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(NonNegative(NumberOrPercentage::parse_non_negative(\n            context, input,\n        )?))\n    }\n}\n\n/// The value of Opacity is <alpha-value>, which is \"<number> | <percentage>\".\n/// However, we serialize the specified value as number, so it's ok to store\n/// the Opacity as Number.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\npub struct Opacity(Number);\n\nimpl Parse for Opacity {\n    /// Opacity accepts <number> | <percentage>, so we parse it as NumberOrPercentage,\n    /// and then convert into an Number if it's a Percentage.\n    /// https://drafts.csswg.org/cssom/#serializing-css-values\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let number = NumberOrPercentage::parse(context, input)?.to_number();\n        Ok(Opacity(number))\n    }\n}\n\nimpl ToComputedValue for Opacity {\n    type ComputedValue = CSSFloat;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> CSSFloat {\n        let value = self.0.to_computed_value(context);\n        if context.for_animation {\n            // Type <number> and <percentage> should be able to interpolate\n            // out-of-range opacity values which benefits additive animation\n            value\n        } else {\n            value.min(1.0).max(0.0)\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &CSSFloat) -> Self {\n        Opacity(Number::from_computed_value(computed))\n    }\n}\n\n/// A specified `<integer>`, either a simple integer value or a calc expression.\n/// Note that a calc expression may not actually be an integer; it will be rounded\n/// at computed-value time.\n///\n/// <https://drafts.csswg.org/css-values/#integers>\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem, ToTyped)]\n#[typed(todo_derive_fields)]\npub enum Integer {\n    /// A literal integer value.\n    Literal(CSSInteger),\n    /// A calc expression, whose value will be rounded later if necessary.\n    Calc(CSSFloat),\n}\n\nimpl Zero for Integer {\n    #[inline]\n    fn zero() -> Self {\n        Self::new(0)\n    }\n\n    #[inline]\n    fn is_zero(&self) -> bool {\n        *self == 0\n    }\n}\n\nimpl One for Integer {\n    #[inline]\n    fn one() -> Self {\n        Self::new(1)\n    }\n\n    #[inline]\n    fn is_one(&self) -> bool {\n        *self == 1\n    }\n}\n\nimpl PartialEq<i32> for Integer {\n    fn eq(&self, value: &i32) -> bool {\n        self.value() == *value\n    }\n}\n\nimpl Integer {\n    /// Trivially constructs a new `Integer` value.\n    pub fn new(val: CSSInteger) -> Self {\n        Self::Literal(val)\n    }\n\n    /// Returns the (rounded) integer value associated with this value.\n    pub fn value(&self) -> CSSInteger {\n        match *self {\n            Self::Literal(i) => i,\n            Self::Calc(n) => (n + 0.5).floor() as CSSInteger,\n        }\n    }\n\n    /// Trivially constructs a new integer value from a `calc()` expression.\n    fn from_calc(val: CSSFloat) -> Self {\n        Self::Calc(val)\n    }\n}\n\nimpl Parse for Integer {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::Number {\n                int_value: Some(v), ..\n            } => Ok(Integer::new(v)),\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                let result = CalcNode::parse_number(context, input, function)?;\n                Ok(Integer::from_calc(result))\n            },\n            ref t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n}\n\nimpl Integer {\n    /// Parse an integer value which is at least `min`.\n    pub fn parse_with_minimum<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        min: i32,\n    ) -> Result<Integer, ParseError<'i>> {\n        let value = Integer::parse(context, input)?;\n        // FIXME(emilio): The spec asks us to avoid rejecting it at parse\n        // time except until computed value time.\n        //\n        // It's not totally clear it's worth it though, and no other browser\n        // does this.\n        if value.value() < min {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(value)\n    }\n\n    /// Parse a non-negative integer.\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Integer, ParseError<'i>> {\n        Integer::parse_with_minimum(context, input, 0)\n    }\n\n    /// Parse a positive integer (>= 1).\n    pub fn parse_positive<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Integer, ParseError<'i>> {\n        Integer::parse_with_minimum(context, input, 1)\n    }\n}\n\nimpl ToComputedValue for Integer {\n    type ComputedValue = i32;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> i32 {\n        self.value()\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &i32) -> Self {\n        Integer::new(*computed)\n    }\n}\n\nimpl ToCss for Integer {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match *self {\n            Integer::Literal(i) => i.to_css(dest),\n            Integer::Calc(n) => {\n                dest.write_str(\"calc(\")?;\n                n.to_css(dest)?;\n                dest.write_char(')')\n            },\n        }\n    }\n}\n\nimpl SpecifiedValueInfo for Integer {}\n\n/// A wrapper of Integer, with value >= 1.\npub type PositiveInteger = GreaterThanOrEqualToOne<Integer>;\n\nimpl Parse for PositiveInteger {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Integer::parse_positive(context, input).map(GreaterThanOrEqualToOne)\n    }\n}\n\n/// The specified value of a grid `<track-breadth>`\npub type TrackBreadth = GenericTrackBreadth<LengthPercentage>;\n\n/// The specified value of a grid `<track-size>`\npub type TrackSize = GenericTrackSize<LengthPercentage>;\n\n/// The specified value of a grid `<track-size>+`\npub type ImplicitGridTracks = GenericImplicitGridTracks<TrackSize>;\n\n/// The specified value of a grid `<track-list>`\n/// (could also be `<auto-track-list>` or `<explicit-track-list>`)\npub type TrackList = GenericTrackList<LengthPercentage, Integer>;\n\n/// The specified value of a `<grid-line>`.\npub type GridLine = GenericGridLine<Integer>;\n\n/// `<grid-template-rows> | <grid-template-columns>`\npub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;\n\n/// rect(...)\npub type ClipRect = generics::GenericClipRect<LengthOrAuto>;\n\nimpl Parse for ClipRect {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl ClipRect {\n    /// Parses a rect(<top>, <left>, <bottom>, <right>), allowing quirks.\n    fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"rect\")?;\n\n        fn parse_argument<'i, 't>(\n            context: &ParserContext,\n            input: &mut Parser<'i, 't>,\n            allow_quirks: AllowQuirks,\n        ) -> Result<LengthOrAuto, ParseError<'i>> {\n            LengthOrAuto::parse_quirky(context, input, allow_quirks)\n        }\n\n        input.parse_nested_block(|input| {\n            let top = parse_argument(context, input, allow_quirks)?;\n            let right;\n            let bottom;\n            let left;\n\n            if input.try_parse(|input| input.expect_comma()).is_ok() {\n                right = parse_argument(context, input, allow_quirks)?;\n                input.expect_comma()?;\n                bottom = parse_argument(context, input, allow_quirks)?;\n                input.expect_comma()?;\n                left = parse_argument(context, input, allow_quirks)?;\n            } else {\n                right = parse_argument(context, input, allow_quirks)?;\n                bottom = parse_argument(context, input, allow_quirks)?;\n                left = parse_argument(context, input, allow_quirks)?;\n            }\n\n            Ok(ClipRect {\n                top,\n                right,\n                bottom,\n                left,\n            })\n        })\n    }\n}\n\n/// rect(...) | auto\npub type ClipRectOrAuto = generics::GenericClipRectOrAuto<ClipRect>;\n\nimpl ClipRectOrAuto {\n    /// Parses a ClipRect or Auto, allowing quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(v) = input.try_parse(|i| ClipRect::parse_quirky(context, i, allow_quirks)) {\n            return Ok(generics::GenericClipRectOrAuto::Rect(v));\n        }\n        input.expect_ident_matching(\"auto\")?;\n        Ok(generics::GenericClipRectOrAuto::Auto)\n    }\n}\n\n/// Whether quirks are allowed in this context.\n#[derive(Clone, Copy, PartialEq)]\npub enum AllowQuirks {\n    /// Quirks are not allowed.\n    No,\n    /// Quirks are allowed, in quirks mode.\n    Yes,\n    /// Quirks are always allowed, used for SVG lengths.\n    Always,\n}\n\nimpl AllowQuirks {\n    /// Returns `true` if quirks are allowed in this context.\n    pub fn allowed(self, quirks_mode: QuirksMode) -> bool {\n        match self {\n            AllowQuirks::Always => true,\n            AllowQuirks::No => false,\n            AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,\n        }\n    }\n}\n\n#[derive(Clone, Debug, PartialEq, MallocSizeOf, ToShmem)]\n/// A namespace wrapper to distinguish between valid variants\npub enum ParsedNamespace {\n    /// Unregistered namespace\n    Unknown,\n    /// Registered namespace\n    Known(Namespace),\n}\n\nimpl ParsedNamespace {\n    /// Parse a namespace prefix and resolve it to the correct\n    /// namespace URI.\n    pub fn parse<'i, 't>(\n        namespaces: &FxHashMap<Prefix, Namespace>,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // We don't need to keep the prefix because different\n        // prefixes can resolve to the same id. Additionally,\n        // we also don't need it for serialization as substitution\n        // functions serialize from the direct css declaration.\n        parse_namespace(namespaces, input, /*allow_non_registered*/ true)\n            .map(|(_prefix, namespace)| namespace)\n    }\n}\n\nimpl Default for ParsedNamespace {\n    fn default() -> Self {\n        Self::Known(Namespace::default())\n    }\n}\n\n/// An attr(...) rule\n///\n/// `[namespace? `|`]? ident`\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[css(function)]\n#[repr(C)]\npub struct Attr {\n    /// Optional namespace prefix.\n    pub namespace_prefix: Prefix,\n    /// Optional namespace URL.\n    pub namespace_url: Namespace,\n    /// Attribute name\n    pub attribute: Atom,\n    /// Fallback value\n    pub fallback: AtomString,\n}\n\nimpl Parse for Attr {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Attr, ParseError<'i>> {\n        input.expect_function_matching(\"attr\")?;\n        input.parse_nested_block(|i| Attr::parse_function(context, i))\n    }\n}\n\n/// Try to parse a namespace and return it if parsed, or none if there was not one present\npub fn parse_namespace<'i, 't>(\n    namespaces: &FxHashMap<Prefix, Namespace>,\n    input: &mut Parser<'i, 't>,\n    // TODO: Once general attr is enabled, we should remove this flag\n    allow_non_registered: bool,\n) -> Result<(Prefix, ParsedNamespace), ParseError<'i>> {\n    let ns_prefix = match input.next()? {\n        Token::Ident(ref prefix) => Some(Prefix::from(prefix.as_ref())),\n        Token::Delim('|') => None,\n        _ => return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n    };\n\n    if ns_prefix.is_some() && !matches!(*input.next_including_whitespace()?, Token::Delim('|')) {\n        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n    }\n\n    if let Some(prefix) = ns_prefix {\n        let ns = match namespaces.get(&prefix).cloned() {\n            Some(ns) => ParsedNamespace::Known(ns),\n            None => {\n                if allow_non_registered {\n                    ParsedNamespace::Unknown\n                } else {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n            },\n        };\n        Ok((prefix, ns))\n    } else {\n        Ok((Prefix::default(), ParsedNamespace::default()))\n    }\n}\n\nimpl Attr {\n    /// Parse contents of attr() assuming we have already parsed `attr` and are\n    /// within a parse_nested_block()\n    pub fn parse_function<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Attr, ParseError<'i>> {\n        // Syntax is `[namespace? '|']? ident [',' fallback]?`\n        let namespace = input\n            .try_parse(|input| {\n                parse_namespace(\n                    &context.namespaces.prefixes,\n                    input,\n                    /*allow_non_registered*/ false,\n                )\n            })\n            .ok();\n        let namespace_is_some = namespace.is_some();\n        let (namespace_prefix, namespace_url) = namespace.unwrap_or_default();\n        let ParsedNamespace::Known(namespace_url) = namespace_url else {\n            unreachable!(\"Non-registered url not allowed (see parse namespace flag).\")\n        };\n\n        // If there is a namespace, ensure no whitespace following '|'\n        let attribute = Atom::from(if namespace_is_some {\n            let location = input.current_source_location();\n            match *input.next_including_whitespace()? {\n                Token::Ident(ref ident) => ident.as_ref(),\n                ref t => return Err(location.new_unexpected_token_error(t.clone())),\n            }\n        } else {\n            input.expect_ident()?.as_ref()\n        });\n\n        // Fallback will always be a string value for now as we do not support\n        // attr() types yet.\n        let fallback = input\n            .try_parse(|input| -> Result<AtomString, ParseError<'i>> {\n                input.expect_comma()?;\n                Ok(input.expect_string()?.as_ref().into())\n            })\n            .unwrap_or_default();\n\n        Ok(Attr {\n            namespace_prefix,\n            namespace_url,\n            attribute,\n            fallback,\n        })\n    }\n}\n\nimpl ToCss for Attr {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(\"attr(\")?;\n        if !self.namespace_prefix.is_empty() {\n            serialize_atom_identifier(&self.namespace_prefix, dest)?;\n            dest.write_char('|')?;\n        }\n        serialize_atom_identifier(&self.attribute, dest)?;\n\n        if !self.fallback.is_empty() {\n            dest.write_str(\", \")?;\n            self.fallback.to_css(dest)?;\n        }\n\n        dest.write_char(')')\n    }\n}\n"
  },
  {
    "path": "style/values/specified/motion.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values that are related to motion path.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::motion::OffsetRotate as ComputedOffsetRotate;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::motion as generics;\nuse crate::values::specified::basic_shape::BasicShape;\nuse crate::values::specified::position::{HorizontalPosition, VerticalPosition};\nuse crate::values::specified::url::SpecifiedUrl;\nuse crate::values::specified::{Angle, Position};\nuse crate::Zero;\nuse cssparser::Parser;\nuse style_traits::{ParseError, StyleParseErrorKind};\n\n/// The specified value of ray() function.\npub type RayFunction = generics::GenericRayFunction<Angle, Position>;\n\n/// The specified value of <offset-path>.\npub type OffsetPathFunction =\n    generics::GenericOffsetPathFunction<BasicShape, RayFunction, SpecifiedUrl>;\n\n/// The specified value of `offset-path`.\npub type OffsetPath = generics::GenericOffsetPath<OffsetPathFunction>;\n\n/// The specified value of `offset-position`.\npub type OffsetPosition = generics::GenericOffsetPosition<HorizontalPosition, VerticalPosition>;\n\n/// The <coord-box> value, which defines the box that the <offset-path> sizes into.\n/// https://drafts.fxtf.org/motion-1/#valdef-offset-path-coord-box\n///\n/// <coord-box> = content-box | padding-box | border-box | fill-box | stroke-box | view-box\n/// https://drafts.csswg.org/css-box-4/#typedef-coord-box\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum CoordBox {\n    ContentBox,\n    PaddingBox,\n    BorderBox,\n    FillBox,\n    StrokeBox,\n    ViewBox,\n}\n\nimpl CoordBox {\n    /// Returns true if it is default value, border-box.\n    #[inline]\n    pub fn is_default(&self) -> bool {\n        matches!(*self, Self::BorderBox)\n    }\n}\n\nimpl Parse for RayFunction {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        input.expect_function_matching(\"ray\")?;\n        input.parse_nested_block(|i| Self::parse_function_arguments(context, i))\n    }\n}\n\nimpl RayFunction {\n    /// Parse the inner arguments of a `ray` function.\n    fn parse_function_arguments<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::specified::PositionOrAuto;\n\n        let mut angle = None;\n        let mut size = None;\n        let mut contain = false;\n        let mut position = None;\n        loop {\n            if angle.is_none() {\n                angle = input.try_parse(|i| Angle::parse(context, i)).ok();\n            }\n\n            if size.is_none() {\n                size = input.try_parse(generics::RaySize::parse).ok();\n                if size.is_some() {\n                    continue;\n                }\n            }\n\n            if !contain {\n                contain = input\n                    .try_parse(|i| i.expect_ident_matching(\"contain\"))\n                    .is_ok();\n                if contain {\n                    continue;\n                }\n            }\n\n            if position.is_none() {\n                if input.try_parse(|i| i.expect_ident_matching(\"at\")).is_ok() {\n                    let pos = Position::parse(context, input)?;\n                    position = Some(PositionOrAuto::Position(pos));\n                }\n\n                if position.is_some() {\n                    continue;\n                }\n            }\n            break;\n        }\n\n        if angle.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(RayFunction {\n            angle: angle.unwrap(),\n            // If no <ray-size> is specified it defaults to closest-side.\n            size: size.unwrap_or(generics::RaySize::ClosestSide),\n            contain,\n            position: position.unwrap_or(PositionOrAuto::auto()),\n        })\n    }\n}\n\nimpl Parse for OffsetPathFunction {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::specified::basic_shape::{AllowedBasicShapes, ShapeType};\n\n        // <offset-path> = <ray()> | <url> | <basic-shape>\n        // https://drafts.fxtf.org/motion-1/#typedef-offset-path\n        if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) {\n            return Ok(OffsetPathFunction::Ray(ray));\n        }\n\n        if static_prefs::pref!(\"layout.css.motion-path-url.enabled\") {\n            if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) {\n                return Ok(OffsetPathFunction::Url(url));\n            }\n        }\n\n        BasicShape::parse(context, input, AllowedBasicShapes::ALL, ShapeType::Outline)\n            .map(OffsetPathFunction::Shape)\n    }\n}\n\nimpl Parse for OffsetPath {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Parse none.\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(OffsetPath::none());\n        }\n\n        let mut path = None;\n        let mut coord_box = None;\n        loop {\n            if path.is_none() {\n                path = input\n                    .try_parse(|i| OffsetPathFunction::parse(context, i))\n                    .ok();\n            }\n\n            if coord_box.is_none() {\n                coord_box = input.try_parse(CoordBox::parse).ok();\n                if coord_box.is_some() {\n                    continue;\n                }\n            }\n            break;\n        }\n\n        if let Some(p) = path {\n            return Ok(OffsetPath::OffsetPath {\n                path: Box::new(p),\n                coord_box: coord_box.unwrap_or(CoordBox::BorderBox),\n            });\n        }\n\n        match coord_box {\n            Some(c) => Ok(OffsetPath::CoordBox(c)),\n            None => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }\n    }\n}\n\n/// The direction of offset-rotate.\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem,\n)]\n#[repr(u8)]\npub enum OffsetRotateDirection {\n    /// Unspecified direction keyword.\n    #[css(skip)]\n    None,\n    /// 0deg offset (face forward).\n    Auto,\n    /// 180deg offset (face backward).\n    Reverse,\n}\n\nimpl OffsetRotateDirection {\n    /// Returns true if it is none (i.e. the keyword is not specified).\n    #[inline]\n    fn is_none(&self) -> bool {\n        *self == OffsetRotateDirection::None\n    }\n}\n\n#[inline]\nfn direction_specified_and_angle_is_zero(direction: &OffsetRotateDirection, angle: &Angle) -> bool {\n    !direction.is_none() && angle.is_zero()\n}\n\n/// The specified offset-rotate.\n/// The syntax is: \"[ auto | reverse ] || <angle>\"\n///\n/// https://drafts.fxtf.org/motion-1/#offset-rotate-property\n#[derive(\n    Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\n#[typed(todo_derive_fields)]\npub struct OffsetRotate {\n    /// [auto | reverse].\n    #[css(skip_if = \"OffsetRotateDirection::is_none\")]\n    direction: OffsetRotateDirection,\n    /// <angle>.\n    /// If direction is None, this is a fixed angle which indicates a\n    /// constant clockwise rotation transformation applied to it by this\n    /// specified rotation angle. Otherwise, the angle will be added to\n    /// the angle of the direction in layout.\n    #[css(contextual_skip_if = \"direction_specified_and_angle_is_zero\")]\n    angle: Angle,\n}\n\nimpl OffsetRotate {\n    /// Returns the initial value, auto.\n    #[inline]\n    pub fn auto() -> Self {\n        OffsetRotate {\n            direction: OffsetRotateDirection::Auto,\n            angle: Angle::zero(),\n        }\n    }\n\n    /// Returns true if self is auto 0deg.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        self.direction == OffsetRotateDirection::Auto && self.angle.is_zero()\n    }\n}\n\nimpl Parse for OffsetRotate {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let mut direction = input.try_parse(OffsetRotateDirection::parse);\n        let angle = input.try_parse(|i| Angle::parse(context, i));\n        if direction.is_err() {\n            // The direction and angle could be any order, so give it a change to parse\n            // direction again.\n            direction = input.try_parse(OffsetRotateDirection::parse);\n        }\n\n        if direction.is_err() && angle.is_err() {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(OffsetRotate {\n            direction: direction.unwrap_or(OffsetRotateDirection::None),\n            angle: angle.unwrap_or(Zero::zero()),\n        })\n    }\n}\n\nimpl ToComputedValue for OffsetRotate {\n    type ComputedValue = ComputedOffsetRotate;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        use crate::values::computed::Angle as ComputedAngle;\n\n        ComputedOffsetRotate {\n            auto: !self.direction.is_none(),\n            angle: if self.direction == OffsetRotateDirection::Reverse {\n                // The computed value should always convert \"reverse\" into \"auto\".\n                // e.g. \"reverse calc(20deg + 10deg)\" => \"auto 210deg\"\n                self.angle.to_computed_value(context) + ComputedAngle::from_degrees(180.0)\n            } else {\n                self.angle.to_computed_value(context)\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        OffsetRotate {\n            direction: if computed.auto {\n                OffsetRotateDirection::Auto\n            } else {\n                OffsetRotateDirection::None\n            },\n            angle: ToComputedValue::from_computed_value(&computed.angle),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/outline.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified values for outline properties\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::specified::BorderStyle;\nuse cssparser::Parser;\nuse selectors::parser::SelectorParseErrorKind;\nuse style_traits::ParseError;\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Ord,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\n/// <https://drafts.csswg.org/css-ui/#propdef-outline-style>\npub enum OutlineStyle {\n    /// auto\n    Auto,\n    /// <border-style>\n    BorderStyle(BorderStyle),\n}\n\nimpl OutlineStyle {\n    #[inline]\n    /// Get default value as None\n    pub fn none() -> OutlineStyle {\n        OutlineStyle::BorderStyle(BorderStyle::None)\n    }\n\n    #[inline]\n    /// Get value for None or Hidden\n    pub fn none_or_hidden(&self) -> bool {\n        match *self {\n            OutlineStyle::Auto => false,\n            OutlineStyle::BorderStyle(ref style) => style.none_or_hidden(),\n        }\n    }\n}\n\nimpl Parse for OutlineStyle {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<OutlineStyle, ParseError<'i>> {\n        if let Ok(border_style) = input.try_parse(BorderStyle::parse) {\n            if let BorderStyle::Hidden = border_style {\n                return Err(input\n                    .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(\"hidden\".into())));\n            }\n\n            return Ok(OutlineStyle::BorderStyle(border_style));\n        }\n\n        input.expect_ident_matching(\"auto\")?;\n        Ok(OutlineStyle::Auto)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/page.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified @page at-rule properties and named-page style properties\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::size::Size2D;\nuse crate::values::specified::length::NonNegativeLength;\nuse crate::values::{generics, CustomIdent};\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse style_traits::ParseError;\n\npub use generics::page::PageOrientation;\npub use generics::page::PageSizeOrientation;\npub use generics::page::PaperSize;\n/// Specified value of the @page size descriptor\npub type PageSize = generics::page::PageSize<Size2D<NonNegativeLength>>;\n\nimpl Parse for PageSize {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Try to parse as <page-size> [ <orientation> ]\n        if let Ok(paper_size) = input.try_parse(PaperSize::parse) {\n            let orientation = input\n                .try_parse(PageSizeOrientation::parse)\n                .unwrap_or(PageSizeOrientation::Portrait);\n            return Ok(PageSize::PaperSize(paper_size, orientation));\n        }\n        // Try to parse as <orientation> [ <page-size> ]\n        if let Ok(orientation) = input.try_parse(PageSizeOrientation::parse) {\n            if let Ok(paper_size) = input.try_parse(PaperSize::parse) {\n                return Ok(PageSize::PaperSize(paper_size, orientation));\n            }\n            return Ok(PageSize::Orientation(orientation));\n        }\n        // Try to parse dimensions\n        if let Ok(size) =\n            input.try_parse(|i| Size2D::parse_with(context, i, NonNegativeLength::parse))\n        {\n            return Ok(PageSize::Size(size));\n        }\n        // auto value\n        input.expect_ident_matching(\"auto\")?;\n        Ok(PageSize::Auto)\n    }\n}\n\n/// Page name value.\n///\n/// https://drafts.csswg.org/css-page-3/#using-named-pages\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum PageName {\n    /// `auto` value.\n    Auto,\n    /// Page name value\n    PageName(CustomIdent),\n}\n\nimpl Parse for PageName {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let ident = input.expect_ident()?;\n        Ok(match_ignore_ascii_case! { ident,\n            \"auto\" => PageName::auto(),\n            _ => PageName::PageName(CustomIdent::from_ident(location, ident, &[])?),\n        })\n    }\n}\n\nimpl PageName {\n    /// `auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        PageName::Auto\n    }\n\n    /// Whether this is the `auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, PageName::Auto)\n    }\n}\n"
  },
  {
    "path": "style/values/specified/percentage.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified percentages.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::percentage::Percentage as ComputedPercentage;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::NonNegative;\nuse crate::values::specified::calc::CalcNode;\nuse crate::values::specified::Number;\nuse crate::values::{normalize, reify_percentage, serialize_percentage, CSSFloat};\nuse cssparser::{Parser, Token};\nuse std::fmt::{self, Write};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss, ToTyped, TypedValue};\nuse thin_vec::ThinVec;\n\n/// A percentage value.\n#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]\npub struct Percentage {\n    /// The percentage value as a float.\n    ///\n    /// [0 .. 100%] maps to [0.0 .. 1.0]\n    value: CSSFloat,\n    /// If this percentage came from a calc() expression, this tells how\n    /// clamping should be done on the value.\n    calc_clamping_mode: Option<AllowedNumericType>,\n}\n\nimpl ToCss for Percentage {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.calc_clamping_mode.is_some() {\n            dest.write_str(\"calc(\")?;\n        }\n\n        serialize_percentage(self.value, dest)?;\n\n        if self.calc_clamping_mode.is_some() {\n            dest.write_char(')')?;\n        }\n        Ok(())\n    }\n}\n\nimpl ToTyped for Percentage {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        reify_percentage(self.value, self.calc_clamping_mode.is_some(), dest)\n    }\n}\n\nimpl Percentage {\n    /// Creates a percentage from a numeric value.\n    pub(super) fn new_with_clamping_mode(\n        value: CSSFloat,\n        calc_clamping_mode: Option<AllowedNumericType>,\n    ) -> Self {\n        Self {\n            value,\n            calc_clamping_mode,\n        }\n    }\n\n    /// Creates a percentage from a numeric value.\n    pub fn new(value: CSSFloat) -> Self {\n        Self::new_with_clamping_mode(value, None)\n    }\n\n    /// `0%`\n    #[inline]\n    pub fn zero() -> Self {\n        Percentage {\n            value: 0.,\n            calc_clamping_mode: None,\n        }\n    }\n\n    /// `100%`\n    #[inline]\n    pub fn hundred() -> Self {\n        Percentage {\n            value: 1.,\n            calc_clamping_mode: None,\n        }\n    }\n\n    /// Gets the underlying value for this float.\n    pub fn get(&self) -> CSSFloat {\n        self.calc_clamping_mode\n            .map_or(self.value, |mode| mode.clamp(self.value))\n    }\n\n    /// Return the unit, as a string.\n    pub fn unit(&self) -> &'static str {\n        \"percent\"\n    }\n\n    /// Return no canonical unit (percent values do not have one).\n    pub fn canonical_unit(&self) -> Option<&'static str> {\n        None\n    }\n\n    /// Convert only if the unit is the same (conversion to other units does\n    /// not make sense).\n    pub fn to(&self, unit: &str) -> Result<Self, ()> {\n        if !unit.eq_ignore_ascii_case(\"percent\") {\n            return Err(());\n        }\n        Ok(Self {\n            value: self.value,\n            calc_clamping_mode: self.calc_clamping_mode,\n        })\n    }\n\n    /// Returns this percentage as a number.\n    pub fn to_number(&self) -> Number {\n        Number::new_with_clamping_mode(self.value, self.calc_clamping_mode)\n    }\n\n    /// Returns the calc() clamping mode for this percentage.\n    pub fn calc_clamping_mode(&self) -> Option<AllowedNumericType> {\n        self.calc_clamping_mode\n    }\n\n    /// Reverses this percentage, preserving calc-ness.\n    ///\n    /// For example: If it was 20%, convert it into 80%.\n    pub fn reverse(&mut self) {\n        let new_value = 1. - self.value;\n        self.value = new_value;\n    }\n\n    /// Parses a specific kind of percentage.\n    pub fn parse_with_clamping_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        num_context: AllowedNumericType,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::Percentage { unit_value, .. }\n                if num_context.is_ok(context.parsing_mode, unit_value) =>\n            {\n                Ok(Percentage::new(unit_value))\n            },\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                let value = CalcNode::parse_percentage(context, input, function)?;\n                Ok(Percentage {\n                    value,\n                    calc_clamping_mode: Some(num_context),\n                })\n            },\n            ref t => Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n\n    /// Parses a percentage token, but rejects it if it's negative.\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)\n    }\n\n    /// Parses a percentage token, but rejects it if it's negative or more than\n    /// 100%.\n    pub fn parse_zero_to_a_hundred<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::ZeroToOne)\n    }\n\n    /// Clamp to 100% if the value is over 100%.\n    #[inline]\n    pub fn clamp_to_hundred(self) -> Self {\n        Percentage {\n            value: self.value.min(1.),\n            calc_clamping_mode: self.calc_clamping_mode,\n        }\n    }\n}\n\nimpl Parse for Percentage {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)\n    }\n}\n\nimpl ToComputedValue for Percentage {\n    type ComputedValue = ComputedPercentage;\n\n    #[inline]\n    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {\n        ComputedPercentage(normalize(self.get()))\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Percentage::new(computed.0)\n    }\n}\n\nimpl SpecifiedValueInfo for Percentage {}\n\n/// Turns the percentage into a plain float.\npub trait ToPercentage {\n    /// Returns whether this percentage used to be a calc().\n    fn is_calc(&self) -> bool {\n        false\n    }\n    /// Turns the percentage into a plain float.\n    fn to_percentage(&self) -> CSSFloat;\n}\n\nimpl ToPercentage for Percentage {\n    fn is_calc(&self) -> bool {\n        self.calc_clamping_mode.is_some()\n    }\n\n    fn to_percentage(&self) -> CSSFloat {\n        self.get()\n    }\n}\n\n/// A wrapper of Percentage, whose value must be >= 0.\npub type NonNegativePercentage = NonNegative<Percentage>;\n\nimpl Parse for NonNegativePercentage {\n    #[inline]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Ok(NonNegative(Percentage::parse_non_negative(context, input)?))\n    }\n}\n\nimpl NonNegativePercentage {\n    /// Convert to ComputedPercentage, for FontFaceRule size-adjust getter.\n    #[inline]\n    pub fn compute(&self) -> ComputedPercentage {\n        ComputedPercentage(self.0.get())\n    }\n}\n"
  },
  {
    "path": "style/values/specified/position.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! CSS handling for the specified value of\n//! [`position`][position]s\n//!\n//! [position]: https://drafts.csswg.org/css-backgrounds-3/#position\n\nuse crate::derives::*;\nuse crate::logical_geometry::{LogicalAxis, LogicalSide, PhysicalSide, WritingMode};\nuse crate::parser::{Parse, ParserContext};\nuse crate::selector_map::PrecomputedHashMap;\nuse crate::str::HTML_SPACE_CHARACTERS;\nuse crate::values::computed::LengthPercentage as ComputedLengthPercentage;\nuse crate::values::computed::{Context, Percentage, ToComputedValue};\nuse crate::values::generics::length::GenericAnchorSizeFunction;\nuse crate::values::generics::position::PositionComponent as GenericPositionComponent;\nuse crate::values::generics::position::PositionOrAuto as GenericPositionOrAuto;\nuse crate::values::generics::position::ZIndex as GenericZIndex;\nuse crate::values::generics::position::{AspectRatio as GenericAspectRatio, GenericAnchorSide};\nuse crate::values::generics::position::{GenericAnchorFunction, GenericInset, TreeScoped};\nuse crate::values::generics::position::{IsTreeScoped, Position as GenericPosition};\nuse crate::values::specified;\nuse crate::values::specified::align::AlignFlags;\nuse crate::values::specified::{AllowQuirks, Integer, LengthPercentage, NonNegativeNumber};\nuse crate::values::{AtomIdent, DashedIdent};\nuse crate::{Atom, Zero};\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse num_traits::FromPrimitive;\nuse selectors::parser::SelectorParseErrorKind;\nuse servo_arc::Arc;\nuse smallvec::{smallvec, SmallVec};\nuse std::collections::hash_map::Entry;\nuse std::fmt::{self, Write};\nuse style_traits::arc_slice::ArcSlice;\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\nuse thin_vec::ThinVec;\n\n/// The specified value of a CSS `<position>`\npub type Position = GenericPosition<HorizontalPosition, VerticalPosition>;\n\n/// The specified value of an `auto | <position>`.\npub type PositionOrAuto = GenericPositionOrAuto<Position>;\n\n/// The specified value of a horizontal position.\npub type HorizontalPosition = PositionComponent<HorizontalPositionKeyword>;\n\n/// The specified value of a vertical position.\npub type VerticalPosition = PositionComponent<VerticalPositionKeyword>;\n\n/// The specified value of a component of a CSS `<position>`.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\n#[typed(todo_derive_fields)]\npub enum PositionComponent<S> {\n    /// `center`\n    Center,\n    /// `<length-percentage>`\n    Length(LengthPercentage),\n    /// `<side> <length-percentage>?`\n    Side(S, Option<LengthPercentage>),\n}\n\n/// A keyword for the X direction.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum HorizontalPositionKeyword {\n    Left,\n    Right,\n}\n\n/// A keyword for the Y direction.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum VerticalPositionKeyword {\n    Top,\n    Bottom,\n}\n\nimpl Parse for Position {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let position = Self::parse_three_value_quirky(context, input, AllowQuirks::No)?;\n        if position.is_three_value_syntax() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(position)\n    }\n}\n\nimpl Position {\n    /// Parses a `<bg-position>`, with quirks.\n    pub fn parse_three_value_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        match input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks)) {\n            Ok(x_pos @ PositionComponent::Center) => {\n                if let Ok(y_pos) =\n                    input.try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))\n                {\n                    return Ok(Self::new(x_pos, y_pos));\n                }\n                let x_pos = input\n                    .try_parse(|i| PositionComponent::parse_quirky(context, i, allow_quirks))\n                    .unwrap_or(x_pos);\n                let y_pos = PositionComponent::Center;\n                return Ok(Self::new(x_pos, y_pos));\n            },\n            Ok(PositionComponent::Side(x_keyword, lp)) => {\n                if input\n                    .try_parse(|i| i.expect_ident_matching(\"center\"))\n                    .is_ok()\n                {\n                    let x_pos = PositionComponent::Side(x_keyword, lp);\n                    let y_pos = PositionComponent::Center;\n                    return Ok(Self::new(x_pos, y_pos));\n                }\n                if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {\n                    let y_lp = input\n                        .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n                        .ok();\n                    let x_pos = PositionComponent::Side(x_keyword, lp);\n                    let y_pos = PositionComponent::Side(y_keyword, y_lp);\n                    return Ok(Self::new(x_pos, y_pos));\n                }\n                let x_pos = PositionComponent::Side(x_keyword, None);\n                let y_pos = lp.map_or(PositionComponent::Center, PositionComponent::Length);\n                return Ok(Self::new(x_pos, y_pos));\n            },\n            Ok(x_pos @ PositionComponent::Length(_)) => {\n                if let Ok(y_keyword) = input.try_parse(VerticalPositionKeyword::parse) {\n                    let y_pos = PositionComponent::Side(y_keyword, None);\n                    return Ok(Self::new(x_pos, y_pos));\n                }\n                if let Ok(y_lp) =\n                    input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n                {\n                    let y_pos = PositionComponent::Length(y_lp);\n                    return Ok(Self::new(x_pos, y_pos));\n                }\n                let y_pos = PositionComponent::Center;\n                let _ = input.try_parse(|i| i.expect_ident_matching(\"center\"));\n                return Ok(Self::new(x_pos, y_pos));\n            },\n            Err(_) => {},\n        }\n        let y_keyword = VerticalPositionKeyword::parse(input)?;\n        let lp_and_x_pos: Result<_, ParseError> = input.try_parse(|i| {\n            let y_lp = i\n                .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n                .ok();\n            if let Ok(x_keyword) = i.try_parse(HorizontalPositionKeyword::parse) {\n                let x_lp = i\n                    .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n                    .ok();\n                let x_pos = PositionComponent::Side(x_keyword, x_lp);\n                return Ok((y_lp, x_pos));\n            };\n            i.expect_ident_matching(\"center\")?;\n            let x_pos = PositionComponent::Center;\n            Ok((y_lp, x_pos))\n        });\n        if let Ok((y_lp, x_pos)) = lp_and_x_pos {\n            let y_pos = PositionComponent::Side(y_keyword, y_lp);\n            return Ok(Self::new(x_pos, y_pos));\n        }\n        let x_pos = PositionComponent::Center;\n        let y_pos = PositionComponent::Side(y_keyword, None);\n        Ok(Self::new(x_pos, y_pos))\n    }\n\n    /// `center center`\n    #[inline]\n    pub fn center() -> Self {\n        Self::new(PositionComponent::Center, PositionComponent::Center)\n    }\n\n    /// Returns true if this uses a 3 value syntax.\n    #[inline]\n    fn is_three_value_syntax(&self) -> bool {\n        self.horizontal.component_count() != self.vertical.component_count()\n    }\n}\n\nimpl ToCss for Position {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match (&self.horizontal, &self.vertical) {\n            (\n                x_pos @ &PositionComponent::Side(_, Some(_)),\n                &PositionComponent::Length(ref y_lp),\n            ) => {\n                x_pos.to_css(dest)?;\n                dest.write_str(\" top \")?;\n                y_lp.to_css(dest)\n            },\n            (\n                &PositionComponent::Length(ref x_lp),\n                y_pos @ &PositionComponent::Side(_, Some(_)),\n            ) => {\n                dest.write_str(\"left \")?;\n                x_lp.to_css(dest)?;\n                dest.write_char(' ')?;\n                y_pos.to_css(dest)\n            },\n            (x_pos, y_pos) => {\n                x_pos.to_css(dest)?;\n                dest.write_char(' ')?;\n                y_pos.to_css(dest)\n            },\n        }\n    }\n}\n\nimpl<S: Parse> Parse for PositionComponent<S> {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\nimpl<S: Parse> PositionComponent<S> {\n    /// Parses a component of a CSS position, with quirks.\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"center\"))\n            .is_ok()\n        {\n            return Ok(PositionComponent::Center);\n        }\n        if let Ok(lp) =\n            input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n        {\n            return Ok(PositionComponent::Length(lp));\n        }\n        let keyword = S::parse(context, input)?;\n        let lp = input\n            .try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n            .ok();\n        Ok(PositionComponent::Side(keyword, lp))\n    }\n}\n\nimpl<S> GenericPositionComponent for PositionComponent<S> {\n    fn is_center(&self) -> bool {\n        match *self {\n            PositionComponent::Center => true,\n            PositionComponent::Length(LengthPercentage::Percentage(ref per)) => per.0 == 0.5,\n            // 50% from any side is still the center.\n            PositionComponent::Side(_, Some(LengthPercentage::Percentage(ref per))) => per.0 == 0.5,\n            _ => false,\n        }\n    }\n}\n\nimpl<S> PositionComponent<S> {\n    /// `0%`\n    pub fn zero() -> Self {\n        PositionComponent::Length(LengthPercentage::Percentage(Percentage::zero()))\n    }\n\n    /// Returns the count of this component.\n    fn component_count(&self) -> usize {\n        match *self {\n            PositionComponent::Length(..) | PositionComponent::Center => 1,\n            PositionComponent::Side(_, ref lp) => {\n                if lp.is_some() {\n                    2\n                } else {\n                    1\n                }\n            },\n        }\n    }\n}\n\nimpl<S: Side> ToComputedValue for PositionComponent<S> {\n    type ComputedValue = ComputedLengthPercentage;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            PositionComponent::Center => ComputedLengthPercentage::new_percent(Percentage(0.5)),\n            PositionComponent::Side(ref keyword, None) => {\n                let p = Percentage(if keyword.is_start() { 0. } else { 1. });\n                ComputedLengthPercentage::new_percent(p)\n            },\n            PositionComponent::Side(ref keyword, Some(ref length)) if !keyword.is_start() => {\n                let length = length.to_computed_value(context);\n                // We represent `<end-side> <length>` as `calc(100% - <length>)`.\n                ComputedLengthPercentage::hundred_percent_minus(length, AllowedNumericType::All)\n            },\n            PositionComponent::Side(_, Some(ref length))\n            | PositionComponent::Length(ref length) => length.to_computed_value(context),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        PositionComponent::Length(ToComputedValue::from_computed_value(computed))\n    }\n}\n\nimpl<S: Side> PositionComponent<S> {\n    /// The initial specified value of a position component, i.e. the start side.\n    pub fn initial_specified_value() -> Self {\n        PositionComponent::Side(S::start(), None)\n    }\n}\n\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-anchor-name\n#[derive(\n    Animate,\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(comma)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct AnchorNameIdent(\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    #[animation(constant)]\n    pub crate::ArcSlice<DashedIdent>,\n);\n\nimpl AnchorNameIdent {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self(Default::default())\n    }\n}\n\nimpl Parse for AnchorNameIdent {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let first = input.expect_ident()?;\n        if first.eq_ignore_ascii_case(\"none\") {\n            return Ok(Self::none());\n        }\n        // The common case is probably just to have a single anchor name, so\n        // space for four on the stack should be plenty.\n        let mut idents: SmallVec<[DashedIdent; 4]> =\n            smallvec![DashedIdent::from_ident(location, first,)?];\n        while input.try_parse(|input| input.expect_comma()).is_ok() {\n            idents.push(DashedIdent::parse(context, input)?);\n        }\n        Ok(AnchorNameIdent(ArcSlice::from_iter(idents.drain(..))))\n    }\n}\n\nimpl IsTreeScoped for AnchorNameIdent {\n    fn is_tree_scoped(&self) -> bool {\n        !self.0.is_empty()\n    }\n}\n\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-anchor-name\npub type AnchorName = TreeScoped<AnchorNameIdent>;\n\nimpl AnchorName {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self::with_default_level(AnchorNameIdent::none())\n    }\n}\n\n/// List of scoped names, or none.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[css(comma)]\n#[typed(todo_derive_fields)]\npub struct ScopedNameList(\n    /// `none | all | <dashed-ident>#`\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    crate::ArcSlice<AtomIdent>,\n);\n\nimpl ScopedNameList {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self(crate::ArcSlice::default())\n    }\n\n    /// Whether we're the `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Return the `all` value.\n    pub fn all() -> Self {\n        static ALL: std::sync::LazyLock<ScopedNameList> = std::sync::LazyLock::new(|| {\n            ScopedNameList(crate::ArcSlice::from_iter_leaked(std::iter::once(\n                AtomIdent::new(atom!(\"all\")),\n            )))\n        });\n        ALL.clone()\n    }\n}\n\nimpl Parse for ScopedNameList {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let first = input.expect_ident()?;\n        if first.eq_ignore_ascii_case(\"none\") {\n            return Ok(Self::none());\n        }\n        if first.eq_ignore_ascii_case(\"all\") {\n            return Ok(Self::all());\n        }\n        // Authors using more than a handful of anchored elements is likely\n        // uncommon, so we only pre-allocate for 8 on the stack here.\n        let mut idents = SmallVec::<[AtomIdent; 8]>::new();\n        idents.push(AtomIdent::new(DashedIdent::from_ident(location, first)?.0));\n        while input.try_parse(|input| input.expect_comma()).is_ok() {\n            idents.push(AtomIdent::new(DashedIdent::parse(context, input)?.0));\n        }\n        Ok(Self(ArcSlice::from_iter(idents.drain(..))))\n    }\n}\n\nimpl IsTreeScoped for ScopedNameList {\n    fn is_tree_scoped(&self) -> bool {\n        !self.is_none()\n    }\n}\n\n/// A scoped name type, such as:\n/// * https://drafts.csswg.org/css-anchor-position-1/#propdef-scope\npub type ScopedName = TreeScoped<ScopedNameList>;\n\nimpl ScopedName {\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self::with_default_level(ScopedNameList::none())\n    }\n\n    /// Returns true if no scoped name is specified.\n    pub fn is_none(&self) -> bool {\n        self.value.is_none()\n    }\n}\n\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-anchor\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n#[typed(todo_derive_fields)]\npub enum PositionAnchorKeyword {\n    /// `normal`\n    Normal,\n    /// `none`\n    None,\n    /// `auto`\n    Auto,\n    /// `<dashed-ident>`\n    Ident(DashedIdent),\n}\n\n\nimpl IsTreeScoped for PositionAnchorKeyword {\n    fn is_tree_scoped(&self) -> bool {\n        match *self {\n            Self::Normal | Self::None | Self::Auto => false,\n            Self::Ident(_) => true,\n        }\n    }\n}\n\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-anchor\npub type PositionAnchor = TreeScoped<PositionAnchorKeyword>;\n\nimpl PositionAnchor {\n    /// Return the `normal` value.\n    pub fn normal() -> Self {\n        Self::with_default_level(PositionAnchorKeyword::Normal)\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\n/// How to swap values for the automatically-generated position tactic.\npub enum PositionTryFallbacksTryTacticKeyword {\n    /// Swap the values in the block axis.\n    FlipBlock,\n    /// Swap the values in the inline axis.\n    FlipInline,\n    /// Swap the values in the start properties.\n    FlipStart,\n    /// Swap the values in the X axis.\n    FlipX,\n    /// Swap the values in the Y axis.\n    FlipY,\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\n/// Changes for the automatically-generated position option.\n/// Note that this is order-dependent - e.g. `flip-start flip-inline` != `flip-inline flip-start`.\n///\n/// https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-fallbacks-try-tactic\npub struct PositionTryFallbacksTryTactic(\n    #[css(iterable)] pub ThinVec<PositionTryFallbacksTryTacticKeyword>,\n);\n\nimpl Parse for PositionTryFallbacksTryTactic {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut result = ThinVec::with_capacity(5);\n        // Collect up to 5 keywords, disallowing duplicates.\n        for _ in 0..5 {\n            if let Ok(kw) = input.try_parse(PositionTryFallbacksTryTacticKeyword::parse) {\n                if result.contains(&kw) {\n                    return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                }\n                result.push(kw);\n            } else {\n                break;\n            }\n        }\n        if result.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        Ok(Self(result))\n    }\n}\n\nimpl PositionTryFallbacksTryTactic {\n    /// Returns whether there's any tactic.\n    #[inline]\n    pub fn is_empty(&self) -> bool {\n        self.0.is_empty()\n    }\n\n    /// Iterates over the fallbacks in order.\n    #[inline]\n    pub fn iter(&self) -> impl Iterator<Item = &PositionTryFallbacksTryTacticKeyword> {\n        self.0.iter()\n    }\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-try-fallbacks\n/// <dashed-ident> || <try-tactic>\npub struct DashedIdentAndOrTryTactic {\n    /// `<dashed-ident>`\n    pub ident: DashedIdent,\n    /// `<try-tactic>`\n    pub try_tactic: PositionTryFallbacksTryTactic,\n}\n\nimpl Parse for DashedIdentAndOrTryTactic {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut result = Self {\n            ident: DashedIdent::empty(),\n            try_tactic: PositionTryFallbacksTryTactic::default(),\n        };\n\n        loop {\n            if result.ident.is_empty() {\n                if let Ok(ident) = input.try_parse(|i| DashedIdent::parse(context, i)) {\n                    result.ident = ident;\n                    continue;\n                }\n            }\n            if result.try_tactic.is_empty() {\n                if let Ok(try_tactic) =\n                    input.try_parse(|i| PositionTryFallbacksTryTactic::parse(context, i))\n                {\n                    result.try_tactic = try_tactic;\n                    continue;\n                }\n            }\n            break;\n        }\n\n        if result.ident.is_empty() && result.try_tactic.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        return Ok(result);\n    }\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-try-fallbacks\n/// [ [<dashed-ident> || <try-tactic>] | <'position-area'> ]\npub enum PositionTryFallbacksItem {\n    /// `<dashed-ident> || <try-tactic>`\n    IdentAndOrTactic(DashedIdentAndOrTryTactic),\n    #[parse(parse_fn = \"PositionArea::parse_except_none\")]\n    /// `<position-area>`\n    PositionArea(PositionArea),\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(comma)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// https://drafts.csswg.org/css-anchor-position-1/#position-try-fallbacks\npub struct PositionTryFallbacks(\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    pub crate::ArcSlice<PositionTryFallbacksItem>,\n);\n\nimpl PositionTryFallbacks {\n    #[inline]\n    /// Return the `none` value.\n    pub fn none() -> Self {\n        Self(Default::default())\n    }\n\n    /// Returns whether this is the `none` value.\n    pub fn is_none(&self) -> bool {\n        self.0.is_empty()\n    }\n}\n\nimpl Parse for PositionTryFallbacks {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(Self::none());\n        }\n        // The common case is unlikely to include many alternate positioning\n        // styles, so space for four on the stack should typically be enough.\n        let mut items: SmallVec<[PositionTryFallbacksItem; 4]> =\n            smallvec![PositionTryFallbacksItem::parse(context, input)?];\n        while input.try_parse(|input| input.expect_comma()).is_ok() {\n            items.push(PositionTryFallbacksItem::parse(context, input)?);\n        }\n        Ok(Self(ArcSlice::from_iter(items.drain(..))))\n    }\n}\n\n/// https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum PositionTryOrder {\n    #[default]\n    /// `normal`\n    Normal,\n    /// `most-width`\n    MostWidth,\n    /// `most-height`\n    MostHeight,\n    /// `most-block-size`\n    MostBlockSize,\n    /// `most-inline-size`\n    MostInlineSize,\n}\n\nimpl PositionTryOrder {\n    #[inline]\n    /// Return the `auto` value.\n    pub fn normal() -> Self {\n        Self::Normal\n    }\n\n    /// Returns whether this is the `auto` value.\n    pub fn is_normal(&self) -> bool {\n        *self == Self::Normal\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(single = \"always\", mixed = \"anchors-valid,anchors-visible,no-overflow\"))]\n#[repr(C)]\n/// Specified keyword values for the position-visibility property.\npub struct PositionVisibility(u8);\nbitflags! {\n    impl PositionVisibility: u8 {\n        /// Element is displayed without regard for its anchors or its overflowing status.\n        const ALWAYS = 0;\n        /// anchors-valid\n        const ANCHORS_VALID = 1 << 0;\n        /// anchors-visible\n        const ANCHORS_VISIBLE = 1 << 1;\n        /// no-overflow\n        const NO_OVERFLOW = 1 << 2;\n    }\n}\n\nimpl Default for PositionVisibility {\n    fn default() -> Self {\n        Self::ALWAYS\n    }\n}\n\nimpl PositionVisibility {\n    #[inline]\n    /// Returns the initial value of position-visibility\n    pub fn always() -> Self {\n        Self::ALWAYS\n    }\n}\n\n/// A value indicating which high level group in the formal grammar a\n/// PositionAreaKeyword or PositionArea belongs to.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum PositionAreaType {\n    /// X || Y\n    Physical,\n    /// block || inline\n    Logical,\n    /// self-block || self-inline\n    SelfLogical,\n    /// start|end|span-* {1,2}\n    Inferred,\n    /// self-start|self-end|span-self-* {1,2}\n    SelfInferred,\n    /// center, span-all\n    Common,\n    /// none\n    None,\n}\n\n/// A three-bit value that represents the axis in which position-area operates on.\n/// Represented as 4 bits: axis type (physical or logical), direction type (physical or logical),\n/// axis value.\n///\n/// There are two special values on top (Inferred and None) that represent ambiguous or axis-less\n/// keywords, respectively.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]\n#[allow(missing_docs)]\npub enum PositionAreaAxis {\n    Horizontal = 0b000,\n    Vertical = 0b001,\n\n    X = 0b010,\n    Y = 0b011,\n\n    Block = 0b110,\n    Inline = 0b111,\n\n    Inferred = 0b100,\n    None = 0b101,\n}\n\nimpl PositionAreaAxis {\n    /// Whether this axis is physical or not.\n    pub fn is_physical(self) -> bool {\n        (self as u8 & 0b100) == 0\n    }\n\n    /// Whether the direction is logical or not.\n    fn is_flow_relative_direction(self) -> bool {\n        self == Self::Inferred || (self as u8 & 0b10) != 0\n    }\n\n    /// Whether this axis goes first in the canonical syntax.\n    fn is_canonically_first(self) -> bool {\n        self != Self::Inferred && (self as u8) & 1 == 0\n    }\n\n    #[allow(unused)]\n    fn flip(self) -> Self {\n        if matches!(self, Self::Inferred | Self::None) {\n            return self;\n        }\n        Self::from_u8(self as u8 ^ 1u8).unwrap()\n    }\n\n    fn to_logical(self, wm: WritingMode, inferred: LogicalAxis) -> Option<LogicalAxis> {\n        Some(match self {\n            PositionAreaAxis::Horizontal | PositionAreaAxis::X => {\n                if wm.is_vertical() {\n                    LogicalAxis::Block\n                } else {\n                    LogicalAxis::Inline\n                }\n            },\n            PositionAreaAxis::Vertical | PositionAreaAxis::Y => {\n                if wm.is_vertical() {\n                    LogicalAxis::Inline\n                } else {\n                    LogicalAxis::Block\n                }\n            },\n            PositionAreaAxis::Block => LogicalAxis::Block,\n            PositionAreaAxis::Inline => LogicalAxis::Inline,\n            PositionAreaAxis::Inferred => inferred,\n            PositionAreaAxis::None => return None,\n        })\n    }\n}\n\n/// Specifies which tracks(s) on the axis that the position-area span occupies.\n/// Represented as 3 bits: start, center, end track.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)]\npub enum PositionAreaTrack {\n    /// First track\n    Start = 0b001,\n    /// First and center.\n    SpanStart = 0b011,\n    /// Last track.\n    End = 0b100,\n    /// Last and center.\n    SpanEnd = 0b110,\n    /// Center track.\n    Center = 0b010,\n    /// All tracks\n    SpanAll = 0b111,\n}\n\nimpl PositionAreaTrack {\n    fn flip(self) -> Self {\n        match self {\n            Self::Start => Self::End,\n            Self::SpanStart => Self::SpanEnd,\n            Self::End => Self::Start,\n            Self::SpanEnd => Self::SpanStart,\n            Self::Center | Self::SpanAll => self,\n        }\n    }\n\n    fn start(self) -> bool {\n        self as u8 & 1 != 0\n    }\n}\n\n/// The shift to the left needed to set the axis.\npub const AXIS_SHIFT: usize = 3;\n/// The mask used to extract the axis.\npub const AXIS_MASK: u8 = 0b111u8 << AXIS_SHIFT;\n/// The mask used to extract the track.\npub const TRACK_MASK: u8 = 0b111u8;\n/// The self-wm bit.\npub const SELF_WM: u8 = 1u8 << 6;\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    FromPrimitive,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\n/// Possible values for the `position-area` property's keywords.\n/// Represented by [0z xxx yyy], where z means \"self wm resolution\", xxxx is the axis (as in\n/// PositionAreaAxis) and yyy is the PositionAreaTrack\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-area\npub enum PositionAreaKeyword {\n    #[default]\n    None = (PositionAreaAxis::None as u8) << AXIS_SHIFT,\n\n    // Common (shared) keywords:\n    Center = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::Center as u8,\n    SpanAll = ((PositionAreaAxis::None as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanAll as u8,\n\n    // Inferred-axis edges:\n    Start = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    End = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n    SpanStart =\n        ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanEnd = ((PositionAreaAxis::Inferred as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n\n    // Purely physical edges:\n    Left = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    Right = ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n    Top = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    Bottom = ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n\n    // Flow-relative physical-axis edges:\n    XStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    XEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n    YStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    YEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n\n    // Logical edges:\n    BlockStart = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    BlockEnd = ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n    InlineStart = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::Start as u8,\n    InlineEnd = ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::End as u8,\n\n    // Composite values with Span:\n    SpanLeft =\n        ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanRight =\n        ((PositionAreaAxis::Horizontal as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n    SpanTop =\n        ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanBottom =\n        ((PositionAreaAxis::Vertical as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n\n    // Flow-relative physical-axis edges:\n    SpanXStart = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanXEnd = ((PositionAreaAxis::X as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n    SpanYStart = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanYEnd = ((PositionAreaAxis::Y as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n\n    // Logical edges:\n    SpanBlockStart =\n        ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanBlockEnd =\n        ((PositionAreaAxis::Block as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n    SpanInlineStart =\n        ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanStart as u8,\n    SpanInlineEnd =\n        ((PositionAreaAxis::Inline as u8) << AXIS_SHIFT) | PositionAreaTrack::SpanEnd as u8,\n\n    // Values using the Self element's writing-mode:\n    SelfStart = SELF_WM | (Self::Start as u8),\n    SelfEnd = SELF_WM | (Self::End as u8),\n    SpanSelfStart = SELF_WM | (Self::SpanStart as u8),\n    SpanSelfEnd = SELF_WM | (Self::SpanEnd as u8),\n\n    SelfXStart = SELF_WM | (Self::XStart as u8),\n    SelfXEnd = SELF_WM | (Self::XEnd as u8),\n    SelfYStart = SELF_WM | (Self::YStart as u8),\n    SelfYEnd = SELF_WM | (Self::YEnd as u8),\n    SelfBlockStart = SELF_WM | (Self::BlockStart as u8),\n    SelfBlockEnd = SELF_WM | (Self::BlockEnd as u8),\n    SelfInlineStart = SELF_WM | (Self::InlineStart as u8),\n    SelfInlineEnd = SELF_WM | (Self::InlineEnd as u8),\n\n    SpanSelfXStart = SELF_WM | (Self::SpanXStart as u8),\n    SpanSelfXEnd = SELF_WM | (Self::SpanXEnd as u8),\n    SpanSelfYStart = SELF_WM | (Self::SpanYStart as u8),\n    SpanSelfYEnd = SELF_WM | (Self::SpanYEnd as u8),\n    SpanSelfBlockStart = SELF_WM | (Self::SpanBlockStart as u8),\n    SpanSelfBlockEnd = SELF_WM | (Self::SpanBlockEnd as u8),\n    SpanSelfInlineStart = SELF_WM | (Self::SpanInlineStart as u8),\n    SpanSelfInlineEnd = SELF_WM | (Self::SpanInlineEnd as u8),\n}\n\nimpl PositionAreaKeyword {\n    /// Returns the 'none' value.\n    #[inline]\n    pub fn none() -> Self {\n        Self::None\n    }\n\n    /// Returns true if this is the none keyword.\n    pub fn is_none(&self) -> bool {\n        *self == Self::None\n    }\n\n    /// Whether we're one of the self-wm keywords.\n    pub fn self_wm(self) -> bool {\n        (self as u8 & SELF_WM) != 0\n    }\n\n    /// Get this keyword's axis.\n    pub fn axis(self) -> PositionAreaAxis {\n        PositionAreaAxis::from_u8((self as u8 >> AXIS_SHIFT) & 0b111).unwrap()\n    }\n\n    /// Returns this keyword but with the axis swapped by the argument.\n    pub fn with_axis(self, axis: PositionAreaAxis) -> Self {\n        Self::from_u8(((self as u8) & !AXIS_MASK) | ((axis as u8) << AXIS_SHIFT)).unwrap()\n    }\n\n    /// If this keyword uses an inferred axis, replaces it.\n    pub fn with_inferred_axis(self, axis: PositionAreaAxis) -> Self {\n        if self.axis() == PositionAreaAxis::Inferred {\n            self.with_axis(axis)\n        } else {\n            self\n        }\n    }\n\n    /// Get this keyword's track, or None if we're the `None` keyword.\n    pub fn track(self) -> Option<PositionAreaTrack> {\n        let result = PositionAreaTrack::from_u8(self as u8 & TRACK_MASK);\n        debug_assert_eq!(\n            result.is_none(),\n            self.is_none(),\n            \"Only the none keyword has no track\"\n        );\n        result\n    }\n\n    fn group_type(self) -> PositionAreaType {\n        let axis = self.axis();\n        if axis == PositionAreaAxis::None {\n            if self.is_none() {\n                return PositionAreaType::None;\n            }\n            return PositionAreaType::Common;\n        }\n        if axis == PositionAreaAxis::Inferred {\n            return if self.self_wm() {\n                PositionAreaType::SelfInferred\n            } else {\n                PositionAreaType::Inferred\n            };\n        }\n        if axis.is_physical() {\n            return PositionAreaType::Physical;\n        }\n        if self.self_wm() {\n            PositionAreaType::SelfLogical\n        } else {\n            PositionAreaType::Logical\n        }\n    }\n\n    fn to_physical(\n        self,\n        cb_wm: WritingMode,\n        self_wm: WritingMode,\n        inferred_axis: LogicalAxis,\n    ) -> Self {\n        let wm = if self.self_wm() { self_wm } else { cb_wm };\n        let axis = self.axis();\n        if !axis.is_flow_relative_direction() {\n            return self;\n        }\n        let Some(logical_axis) = axis.to_logical(wm, inferred_axis) else {\n            return self;\n        };\n        let Some(track) = self.track() else {\n            debug_assert!(false, \"How did we end up with no track here? {self:?}\");\n            return self;\n        };\n        let start = track.start();\n        let logical_side = match logical_axis {\n            LogicalAxis::Block => {\n                if start {\n                    LogicalSide::BlockStart\n                } else {\n                    LogicalSide::BlockEnd\n                }\n            },\n            LogicalAxis::Inline => {\n                if start {\n                    LogicalSide::InlineStart\n                } else {\n                    LogicalSide::InlineEnd\n                }\n            },\n        };\n        let physical_side = logical_side.to_physical(wm);\n        let physical_start = matches!(physical_side, PhysicalSide::Top | PhysicalSide::Left);\n        let new_track = if physical_start != start {\n            track.flip()\n        } else {\n            track\n        };\n        let new_axis = if matches!(physical_side, PhysicalSide::Top | PhysicalSide::Bottom) {\n            PositionAreaAxis::Vertical\n        } else {\n            PositionAreaAxis::Horizontal\n        };\n        Self::from_u8(new_track as u8 | ((new_axis as u8) << AXIS_SHIFT)).unwrap()\n    }\n\n    fn flip_track(self) -> Self {\n        let Some(old_track) = self.track() else {\n            return self;\n        };\n        let new_track = old_track.flip();\n        Self::from_u8((self as u8 & !TRACK_MASK) | new_track as u8).unwrap()\n    }\n\n    /// Returns a value for the self-alignment properties in order to resolve\n    /// `normal`, in terms of the containing block's writing mode.\n    ///\n    /// Note that the caller must have converted the position-area to physical\n    /// values.\n    ///\n    /// <https://drafts.csswg.org/css-anchor-position/#position-area-alignment>\n    pub fn to_self_alignment(self, axis: LogicalAxis, cb_wm: &WritingMode) -> Option<AlignFlags> {\n        let track = self.track()?;\n        Some(match track {\n            // \"If the only the center track in an axis is selected, the default alignment in that axis is center.\"\n            PositionAreaTrack::Center => AlignFlags::CENTER,\n            // \"If all three tracks are selected, the default alignment in that axis is anchor-center.\"\n            PositionAreaTrack::SpanAll => AlignFlags::ANCHOR_CENTER,\n            // \"Otherwise, the default alignment in that axis is toward the non-specified side track: if it’s\n            // specifying the “start” track of its axis, the default alignment in that axis is end; etc.\"\n            _ => {\n                debug_assert_eq!(self.group_type(), PositionAreaType::Physical);\n                if axis == LogicalAxis::Inline {\n                    // For the inline axis, map 'start' to 'end' unless the axis is inline-reversed,\n                    // meaning that its logical flow is counter to physical coordinates and therefore\n                    // physical 'start' already corresponds to logical 'end'.\n                    if track.start() == cb_wm.intersects(WritingMode::INLINE_REVERSED) {\n                        AlignFlags::START\n                    } else {\n                        AlignFlags::END\n                    }\n                } else {\n                    // For the block axis, only vertical-rl has reversed flow and therefore\n                    // does not map 'start' to 'end' here.\n                    if track.start() == cb_wm.is_vertical_rl() {\n                        AlignFlags::START\n                    } else {\n                        AlignFlags::END\n                    }\n                }\n            },\n        })\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// https://drafts.csswg.org/css-anchor-position-1/#propdef-position-area\npub struct PositionArea {\n    /// First keyword, if any.\n    pub first: PositionAreaKeyword,\n    /// Second keyword, if any.\n    #[css(skip_if = \"PositionAreaKeyword::is_none\")]\n    pub second: PositionAreaKeyword,\n}\n\nimpl PositionArea {\n    /// Returns the none value.\n    #[inline]\n    pub fn none() -> Self {\n        Self {\n            first: PositionAreaKeyword::None,\n            second: PositionAreaKeyword::None,\n        }\n    }\n\n    /// Returns whether we're the none value.\n    #[inline]\n    pub fn is_none(&self) -> bool {\n        self.first.is_none()\n    }\n\n    /// Parses a <position-area> without allowing `none`.\n    pub fn parse_except_none<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, /*allow_none*/ false)\n    }\n\n    /// Get the high-level grammar group of this.\n    pub fn get_type(&self) -> PositionAreaType {\n        let first = self.first.group_type();\n        let second = self.second.group_type();\n        if matches!(second, PositionAreaType::None | PositionAreaType::Common) {\n            return first;\n        }\n        if first == PositionAreaType::Common {\n            return second;\n        }\n        if first != second {\n            return PositionAreaType::None;\n        }\n        let first_axis = self.first.axis();\n        if first_axis != PositionAreaAxis::Inferred\n            && first_axis.is_canonically_first() == self.second.axis().is_canonically_first()\n        {\n            return PositionAreaType::None;\n        }\n        first\n    }\n\n    fn parse_internal<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_none: bool,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut location = input.current_source_location();\n        let mut first = PositionAreaKeyword::parse(input)?;\n        if first.is_none() {\n            if allow_none {\n                return Ok(Self::none());\n            }\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        location = input.current_source_location();\n        let second = input.try_parse(PositionAreaKeyword::parse);\n        if let Ok(PositionAreaKeyword::None) = second {\n            // `none` is only allowed as a single value\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        let mut second = second.unwrap_or(PositionAreaKeyword::None);\n        if second.is_none() {\n            // Either there was no second keyword and try_parse returned a\n            // BasicParseErrorKind::EndOfInput, or else the second \"keyword\"\n            // was invalid. We assume the former case here, and if it's the\n            // latter case then our caller detects the error (try_parse will,\n            // have rewound, leaving an unparsed token).\n            return Ok(Self { first, second });\n        }\n\n        let pair_type = Self { first, second }.get_type();\n        if pair_type == PositionAreaType::None {\n            // Mismatched types or what not.\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        // For types that have a canonical order, remove 'span-all' (the default behavior;\n        // unnecessary for keyword pairs with a known order).\n        if matches!(\n            pair_type,\n            PositionAreaType::Physical | PositionAreaType::Logical | PositionAreaType::SelfLogical\n        ) {\n            if second == PositionAreaKeyword::SpanAll {\n                // Span-all is the default behavior, so specifying `span-all` is\n                // superfluous.\n                second = PositionAreaKeyword::None;\n            } else if first == PositionAreaKeyword::SpanAll {\n                first = second;\n                second = PositionAreaKeyword::None;\n            }\n        }\n        if first == second {\n            second = PositionAreaKeyword::None;\n        }\n        let mut result = Self { first, second };\n        result.canonicalize_order();\n        Ok(result)\n    }\n\n    fn canonicalize_order(&mut self) {\n        let first_axis = self.first.axis();\n        if first_axis.is_canonically_first() || self.second.is_none() {\n            return;\n        }\n        let second_axis = self.second.axis();\n        if first_axis == second_axis {\n            // Inferred or axis-less keywords.\n            return;\n        }\n        if second_axis.is_canonically_first()\n            || (second_axis == PositionAreaAxis::None && first_axis != PositionAreaAxis::Inferred)\n        {\n            std::mem::swap(&mut self.first, &mut self.second);\n        }\n    }\n\n    fn make_missing_second_explicit(&mut self) {\n        if !self.second.is_none() {\n            return;\n        }\n        let axis = self.first.axis();\n        if matches!(axis, PositionAreaAxis::Inferred | PositionAreaAxis::None) {\n            self.second = self.first;\n            return;\n        }\n        self.second = PositionAreaKeyword::SpanAll;\n        if !axis.is_canonically_first() {\n            std::mem::swap(&mut self.first, &mut self.second);\n        }\n    }\n\n    /// Turns this <position-area> value into a physical <position-area>.\n    pub fn to_physical(mut self, cb_wm: WritingMode, self_wm: WritingMode) -> Self {\n        self.make_missing_second_explicit();\n        // If both axes are None, to_physical and canonicalize_order are not useful.\n        // The first value refers to the block axis, the second to the inline axis;\n        // but as a physical type, they will be interpreted as the x- and y-axis\n        // respectively, so if the writing mode is horizontal we need to swap the\n        // values (block -> y, inline -> x).\n        if self.first.axis() == PositionAreaAxis::None\n            && self.second.axis() == PositionAreaAxis::None\n            && !cb_wm.is_vertical()\n        {\n            std::mem::swap(&mut self.first, &mut self.second);\n        } else {\n            self.first = self.first.to_physical(cb_wm, self_wm, LogicalAxis::Block);\n            self.second = self.second.to_physical(cb_wm, self_wm, LogicalAxis::Inline);\n            self.canonicalize_order();\n        }\n        self\n    }\n\n    fn flip_logical_axis(&mut self, wm: WritingMode, axis: LogicalAxis) {\n        if self.first.axis().to_logical(wm, LogicalAxis::Block) == Some(axis) {\n            self.first = self.first.flip_track();\n        } else {\n            self.second = self.second.flip_track();\n        }\n    }\n\n    fn flip_start(&mut self) {\n        self.first = self.first.with_axis(self.first.axis().flip());\n        self.second = self.second.with_axis(self.second.axis().flip());\n    }\n\n    /// Applies a try tactic to this `<position-area>` value.\n    pub fn with_tactic(\n        mut self,\n        wm: WritingMode,\n        tactic: PositionTryFallbacksTryTacticKeyword,\n    ) -> Self {\n        self.make_missing_second_explicit();\n        let axis_to_flip = match tactic {\n            PositionTryFallbacksTryTacticKeyword::FlipStart => {\n                self.flip_start();\n                return self;\n            },\n            PositionTryFallbacksTryTacticKeyword::FlipBlock => LogicalAxis::Block,\n            PositionTryFallbacksTryTacticKeyword::FlipInline => LogicalAxis::Inline,\n            PositionTryFallbacksTryTacticKeyword::FlipX => {\n                if wm.is_horizontal() {\n                    LogicalAxis::Inline\n                } else {\n                    LogicalAxis::Block\n                }\n            },\n            PositionTryFallbacksTryTacticKeyword::FlipY => {\n                if wm.is_vertical() {\n                    LogicalAxis::Inline\n                } else {\n                    LogicalAxis::Block\n                }\n            },\n        };\n        self.flip_logical_axis(wm, axis_to_flip);\n        self\n    }\n}\n\nimpl Parse for PositionArea {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, /* allow_none = */ true)\n    }\n}\n\n/// Represents a side, either horizontal or vertical, of a CSS position.\npub trait Side {\n    /// Returns the start side.\n    fn start() -> Self;\n\n    /// Returns whether this side is the start side.\n    fn is_start(&self) -> bool;\n}\n\nimpl Side for HorizontalPositionKeyword {\n    #[inline]\n    fn start() -> Self {\n        HorizontalPositionKeyword::Left\n    }\n\n    #[inline]\n    fn is_start(&self) -> bool {\n        *self == Self::start()\n    }\n}\n\nimpl Side for VerticalPositionKeyword {\n    #[inline]\n    fn start() -> Self {\n        VerticalPositionKeyword::Top\n    }\n\n    #[inline]\n    fn is_start(&self) -> bool {\n        *self == Self::start()\n    }\n}\n\n/// Controls how the auto-placement algorithm works specifying exactly how auto-placed items\n/// get flowed into the grid: [ row | column ] || dense\n/// https://drafts.csswg.org/css-grid-2/#grid-auto-flow-property\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    mixed = \"row,column,dense\",\n    validate_mixed = \"Self::validate_and_simplify\"\n))]\n#[repr(C)]\npub struct GridAutoFlow(u8);\nbitflags! {\n    impl GridAutoFlow: u8 {\n        /// 'row' - mutually exclusive with 'column'\n        const ROW = 1 << 0;\n        /// 'column' - mutually exclusive with 'row'\n        const COLUMN = 1 << 1;\n        /// 'dense'\n        const DENSE = 1 << 2;\n    }\n}\n\nimpl GridAutoFlow {\n    /// [ row | column ] || dense\n    fn validate_and_simplify(&mut self) -> bool {\n        if self.contains(Self::ROW | Self::COLUMN) {\n            // row and column are mutually exclusive.\n            return false;\n        }\n        if *self == Self::DENSE {\n            // If there's no column, default to row.\n            self.insert(Self::ROW);\n        }\n        true\n    }\n}\n\nimpl ToCss for GridAutoFlow {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let dense = self.intersects(Self::DENSE);\n        if self.intersects(Self::ROW) {\n            return if dense {\n                dest.write_str(\"dense\")\n            } else {\n                dest.write_str(\"row\")\n            };\n        }\n        debug_assert!(self.intersects(Self::COLUMN));\n        if dense {\n            dest.write_str(\"column dense\")\n        } else {\n            dest.write_str(\"column\")\n        }\n    }\n}\n\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n/// Masonry auto-placement algorithm packing.\npub enum MasonryPlacement {\n    /// Place the item in the track(s) with the smallest extent so far.\n    Pack,\n    /// Place the item after the last item, from start to end.\n    Next,\n}\n\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n/// Masonry auto-placement algorithm item sorting option.\npub enum MasonryItemOrder {\n    /// Place all items with a definite placement before auto-placed items.\n    DefiniteFirst,\n    /// Place items in `order-modified document order`.\n    Ordered,\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// Controls how the Masonry layout algorithm works\n/// specifying exactly how auto-placed items get flowed in the masonry axis.\npub struct MasonryAutoFlow {\n    /// Specify how to pick a auto-placement track.\n    #[css(contextual_skip_if = \"is_pack_with_non_default_order\")]\n    pub placement: MasonryPlacement,\n    /// Specify how to pick an item to place.\n    #[css(skip_if = \"is_item_order_definite_first\")]\n    pub order: MasonryItemOrder,\n}\n\n#[inline]\nfn is_pack_with_non_default_order(placement: &MasonryPlacement, order: &MasonryItemOrder) -> bool {\n    *placement == MasonryPlacement::Pack && *order != MasonryItemOrder::DefiniteFirst\n}\n\n#[inline]\nfn is_item_order_definite_first(order: &MasonryItemOrder) -> bool {\n    *order == MasonryItemOrder::DefiniteFirst\n}\n\nimpl MasonryAutoFlow {\n    #[inline]\n    /// Get initial `masonry-auto-flow` value.\n    pub fn initial() -> MasonryAutoFlow {\n        MasonryAutoFlow {\n            placement: MasonryPlacement::Pack,\n            order: MasonryItemOrder::DefiniteFirst,\n        }\n    }\n}\n\nimpl Parse for MasonryAutoFlow {\n    /// [ definite-first | ordered ] || [ pack | next ]\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<MasonryAutoFlow, ParseError<'i>> {\n        let mut value = MasonryAutoFlow::initial();\n        let mut got_placement = false;\n        let mut got_order = false;\n        while !input.is_exhausted() {\n            let location = input.current_source_location();\n            let ident = input.expect_ident()?;\n            let success = match_ignore_ascii_case! { &ident,\n                \"pack\" if !got_placement => {\n                    got_placement = true;\n                    true\n                },\n                \"next\" if !got_placement => {\n                    value.placement = MasonryPlacement::Next;\n                    got_placement = true;\n                    true\n                },\n                \"definite-first\" if !got_order => {\n                    got_order = true;\n                    true\n                },\n                \"ordered\" if !got_order => {\n                    value.order = MasonryItemOrder::Ordered;\n                    got_order = true;\n                    true\n                },\n                _ => false\n            };\n            if !success {\n                return Err(location\n                    .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone())));\n            }\n        }\n\n        if got_placement || got_order {\n            Ok(value)\n        } else {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\n/// https://drafts.csswg.org/css-grid/#named-grid-area\npub struct TemplateAreas {\n    /// `named area` containing for each template area\n    #[css(skip)]\n    pub areas: crate::OwnedSlice<NamedArea>,\n    /// The simplified CSS strings for serialization purpose.\n    /// https://drafts.csswg.org/css-grid/#serialize-template\n    // Note: We also use the length of `strings` when computing the explicit grid end line number\n    // (i.e. row number).\n    #[css(iterable)]\n    pub strings: crate::OwnedSlice<crate::OwnedStr>,\n    /// The number of columns of the grid.\n    #[css(skip)]\n    pub width: u32,\n}\n\n/// Parser for grid template areas.\n#[derive(Default)]\npub struct TemplateAreasParser {\n    areas: Vec<NamedArea>,\n    area_indices: PrecomputedHashMap<Atom, usize>,\n    strings: Vec<crate::OwnedStr>,\n    width: u32,\n    row: u32,\n}\n\nimpl TemplateAreasParser {\n    /// Parse a single string.\n    pub fn try_parse_string<'i>(\n        &mut self,\n        input: &mut Parser<'i, '_>,\n    ) -> Result<(), ParseError<'i>> {\n        input.try_parse(|input| {\n            self.parse_string(input.expect_string()?)\n                .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        })\n    }\n\n    /// Parse a single string.\n    fn parse_string(&mut self, string: &str) -> Result<(), ()> {\n        self.row += 1;\n        let mut simplified_string = String::new();\n        let mut current_area_index: Option<usize> = None;\n        let mut column = 0u32;\n        for token in TemplateAreasTokenizer(string) {\n            column += 1;\n            if column > 1 {\n                simplified_string.push(' ');\n            }\n            let name = if let Some(token) = token? {\n                simplified_string.push_str(token);\n                Atom::from(token)\n            } else {\n                if let Some(index) = current_area_index.take() {\n                    if self.areas[index].columns.end != column {\n                        return Err(());\n                    }\n                }\n                simplified_string.push('.');\n                continue;\n            };\n            if let Some(index) = current_area_index {\n                if self.areas[index].name == name {\n                    if self.areas[index].rows.start == self.row {\n                        self.areas[index].columns.end += 1;\n                    }\n                    continue;\n                }\n                if self.areas[index].columns.end != column {\n                    return Err(());\n                }\n            }\n            match self.area_indices.entry(name) {\n                Entry::Occupied(ref e) => {\n                    let index = *e.get();\n                    if self.areas[index].columns.start != column\n                        || self.areas[index].rows.end != self.row\n                    {\n                        return Err(());\n                    }\n                    self.areas[index].rows.end += 1;\n                    current_area_index = Some(index);\n                },\n                Entry::Vacant(v) => {\n                    let index = self.areas.len();\n                    let name = v.key().clone();\n                    v.insert(index);\n                    self.areas.push(NamedArea {\n                        name,\n                        columns: UnsignedRange {\n                            start: column,\n                            end: column + 1,\n                        },\n                        rows: UnsignedRange {\n                            start: self.row,\n                            end: self.row + 1,\n                        },\n                    });\n                    current_area_index = Some(index);\n                },\n            }\n        }\n        if column == 0 {\n            // Each string must produce a valid token.\n            // https://github.com/w3c/csswg-drafts/issues/5110\n            return Err(());\n        }\n        if let Some(index) = current_area_index {\n            if self.areas[index].columns.end != column + 1 {\n                debug_assert_ne!(self.areas[index].rows.start, self.row);\n                return Err(());\n            }\n        }\n        if self.row == 1 {\n            self.width = column;\n        } else if self.width != column {\n            return Err(());\n        }\n\n        self.strings.push(simplified_string.into());\n        Ok(())\n    }\n\n    /// Return the parsed template areas.\n    pub fn finish(self) -> Result<TemplateAreas, ()> {\n        if self.strings.is_empty() {\n            return Err(());\n        }\n        Ok(TemplateAreas {\n            areas: self.areas.into(),\n            strings: self.strings.into(),\n            width: self.width,\n        })\n    }\n}\n\nimpl TemplateAreas {\n    fn parse_internal(input: &mut Parser) -> Result<Self, ()> {\n        let mut parser = TemplateAreasParser::default();\n        while parser.try_parse_string(input).is_ok() {}\n        parser.finish()\n    }\n}\n\nimpl Parse for TemplateAreas {\n    fn parse<'i, 't>(\n        _: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(input)\n            .map_err(|()| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n    }\n}\n\n/// Arc type for `Arc<TemplateAreas>`\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(transparent)]\npub struct TemplateAreasArc(#[ignore_malloc_size_of = \"Arc\"] pub Arc<TemplateAreas>);\n\nimpl Parse for TemplateAreasArc {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let parsed = TemplateAreas::parse(context, input)?;\n        Ok(TemplateAreasArc(Arc::new(parsed)))\n    }\n}\n\n/// A range of rows or columns. Using this instead of std::ops::Range for FFI\n/// purposes.\n#[repr(C)]\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\npub struct UnsignedRange {\n    /// The start of the range.\n    pub start: u32,\n    /// The end of the range.\n    pub end: u32,\n}\n\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\n/// Not associated with any particular grid item, but can be referenced from the\n/// grid-placement properties.\npub struct NamedArea {\n    /// Name of the `named area`\n    pub name: Atom,\n    /// Rows of the `named area`\n    pub rows: UnsignedRange,\n    /// Columns of the `named area`\n    pub columns: UnsignedRange,\n}\n\n/// Tokenize the string into a list of the tokens,\n/// using longest-match semantics\nstruct TemplateAreasTokenizer<'a>(&'a str);\n\nimpl<'a> Iterator for TemplateAreasTokenizer<'a> {\n    type Item = Result<Option<&'a str>, ()>;\n\n    fn next(&mut self) -> Option<Self::Item> {\n        let rest = self.0.trim_start_matches(HTML_SPACE_CHARACTERS);\n        if rest.is_empty() {\n            return None;\n        }\n        if rest.starts_with('.') {\n            self.0 = &rest[rest.find(|c| c != '.').unwrap_or(rest.len())..];\n            return Some(Ok(None));\n        }\n        if !rest.starts_with(is_name_code_point) {\n            return Some(Err(()));\n        }\n        let token_len = rest.find(|c| !is_name_code_point(c)).unwrap_or(rest.len());\n        let token = &rest[..token_len];\n        self.0 = &rest[token_len..];\n        Some(Ok(Some(token)))\n    }\n}\n\nfn is_name_code_point(c: char) -> bool {\n    c >= 'A' && c <= 'Z'\n        || c >= 'a' && c <= 'z'\n        || c >= '\\u{80}'\n        || c == '_'\n        || c >= '0' && c <= '9'\n        || c == '-'\n}\n\n/// This property specifies named grid areas.\n///\n/// The syntax of this property also provides a visualization of the structure\n/// of the grid, making the overall layout of the grid container easier to\n/// understand.\n#[repr(C, u8)]\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[typed(todo_derive_fields)]\npub enum GridTemplateAreas {\n    /// The `none` value.\n    None,\n    /// The actual value.\n    Areas(TemplateAreasArc),\n}\n\nimpl GridTemplateAreas {\n    #[inline]\n    /// Get default value as `none`\n    pub fn none() -> GridTemplateAreas {\n        GridTemplateAreas::None\n    }\n}\n\n/// A specified value for the `z-index` property.\npub type ZIndex = GenericZIndex<Integer>;\n\n/// A specified value for the `aspect-ratio` property.\npub type AspectRatio = GenericAspectRatio<NonNegativeNumber>;\n\nimpl Parse for AspectRatio {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::values::generics::position::PreferredRatio;\n        use crate::values::specified::Ratio;\n\n        let location = input.current_source_location();\n        let mut auto = input.try_parse(|i| i.expect_ident_matching(\"auto\"));\n        let ratio = input.try_parse(|i| Ratio::parse(context, i));\n        if auto.is_err() {\n            auto = input.try_parse(|i| i.expect_ident_matching(\"auto\"));\n        }\n\n        if auto.is_err() && ratio.is_err() {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(AspectRatio {\n            auto: auto.is_ok(),\n            ratio: match ratio {\n                Ok(ratio) => PreferredRatio::Ratio(ratio),\n                Err(..) => PreferredRatio::None,\n            },\n        })\n    }\n}\n\nimpl AspectRatio {\n    /// Returns Self by a valid ratio.\n    pub fn from_mapped_ratio(w: f32, h: f32) -> Self {\n        use crate::values::generics::position::PreferredRatio;\n        use crate::values::generics::ratio::Ratio;\n        AspectRatio {\n            auto: true,\n            ratio: PreferredRatio::Ratio(Ratio(\n                NonNegativeNumber::new(w),\n                NonNegativeNumber::new(h),\n            )),\n        }\n    }\n}\n\n/// A specified value for inset types.\npub type Inset = GenericInset<specified::Percentage, LengthPercentage>;\n\nimpl Inset {\n    /// Parses an inset type, allowing the unitless length quirk.\n    /// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>\n    #[inline]\n    pub fn parse_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(l) = input.try_parse(|i| LengthPercentage::parse_quirky(context, i, allow_quirks))\n        {\n            return Ok(Self::LengthPercentage(l));\n        }\n        match input.try_parse(|i| i.expect_ident_matching(\"auto\")) {\n            Ok(_) => return Ok(Self::Auto),\n            Err(e) if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") => {\n                return Err(e.into());\n            },\n            Err(_) => (),\n        };\n        Self::parse_anchor_functions_quirky(context, input, allow_quirks)\n    }\n\n    fn parse_as_anchor_function_fallback<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(l) =\n            input.try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::No))\n        {\n            return Ok(Self::LengthPercentage(l));\n        }\n        Self::parse_anchor_functions_quirky(context, input, AllowQuirks::No)\n    }\n\n    fn parse_anchor_functions_quirky<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_quirks: AllowQuirks,\n    ) -> Result<Self, ParseError<'i>> {\n        debug_assert!(\n            static_prefs::pref!(\"layout.css.anchor-positioning.enabled\"),\n            \"How are we parsing with pref off?\"\n        );\n        if let Ok(inner) = input.try_parse(|i| AnchorFunction::parse(context, i)) {\n            return Ok(Self::AnchorFunction(Box::new(inner)));\n        }\n        if let Ok(inner) =\n            input.try_parse(|i| GenericAnchorSizeFunction::<Inset>::parse(context, i))\n        {\n            return Ok(Self::AnchorSizeFunction(Box::new(inner)));\n        }\n        Ok(Self::AnchorContainingCalcFunction(input.try_parse(\n            |i| LengthPercentage::parse_quirky_with_anchor_functions(context, i, allow_quirks),\n        )?))\n    }\n}\n\nimpl Parse for Inset {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_quirky(context, input, AllowQuirks::No)\n    }\n}\n\n/// A specified value for `anchor()` function.\npub type AnchorFunction = GenericAnchorFunction<specified::Percentage, Inset>;\n\nimpl Parse for AnchorFunction {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if !static_prefs::pref!(\"layout.css.anchor-positioning.enabled\") {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        input.expect_function_matching(\"anchor\")?;\n        input.parse_nested_block(|i| {\n            let target_element = i.try_parse(|i| DashedIdent::parse(context, i)).ok();\n            let side = GenericAnchorSide::parse(context, i)?;\n            let target_element = if target_element.is_none() {\n                i.try_parse(|i| DashedIdent::parse(context, i)).ok()\n            } else {\n                target_element\n            };\n            let fallback = i\n                .try_parse(|i| {\n                    i.expect_comma()?;\n                    Inset::parse_as_anchor_function_fallback(context, i)\n                })\n                .ok();\n            Ok(Self {\n                target_element: TreeScoped::with_default_level(\n                    target_element.unwrap_or_else(DashedIdent::empty),\n                ),\n                side,\n                fallback: fallback.into(),\n            })\n        })\n    }\n}\n"
  },
  {
    "path": "style/values/specified/ratio.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for <ratio>.\n//!\n//! [ratio]: https://drafts.csswg.org/css-values/#ratios\n\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::ratio::Ratio as GenericRatio;\nuse crate::values::specified::NonNegativeNumber;\nuse crate::One;\nuse cssparser::Parser;\nuse style_traits::ParseError;\n\n/// A specified <ratio> value.\npub type Ratio = GenericRatio<NonNegativeNumber>;\n\nimpl Parse for Ratio {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let a = NonNegativeNumber::parse(context, input)?;\n        let b = match input.try_parse(|input| input.expect_delim('/')) {\n            Ok(()) => NonNegativeNumber::parse(context, input)?,\n            _ => One::one(),\n        };\n\n        Ok(GenericRatio(a, b))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/rect.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS borders.\n\nuse crate::values::generics::rect::Rect;\nuse crate::values::specified::length::NonNegativeLengthOrNumber;\n\n/// A specified rectangle made of four `<length-or-number>` values.\npub type NonNegativeLengthOrNumberRect = Rect<NonNegativeLengthOrNumber>;\n"
  },
  {
    "path": "style/values/specified/resolution.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Resolution values:\n//!\n//! https://drafts.csswg.org/css-values/#resolution\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::specified::CalcNode;\nuse crate::values::CSSFloat;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse std::fmt::{self, Write};\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// A specified resolution.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]\npub struct Resolution {\n    value: CSSFloat,\n    unit: ResolutionUnit,\n    was_calc: bool,\n}\n\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\nenum ResolutionUnit {\n    /// Dots per inch.\n    Dpi,\n    /// An alias unit for dots per pixel.\n    X,\n    /// Dots per pixel.\n    Dppx,\n    /// Dots per centimeter.\n    Dpcm,\n}\n\nimpl ResolutionUnit {\n    fn as_str(self) -> &'static str {\n        match self {\n            Self::Dpi => \"dpi\",\n            Self::X => \"x\",\n            Self::Dppx => \"dppx\",\n            Self::Dpcm => \"dpcm\",\n        }\n    }\n}\n\nimpl Resolution {\n    /// Returns a resolution value from dppx units.\n    pub fn from_dppx(value: CSSFloat) -> Self {\n        Self {\n            value,\n            unit: ResolutionUnit::Dppx,\n            was_calc: false,\n        }\n    }\n\n    /// Returns a resolution value from dppx units.\n    pub fn from_x(value: CSSFloat) -> Self {\n        Self {\n            value,\n            unit: ResolutionUnit::X,\n            was_calc: false,\n        }\n    }\n\n    /// Returns a resolution value from dppx units.\n    pub fn from_dppx_calc(value: CSSFloat) -> Self {\n        Self {\n            value,\n            unit: ResolutionUnit::Dppx,\n            was_calc: true,\n        }\n    }\n\n    /// Convert this resolution value to dppx units.\n    pub fn dppx(&self) -> CSSFloat {\n        match self.unit {\n            ResolutionUnit::X | ResolutionUnit::Dppx => self.value,\n            _ => self.dpi() / 96.0,\n        }\n    }\n\n    /// Convert this resolution value to dpi units.\n    pub fn dpi(&self) -> CSSFloat {\n        match self.unit {\n            ResolutionUnit::Dpi => self.value,\n            ResolutionUnit::X | ResolutionUnit::Dppx => self.value * 96.0,\n            ResolutionUnit::Dpcm => self.value * 2.54,\n        }\n    }\n\n    /// Parse a resolution given a value and unit.\n    pub fn parse_dimension<'i, 't>(value: CSSFloat, unit: &str) -> Result<Self, ()> {\n        let unit = match_ignore_ascii_case! { &unit,\n            \"dpi\" => ResolutionUnit::Dpi,\n            \"dppx\" => ResolutionUnit::Dppx,\n            \"dpcm\" => ResolutionUnit::Dpcm,\n            \"x\" => ResolutionUnit::X,\n            _ => return Err(())\n        };\n        Ok(Self {\n            value,\n            unit,\n            was_calc: false,\n        })\n    }\n}\n\nimpl ToCss for Resolution {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        crate::values::serialize_specified_dimension(\n            self.value,\n            self.unit.as_str(),\n            self.was_calc,\n            dest,\n        )\n    }\n}\n\nimpl Parse for Resolution {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        match *input.next()? {\n            Token::Dimension {\n                value, ref unit, ..\n            } if value >= 0. => Self::parse_dimension(value, unit)\n                .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                CalcNode::parse_resolution(context, input, function)\n            },\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/source_size_list.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! https://html.spec.whatwg.org/multipage/#source-size-list\n\nuse crate::device::Device;\nuse crate::dom::AttributeTracker;\nuse crate::parser::{Parse, ParserContext};\nuse crate::queries::{FeatureType, QueryCondition};\nuse crate::stylesheets::CustomMediaEvaluator;\nuse crate::values::computed::{self, ToComputedValue};\nuse crate::values::specified::{Length, NoCalcLength, ViewportPercentageLength};\nuse app_units::Au;\nuse cssparser::{Delimiter, Parser, Token};\nuse selectors::context::QuirksMode;\nuse style_traits::ParseError;\n\n/// A value for a `<source-size>`:\n///\n/// https://html.spec.whatwg.org/multipage/#source-size\n#[derive(Debug)]\npub struct SourceSize {\n    condition: QueryCondition,\n    value: Length,\n}\n\nimpl Parse for SourceSize {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let condition = QueryCondition::parse(context, input, FeatureType::Media)?;\n        let value = Length::parse_non_negative(context, input)?;\n        Ok(Self { condition, value })\n    }\n}\n\n/// A value for a `<source-size-list>`:\n///\n/// https://html.spec.whatwg.org/multipage/#source-size-list\n#[derive(Debug)]\npub struct SourceSizeList {\n    source_sizes: Vec<SourceSize>,\n    value: Option<Length>,\n}\n\nimpl SourceSizeList {\n    /// Create an empty `SourceSizeList`, which can be used as a fall-back.\n    pub fn empty() -> Self {\n        Self {\n            source_sizes: vec![],\n            value: None,\n        }\n    }\n\n    /// Evaluate this <source-size-list> to get the final viewport length.\n    pub fn evaluate(&self, device: &Device, quirks_mode: QuirksMode) -> Au {\n        computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {\n            let matching_source_size = self.source_sizes.iter().find(|source_size| {\n                source_size\n                    .condition\n                    .matches(\n                        context,\n                        &mut CustomMediaEvaluator::none(),\n                        &mut AttributeTracker::new_dummy(),\n                    )\n                    .to_bool(/* unknown = */ false)\n            });\n\n            match matching_source_size {\n                Some(source_size) => source_size.value.to_computed_value(context),\n                None => match self.value {\n                    Some(ref v) => v.to_computed_value(context),\n                    None => Length::NoCalc(NoCalcLength::ViewportPercentage(\n                        ViewportPercentageLength::Vw(100.),\n                    ))\n                    .to_computed_value(context),\n                },\n            }\n        })\n        .into()\n    }\n}\n\nenum SourceSizeOrLength {\n    SourceSize(SourceSize),\n    Length(Length),\n}\n\nimpl Parse for SourceSizeOrLength {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(size) = input.try_parse(|input| SourceSize::parse(context, input)) {\n            return Ok(SourceSizeOrLength::SourceSize(size));\n        }\n\n        let length = Length::parse_non_negative(context, input)?;\n        Ok(SourceSizeOrLength::Length(length))\n    }\n}\n\nimpl SourceSizeList {\n    /// NOTE(emilio): This doesn't match the grammar in the spec, see:\n    ///\n    /// https://html.spec.whatwg.org/multipage/#parsing-a-sizes-attribute\n    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Self {\n        let mut source_sizes = vec![];\n\n        loop {\n            let result = input.parse_until_before(Delimiter::Comma, |input| {\n                SourceSizeOrLength::parse(context, input)\n            });\n\n            match result {\n                Ok(SourceSizeOrLength::Length(value)) => {\n                    return Self {\n                        source_sizes,\n                        value: Some(value),\n                    };\n                },\n                Ok(SourceSizeOrLength::SourceSize(source_size)) => {\n                    source_sizes.push(source_size);\n                },\n                Err(..) => {},\n            }\n\n            match input.next() {\n                Ok(&Token::Comma) => {},\n                Err(..) => break,\n                _ => unreachable!(),\n            }\n        }\n\n        SourceSizeList {\n            source_sizes,\n            value: None,\n        }\n    }\n}\n"
  },
  {
    "path": "style/values/specified/svg.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for SVG properties.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::svg as generic;\nuse crate::values::specified::color::Color;\nuse crate::values::specified::url::SpecifiedUrl;\nuse crate::values::specified::AllowQuirks;\nuse crate::values::specified::LengthPercentage;\nuse crate::values::specified::SVGPathData;\nuse crate::values::specified::{NonNegativeLengthPercentage, Opacity};\nuse crate::values::CustomIdent;\nuse cssparser::{Parser, Token};\nuse std::fmt::{self, Write};\nuse style_traits::{CommaWithSpace, CssWriter, ParseError, Separator};\nuse style_traits::{StyleParseErrorKind, ToCss};\n\n/// Specified SVG Paint value\npub type SVGPaint = generic::GenericSVGPaint<Color, SpecifiedUrl>;\n\n/// <length> | <percentage> | <number> | context-value\npub type SVGLength = generic::GenericSVGLength<LengthPercentage>;\n\n/// A non-negative version of SVGLength.\npub type SVGWidth = generic::GenericSVGLength<NonNegativeLengthPercentage>;\n\n/// [ <length> | <percentage> | <number> ]# | context-value\npub type SVGStrokeDashArray = generic::GenericSVGStrokeDashArray<NonNegativeLengthPercentage>;\n\n/// Whether the `context-value` value is enabled.\n#[cfg(feature = \"gecko\")]\npub fn is_context_value_enabled() -> bool {\n    static_prefs::pref!(\"gfx.font_rendering.opentype_svg.enabled\")\n}\n\n/// Whether the `context-value` value is enabled.\n#[cfg(not(feature = \"gecko\"))]\npub fn is_context_value_enabled() -> bool {\n    false\n}\n\nmacro_rules! parse_svg_length {\n    ($ty:ty, $lp:ty) => {\n        impl Parse for $ty {\n            fn parse<'i, 't>(\n                context: &ParserContext,\n                input: &mut Parser<'i, 't>,\n            ) -> Result<Self, ParseError<'i>> {\n                if let Ok(lp) =\n                    input.try_parse(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))\n                {\n                    return Ok(generic::SVGLength::LengthPercentage(lp));\n                }\n\n                try_match_ident_ignore_ascii_case! { input,\n                    \"context-value\" if is_context_value_enabled() => {\n                        Ok(generic::SVGLength::ContextValue)\n                    },\n                }\n            }\n        }\n    };\n}\n\nparse_svg_length!(SVGLength, LengthPercentage);\nparse_svg_length!(SVGWidth, NonNegativeLengthPercentage);\n\nimpl Parse for SVGStrokeDashArray {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(values) = input.try_parse(|i| {\n            CommaWithSpace::parse(i, |i| {\n                NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)\n            })\n        }) {\n            return Ok(generic::SVGStrokeDashArray::Values(values.into()));\n        }\n\n        try_match_ident_ignore_ascii_case! { input,\n            \"context-value\" if is_context_value_enabled() => {\n                Ok(generic::SVGStrokeDashArray::ContextValue)\n            },\n            \"none\" => Ok(generic::SVGStrokeDashArray::Values(Default::default())),\n        }\n    }\n}\n\n/// <opacity-value> | context-fill-opacity | context-stroke-opacity\npub type SVGOpacity = generic::SVGOpacity<Opacity>;\n\n/// The specified value for a single CSS paint-order property.\n#[repr(u8)]\n#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, ToCss)]\npub enum PaintOrder {\n    /// `normal` variant\n    Normal = 0,\n    /// `fill` variant\n    Fill = 1,\n    /// `stroke` variant\n    Stroke = 2,\n    /// `markers` variant\n    Markers = 3,\n}\n\n/// Number of non-normal components\npub const PAINT_ORDER_COUNT: u8 = 3;\n\n/// Number of bits for each component\npub const PAINT_ORDER_SHIFT: u8 = 2;\n\n/// Mask with above bits set\npub const PAINT_ORDER_MASK: u8 = 0b11;\n\n/// The specified value is tree `PaintOrder` values packed into the\n/// bitfields below, as a six-bit field, of 3 two-bit pairs\n///\n/// Each pair can be set to FILL, STROKE, or MARKERS\n/// Lowest significant bit pairs are highest priority.\n///  `normal` is the empty bitfield. The three pairs are\n/// never zero in any case other than `normal`.\n///\n/// Higher priority values, i.e. the values specified first,\n/// will be painted first (and may be covered by paintings of lower priority)\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct SVGPaintOrder(pub u8);\n\nimpl SVGPaintOrder {\n    /// Get default `paint-order` with `0`\n    pub fn normal() -> Self {\n        SVGPaintOrder(0)\n    }\n\n    /// Get variant of `paint-order`\n    pub fn order_at(&self, pos: u8) -> PaintOrder {\n        match (self.0 >> pos * PAINT_ORDER_SHIFT) & PAINT_ORDER_MASK {\n            0 => PaintOrder::Normal,\n            1 => PaintOrder::Fill,\n            2 => PaintOrder::Stroke,\n            3 => PaintOrder::Markers,\n            _ => unreachable!(\"this cannot happen\"),\n        }\n    }\n}\n\nimpl Parse for SVGPaintOrder {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<SVGPaintOrder, ParseError<'i>> {\n        if let Ok(()) = input.try_parse(|i| i.expect_ident_matching(\"normal\")) {\n            return Ok(SVGPaintOrder::normal());\n        }\n\n        let mut value = 0;\n        // bitfield representing what we've seen so far\n        // bit 1 is fill, bit 2 is stroke, bit 3 is markers\n        let mut seen = 0;\n        let mut pos = 0;\n\n        loop {\n            let result: Result<_, ParseError> = input.try_parse(|input| {\n                try_match_ident_ignore_ascii_case! { input,\n                    \"fill\" => Ok(PaintOrder::Fill),\n                    \"stroke\" => Ok(PaintOrder::Stroke),\n                    \"markers\" => Ok(PaintOrder::Markers),\n                }\n            });\n\n            match result {\n                Ok(val) => {\n                    if (seen & (1 << val as u8)) != 0 {\n                        // don't parse the same ident twice\n                        return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n                    }\n\n                    value |= (val as u8) << (pos * PAINT_ORDER_SHIFT);\n                    seen |= 1 << (val as u8);\n                    pos += 1;\n                },\n                Err(_) => break,\n            }\n        }\n\n        if value == 0 {\n            // Couldn't find any keyword\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // fill in rest\n        for i in pos..PAINT_ORDER_COUNT {\n            for paint in 1..(PAINT_ORDER_COUNT + 1) {\n                // if not seen, set bit at position, mark as seen\n                if (seen & (1 << paint)) == 0 {\n                    seen |= 1 << paint;\n                    value |= paint << (i * PAINT_ORDER_SHIFT);\n                    break;\n                }\n            }\n        }\n\n        Ok(SVGPaintOrder(value))\n    }\n}\n\nimpl ToCss for SVGPaintOrder {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.0 == 0 {\n            return dest.write_str(\"normal\");\n        }\n\n        let mut last_pos_to_serialize = 0;\n        for i in (1..PAINT_ORDER_COUNT).rev() {\n            let component = self.order_at(i);\n            let earlier_component = self.order_at(i - 1);\n            if component < earlier_component {\n                last_pos_to_serialize = i - 1;\n                break;\n            }\n        }\n\n        for pos in 0..last_pos_to_serialize + 1 {\n            if pos != 0 {\n                dest.write_char(' ')?\n            }\n            self.order_at(pos).to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n/// The context properties we understand.\n#[derive(\n    Clone,\n    Copy,\n    Eq,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct ContextPropertyBits(u8);\nbitflags! {\n    impl ContextPropertyBits: u8 {\n        /// `fill`\n        const FILL = 1 << 0;\n        /// `stroke`\n        const STROKE = 1 << 1;\n        /// `fill-opacity`\n        const FILL_OPACITY = 1 << 2;\n        /// `stroke-opacity`\n        const STROKE_OPACITY = 1 << 3;\n    }\n}\n\n/// Specified MozContextProperties value.\n/// Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)\n#[derive(\n    Clone,\n    Debug,\n    Default,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\npub struct MozContextProperties {\n    #[css(iterable, if_empty = \"none\")]\n    #[ignore_malloc_size_of = \"Arc\"]\n    idents: crate::ArcSlice<CustomIdent>,\n    #[css(skip)]\n    bits: ContextPropertyBits,\n}\n\nimpl Parse for MozContextProperties {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<MozContextProperties, ParseError<'i>> {\n        let mut values = vec![];\n        let mut bits = ContextPropertyBits::empty();\n        loop {\n            {\n                let location = input.current_source_location();\n                let ident = input.expect_ident()?;\n\n                if ident.eq_ignore_ascii_case(\"none\") && values.is_empty() {\n                    return Ok(Self::default());\n                }\n\n                let ident = CustomIdent::from_ident(location, ident, &[\"all\", \"none\", \"auto\"])?;\n\n                if ident.0 == atom!(\"fill\") {\n                    bits.insert(ContextPropertyBits::FILL);\n                } else if ident.0 == atom!(\"stroke\") {\n                    bits.insert(ContextPropertyBits::STROKE);\n                } else if ident.0 == atom!(\"fill-opacity\") {\n                    bits.insert(ContextPropertyBits::FILL_OPACITY);\n                } else if ident.0 == atom!(\"stroke-opacity\") {\n                    bits.insert(ContextPropertyBits::STROKE_OPACITY);\n                }\n\n                values.push(ident);\n            }\n\n            let location = input.current_source_location();\n            match input.next() {\n                Ok(&Token::Comma) => continue,\n                Err(..) => break,\n                Ok(other) => return Err(location.new_unexpected_token_error(other.clone())),\n            }\n        }\n\n        if values.is_empty() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        Ok(MozContextProperties {\n            idents: crate::ArcSlice::from_iter(values.into_iter()),\n            bits,\n        })\n    }\n}\n\n/// The svg d property type.\n///\n/// https://svgwg.org/svg2-draft/paths.html#TheDProperty\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum DProperty {\n    /// Path value for path(<string>) or just a <string>.\n    #[css(function)]\n    Path(SVGPathData),\n    /// None value.\n    #[animation(error)]\n    None,\n}\n\nimpl DProperty {\n    /// return none.\n    #[inline]\n    pub fn none() -> Self {\n        DProperty::None\n    }\n}\n\nimpl Parse for DProperty {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Parse none.\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(DProperty::none());\n        }\n\n        // Parse possible functions.\n        input.expect_function_matching(\"path\")?;\n        let path_data = input.parse_nested_block(|i| Parse::parse(context, i))?;\n        Ok(DProperty::Path(path_data))\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Default,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(single = \"none\", mixed = \"non-scaling-stroke\"))]\n#[repr(C)]\n/// https://svgwg.org/svg2-draft/coords.html#VectorEffects\npub struct VectorEffect(u8);\nbitflags! {\n    impl VectorEffect: u8 {\n        /// `none`\n        const NONE = 0;\n        /// `non-scaling-stroke`\n        const NON_SCALING_STROKE = 1 << 0;\n    }\n}\n\nimpl VectorEffect {\n    /// Returns the initial value of vector-effect\n    #[inline]\n    pub fn none() -> Self {\n        Self::NONE\n    }\n}\n"
  },
  {
    "path": "style/values/specified/svg_path.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for SVG Path.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::animated::{lists, Animate, Procedure};\nuse crate::values::distance::{ComputeSquaredDistance, SquaredDistance};\nuse crate::values::generics::basic_shape::GenericShapeCommand;\nuse crate::values::generics::basic_shape::{\n    ArcRadii, ArcSize, ArcSweep, AxisEndPoint, AxisPosition, CommandEndPoint, ControlPoint,\n    ControlReference, CoordinatePair, RelativeControlPoint,\n};\nuse crate::values::generics::position::GenericPosition;\nuse crate::values::CSSFloat;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse std::iter::{Cloned, Peekable};\nuse std::ops;\nuse std::slice;\nuse style_traits::values::SequenceWriter;\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\n\n/// Whether to allow empty string in the parser.\n#[derive(Clone, Debug, Eq, PartialEq)]\n#[allow(missing_docs)]\npub enum AllowEmpty {\n    Yes,\n    No,\n}\n\n/// The SVG path data.\n///\n/// https://www.w3.org/TR/SVG11/paths.html#PathData\n#[derive(\n    Clone,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedZero,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\npub struct SVGPathData(\n    // TODO(emilio): Should probably measure this somehow only from the\n    // specified values.\n    #[ignore_malloc_size_of = \"Arc\"] pub crate::ArcSlice<PathCommand>,\n);\n\nimpl SVGPathData {\n    /// Get the array of PathCommand.\n    #[inline]\n    pub fn commands(&self) -> &[PathCommand] {\n        &self.0\n    }\n\n    /// Create a normalized copy of this path by converting each relative\n    /// command to an absolute command.\n    pub fn normalize(&self, reduce: bool) -> Self {\n        let mut state = PathTraversalState {\n            subpath_start: CoordPair::new(0.0, 0.0),\n            pos: CoordPair::new(0.0, 0.0),\n            last_command: PathCommand::Close,\n            last_control: CoordPair::new(0.0, 0.0),\n        };\n        let iter = self.0.iter().map(|seg| seg.normalize(&mut state, reduce));\n        SVGPathData(crate::ArcSlice::from_iter(iter))\n    }\n\n    /// Parse this SVG path string with the argument that indicates whether we should allow the\n    /// empty string.\n    // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make\n    // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.)\n    // e.g. \"M100 200L100 200\" is a valid SVG path string. If we use tokenizer, the first ident\n    // is \"M100\", instead of \"M\", and this is not correct. Therefore, we use a Peekable\n    // str::Char iterator to check each character.\n    //\n    // css-shapes-1 says a path data string that does conform but defines an empty path is\n    // invalid and causes the entire path() to be invalid, so we use allow_empty to decide\n    // whether we should allow it.\n    // https://drafts.csswg.org/css-shapes-1/#typedef-basic-shape\n    pub fn parse<'i, 't>(\n        input: &mut Parser<'i, 't>,\n        allow_empty: AllowEmpty,\n    ) -> Result<Self, ParseError<'i>> {\n        let location = input.current_source_location();\n        let path_string = input.expect_string()?.as_ref();\n        let (path, ok) = Self::parse_bytes(path_string.as_bytes());\n        if !ok || (allow_empty == AllowEmpty::No && path.0.is_empty()) {\n            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n        return Ok(path);\n    }\n\n    /// As above, but just parsing the raw byte stream.\n    ///\n    /// Returns the (potentially empty or partial) path, and whether the parsing was ok or we found\n    /// an error. The API is a bit weird because some SVG callers require \"parse until first error\"\n    /// behavior.\n    pub fn parse_bytes(input: &[u8]) -> (Self, bool) {\n        // Parse the svg path string as multiple sub-paths.\n        let mut ok = true;\n        let mut path_parser = PathParser::new(input);\n\n        while skip_wsp(&mut path_parser.chars) {\n            if path_parser.parse_subpath().is_err() {\n                ok = false;\n                break;\n            }\n        }\n\n        let path = Self(crate::ArcSlice::from_iter(path_parser.path.into_iter()));\n        (path, ok)\n    }\n\n    /// Serializes to the path string, potentially including quotes.\n    pub fn to_css<W>(&self, dest: &mut CssWriter<W>, quote: bool) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        if quote {\n            dest.write_char('\"')?;\n        }\n        let mut writer = SequenceWriter::new(dest, \" \");\n        for command in self.commands() {\n            writer.write_item(|inner| command.to_css_for_svg(inner))?;\n        }\n        if quote {\n            dest.write_char('\"')?;\n        }\n        Ok(())\n    }\n}\n\nimpl ToCss for SVGPathData {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        self.to_css(dest, /* quote = */ true)\n    }\n}\n\nimpl Parse for SVGPathData {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // Note that the EBNF allows the path data string in the d property to be empty, so we\n        // don't reject empty SVG path data.\n        // https://svgwg.org/svg2-draft/single-page.html#paths-PathDataBNF\n        SVGPathData::parse(input, AllowEmpty::Yes)\n    }\n}\n\nimpl Animate for SVGPathData {\n    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {\n        if self.0.len() != other.0.len() {\n            return Err(());\n        }\n\n        // FIXME(emilio): This allocates three copies of the path, that's not\n        // great! Specially, once we're normalized once, we don't need to\n        // re-normalize again.\n        let left = self.normalize(false);\n        let right = other.normalize(false);\n\n        let items: Vec<_> = lists::by_computed_value::animate(&left.0, &right.0, procedure)?;\n        Ok(SVGPathData(crate::ArcSlice::from_iter(items.into_iter())))\n    }\n}\n\nimpl ComputeSquaredDistance for SVGPathData {\n    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {\n        if self.0.len() != other.0.len() {\n            return Err(());\n        }\n        let left = self.normalize(false);\n        let right = other.normalize(false);\n        lists::by_computed_value::squared_distance(&left.0, &right.0)\n    }\n}\n\n/// A position type for SVG path coordinates (just a pair of floats).\npub type SVGPathPosition = GenericPosition<CSSFloat, CSSFloat>;\n\n/// The SVG path command.\n/// The fields of these commands are self-explanatory, so we skip the documents.\n/// Note: the index of the control points, e.g. control1, control2, are mapping to the control\n/// points of the Bézier curve in the spec.\n///\n/// https://www.w3.org/TR/SVG11/paths.html#PathData\npub type PathCommand = GenericShapeCommand<CSSFloat, SVGPathPosition, CSSFloat>;\n\n/// For internal SVGPath normalization.\n#[allow(missing_docs)]\nstruct PathTraversalState {\n    subpath_start: CoordPair,\n    pos: CoordPair,\n    last_command: PathCommand,\n    last_control: CoordPair,\n}\n\nimpl PathCommand {\n    /// Create a normalized copy of this PathCommand. Absolute commands will be copied as-is while\n    /// for relative commands an equivalent absolute command will be returned.\n    ///\n    /// See discussion: https://github.com/w3c/svgwg/issues/321\n    /// If reduce is true then the path will be restricted to\n    /// \"M\", \"L\", \"C\", \"A\" and \"Z\" commands.\n    fn normalize(&self, state: &mut PathTraversalState, reduce: bool) -> Self {\n        use crate::values::generics::basic_shape::GenericShapeCommand::*;\n        match *self {\n            Close => {\n                state.pos = state.subpath_start;\n                if reduce {\n                    state.last_command = *self;\n                }\n                Close\n            },\n            Move { mut point } => {\n                point = point.to_abs(state.pos);\n                state.pos = point.into();\n                state.subpath_start = point.into();\n                if reduce {\n                    state.last_command = *self;\n                }\n                Move { point }\n            },\n            Line { mut point } => {\n                point = point.to_abs(state.pos);\n                state.pos = point.into();\n                if reduce {\n                    state.last_command = *self;\n                }\n                Line { point }\n            },\n            HLine { mut x } => {\n                x = x.to_abs(state.pos.x);\n                state.pos.x = x.into();\n                if reduce {\n                    state.last_command = *self;\n                    PathCommand::Line {\n                        point: CommandEndPoint::ToPosition(state.pos.into()),\n                    }\n                } else {\n                    HLine { x }\n                }\n            },\n            VLine { mut y } => {\n                y = y.to_abs(state.pos.y);\n                state.pos.y = y.into();\n                if reduce {\n                    state.last_command = *self;\n                    PathCommand::Line {\n                        point: CommandEndPoint::ToPosition(state.pos.into()),\n                    }\n                } else {\n                    VLine { y }\n                }\n            },\n            CubicCurve {\n                mut point,\n                mut control1,\n                mut control2,\n            } => {\n                control1 = control1.to_abs(state.pos, point);\n                control2 = control2.to_abs(state.pos, point);\n                point = point.to_abs(state.pos);\n                state.pos = point.into();\n                if reduce {\n                    state.last_command = *self;\n                    state.last_control = control2.into();\n                }\n                CubicCurve {\n                    point,\n                    control1,\n                    control2,\n                }\n            },\n            QuadCurve {\n                mut point,\n                mut control1,\n            } => {\n                control1 = control1.to_abs(state.pos, point);\n                point = point.to_abs(state.pos);\n                if reduce {\n                    let c1 = state.pos + 2. * (CoordPair::from(control1) - state.pos) / 3.;\n                    let control2 = CoordPair::from(point)\n                        + 2. * (CoordPair::from(control1) - point.into()) / 3.;\n                    state.pos = point.into();\n                    state.last_command = *self;\n                    state.last_control = control1.into();\n                    CubicCurve {\n                        point,\n                        control1: ControlPoint::Absolute(c1.into()),\n                        control2: ControlPoint::Absolute(control2.into()),\n                    }\n                } else {\n                    state.pos = point.into();\n                    QuadCurve { point, control1 }\n                }\n            },\n            SmoothCubic {\n                mut point,\n                mut control2,\n            } => {\n                control2 = control2.to_abs(state.pos, point);\n                point = point.to_abs(state.pos);\n                if reduce {\n                    let control1 = match state.last_command {\n                        PathCommand::CubicCurve {\n                            point: _,\n                            control1: _,\n                            control2: _,\n                        }\n                        | PathCommand::SmoothCubic {\n                            point: _,\n                            control2: _,\n                        } => state.pos + state.pos - state.last_control,\n                        _ => state.pos,\n                    };\n                    state.pos = point.into();\n                    state.last_control = control2.into();\n                    state.last_command = *self;\n                    CubicCurve {\n                        point,\n                        control1: ControlPoint::Absolute(control1.into()),\n                        control2,\n                    }\n                } else {\n                    state.pos = point.into();\n                    SmoothCubic { point, control2 }\n                }\n            },\n            SmoothQuad { mut point } => {\n                point = point.to_abs(state.pos);\n                if reduce {\n                    let control = match state.last_command {\n                        PathCommand::QuadCurve {\n                            point: _,\n                            control1: _,\n                        }\n                        | PathCommand::SmoothQuad { point: _ } => {\n                            state.pos + state.pos - state.last_control\n                        },\n                        _ => state.pos,\n                    };\n                    let control1 = state.pos + 2. * (control - state.pos) / 3.;\n                    let control2 = CoordPair::from(point) + 2. * (control - point.into()) / 3.;\n                    state.pos = point.into();\n                    state.last_command = *self;\n                    state.last_control = control;\n                    CubicCurve {\n                        point,\n                        control1: ControlPoint::Absolute(control1.into()),\n                        control2: ControlPoint::Absolute(control2.into()),\n                    }\n                } else {\n                    state.pos = point.into();\n                    SmoothQuad { point }\n                }\n            },\n            Arc {\n                mut point,\n                radii,\n                arc_sweep,\n                arc_size,\n                rotate,\n            } => {\n                point = point.to_abs(state.pos);\n                state.pos = point.into();\n                if reduce {\n                    state.last_command = *self;\n                    if radii.rx == 0. && radii.ry.as_ref().is_none_or(|v| *v == 0.) {\n                        let end_point = CoordPair::from(point);\n                        CubicCurve {\n                            point: CommandEndPoint::ToPosition(state.pos.into()),\n                            control1: ControlPoint::Absolute(end_point.into()),\n                            control2: ControlPoint::Absolute(end_point.into()),\n                        }\n                    } else {\n                        Arc {\n                            point,\n                            radii,\n                            arc_sweep,\n                            arc_size,\n                            rotate,\n                        }\n                    }\n                } else {\n                    Arc {\n                        point,\n                        radii,\n                        arc_sweep,\n                        arc_size,\n                        rotate,\n                    }\n                }\n            },\n        }\n    }\n\n    /// The serialization of the svg path.\n    fn to_css_for_svg<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: fmt::Write,\n    {\n        use crate::values::generics::basic_shape::GenericShapeCommand::*;\n        match *self {\n            Close => dest.write_char('Z'),\n            Move { point } => {\n                dest.write_char(if point.is_abs() { 'M' } else { 'm' })?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            Line { point } => {\n                dest.write_char(if point.is_abs() { 'L' } else { 'l' })?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            CubicCurve {\n                point,\n                control1,\n                control2,\n            } => {\n                dest.write_char(if point.is_abs() { 'C' } else { 'c' })?;\n                dest.write_char(' ')?;\n                control1.to_css(dest, point.is_abs())?;\n                dest.write_char(' ')?;\n                control2.to_css(dest, point.is_abs())?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            QuadCurve { point, control1 } => {\n                dest.write_char(if point.is_abs() { 'Q' } else { 'q' })?;\n                dest.write_char(' ')?;\n                control1.to_css(dest, point.is_abs())?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            Arc {\n                point,\n                radii,\n                arc_sweep,\n                arc_size,\n                rotate,\n            } => {\n                dest.write_char(if point.is_abs() { 'A' } else { 'a' })?;\n                dest.write_char(' ')?;\n                radii.to_css(dest)?;\n                dest.write_char(' ')?;\n                rotate.to_css(dest)?;\n                dest.write_char(' ')?;\n                (arc_size as i32).to_css(dest)?;\n                dest.write_char(' ')?;\n                (arc_sweep as i32).to_css(dest)?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            HLine { x } => {\n                dest.write_char(if x.is_abs() { 'H' } else { 'h' })?;\n                dest.write_char(' ')?;\n                CSSFloat::from(x).to_css(dest)\n            },\n            VLine { y } => {\n                dest.write_char(if y.is_abs() { 'V' } else { 'v' })?;\n                dest.write_char(' ')?;\n                CSSFloat::from(y).to_css(dest)\n            },\n            SmoothCubic { point, control2 } => {\n                dest.write_char(if point.is_abs() { 'S' } else { 's' })?;\n                dest.write_char(' ')?;\n                control2.to_css(dest, point.is_abs())?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n            SmoothQuad { point } => {\n                dest.write_char(if point.is_abs() { 'T' } else { 't' })?;\n                dest.write_char(' ')?;\n                CoordPair::from(point).to_css(dest)\n            },\n        }\n    }\n}\n\n/// The path coord type.\npub type CoordPair = CoordinatePair<CSSFloat>;\n\nimpl ops::Add<CoordPair> for CoordPair {\n    type Output = CoordPair;\n\n    fn add(self, rhs: CoordPair) -> CoordPair {\n        Self {\n            x: self.x + rhs.x,\n            y: self.y + rhs.y,\n        }\n    }\n}\n\nimpl ops::Sub<CoordPair> for CoordPair {\n    type Output = CoordPair;\n\n    fn sub(self, rhs: CoordPair) -> CoordPair {\n        Self {\n            x: self.x - rhs.x,\n            y: self.y - rhs.y,\n        }\n    }\n}\n\nimpl ops::Mul<CSSFloat> for CoordPair {\n    type Output = CoordPair;\n\n    fn mul(self, f: CSSFloat) -> CoordPair {\n        Self {\n            x: self.x * f,\n            y: self.y * f,\n        }\n    }\n}\n\nimpl ops::Mul<CoordPair> for CSSFloat {\n    type Output = CoordPair;\n\n    fn mul(self, rhs: CoordPair) -> CoordPair {\n        rhs * self\n    }\n}\n\nimpl ops::Div<CSSFloat> for CoordPair {\n    type Output = CoordPair;\n\n    fn div(self, f: CSSFloat) -> CoordPair {\n        Self {\n            x: self.x / f,\n            y: self.y / f,\n        }\n    }\n}\n\nimpl CommandEndPoint<SVGPathPosition, CSSFloat> {\n    /// Converts <command-end-point> into absolutely positioned type.\n    pub fn to_abs(self, state_pos: CoordPair) -> Self {\n        // Consume self value.\n        match self {\n            CommandEndPoint::ToPosition(_) => self,\n            CommandEndPoint::ByCoordinate(coord) => {\n                let pos = GenericPosition {\n                    horizontal: coord.x + state_pos.x,\n                    vertical: coord.y + state_pos.y,\n                };\n                CommandEndPoint::ToPosition(pos)\n            },\n        }\n    }\n}\n\nimpl AxisEndPoint<CSSFloat> {\n    /// Converts possibly relative end point into absolutely positioned type.\n    pub fn to_abs(self, base: CSSFloat) -> AxisEndPoint<CSSFloat> {\n        // Consume self value.\n        match self {\n            AxisEndPoint::ToPosition(_) => self,\n            AxisEndPoint::ByCoordinate(coord) => {\n                AxisEndPoint::ToPosition(AxisPosition::LengthPercent(coord + base))\n            },\n        }\n    }\n}\n\nimpl ControlPoint<SVGPathPosition, CSSFloat> {\n    /// Converts <control-point> into absolutely positioned control point type.\n    pub fn to_abs(\n        self,\n        state_pos: CoordPair,\n        end_point: CommandEndPoint<SVGPathPosition, CSSFloat>,\n    ) -> Self {\n        // Consume self value.\n        match self {\n            ControlPoint::Absolute(_) => self,\n            ControlPoint::Relative(point) => {\n                let mut pos = GenericPosition {\n                    horizontal: point.coord.x,\n                    vertical: point.coord.y,\n                };\n\n                match point.reference {\n                    ControlReference::Start => {\n                        pos.horizontal += state_pos.x;\n                        pos.vertical += state_pos.y;\n                    },\n                    ControlReference::End => {\n                        let end = CoordPair::from(end_point);\n                        pos.horizontal += end.x;\n                        pos.vertical += end.y;\n                    },\n                    _ => (),\n                }\n                ControlPoint::Absolute(pos)\n            },\n        }\n    }\n}\n\nimpl From<CommandEndPoint<SVGPathPosition, CSSFloat>> for CoordPair {\n    #[inline]\n    fn from(p: CommandEndPoint<SVGPathPosition, CSSFloat>) -> Self {\n        match p {\n            CommandEndPoint::ToPosition(pos) => CoordPair {\n                x: pos.horizontal,\n                y: pos.vertical,\n            },\n            CommandEndPoint::ByCoordinate(coord) => coord,\n        }\n    }\n}\n\nimpl From<ControlPoint<SVGPathPosition, CSSFloat>> for CoordPair {\n    #[inline]\n    fn from(point: ControlPoint<SVGPathPosition, CSSFloat>) -> Self {\n        match point {\n            ControlPoint::Absolute(pos) => CoordPair {\n                x: pos.horizontal,\n                y: pos.vertical,\n            },\n            ControlPoint::Relative(_) => {\n                panic!(\n                    \"Attempted to convert a relative ControlPoint to CoordPair, which is lossy. \\\n                        Consider converting it to absolute type first using `.to_abs()`.\"\n                )\n            },\n        }\n    }\n}\n\nimpl From<CoordPair> for CommandEndPoint<SVGPathPosition, CSSFloat> {\n    #[inline]\n    fn from(coord: CoordPair) -> Self {\n        CommandEndPoint::ByCoordinate(coord)\n    }\n}\n\nimpl From<CoordPair> for SVGPathPosition {\n    #[inline]\n    fn from(coord: CoordPair) -> Self {\n        GenericPosition {\n            horizontal: coord.x,\n            vertical: coord.y,\n        }\n    }\n}\n\nimpl From<AxisEndPoint<CSSFloat>> for CSSFloat {\n    #[inline]\n    fn from(p: AxisEndPoint<CSSFloat>) -> Self {\n        match p {\n            AxisEndPoint::ToPosition(AxisPosition::LengthPercent(a)) => a,\n            AxisEndPoint::ToPosition(AxisPosition::Keyword(_)) => {\n                unreachable!(\"Invalid state: SVG path commands cannot contain a keyword.\")\n            },\n            AxisEndPoint::ByCoordinate(a) => a,\n        }\n    }\n}\n\nimpl ToCss for SVGPathPosition {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.horizontal.to_css(dest)?;\n        dest.write_char(' ')?;\n        self.vertical.to_css(dest)\n    }\n}\n\n/// SVG Path parser.\nstruct PathParser<'a> {\n    chars: Peekable<Cloned<slice::Iter<'a, u8>>>,\n    path: Vec<PathCommand>,\n}\n\nmacro_rules! parse_arguments {\n    (\n        $parser:ident,\n        $enum:ident,\n        $( $field:ident : $value:expr, )*\n        [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ]\n    ) => {\n        {\n            loop {\n                let $para = $func(&mut $parser.chars)?;\n                $(\n                    skip_comma_wsp(&mut $parser.chars);\n                    let $other_para = $other_func(&mut $parser.chars)?;\n                )*\n                $parser.path.push(\n                    PathCommand::$enum { $( $field: $value, )* $para $(, $other_para)* }\n                );\n\n                // End of string or the next character is a possible new command.\n                if !skip_wsp(&mut $parser.chars) ||\n                   $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) {\n                    break;\n                }\n                skip_comma_wsp(&mut $parser.chars);\n            }\n            Ok(())\n        }\n    }\n}\n\nimpl<'a> PathParser<'a> {\n    /// Return a PathParser.\n    #[inline]\n    fn new(bytes: &'a [u8]) -> Self {\n        PathParser {\n            chars: bytes.iter().cloned().peekable(),\n            path: Vec::new(),\n        }\n    }\n\n    /// Parse a sub-path.\n    fn parse_subpath(&mut self) -> Result<(), ()> {\n        // Handle \"moveto\" Command first. If there is no \"moveto\", this is not a valid sub-path\n        // (i.e. not a valid moveto-drawto-command-group).\n        self.parse_moveto()?;\n\n        // Handle other commands.\n        loop {\n            skip_wsp(&mut self.chars);\n            if self.chars.peek().map_or(true, |&m| m == b'M' || m == b'm') {\n                break;\n            }\n\n            let command = self.chars.next().unwrap();\n\n            skip_wsp(&mut self.chars);\n            match command {\n                b'Z' | b'z' => self.parse_closepath(),\n                b'L' => self.parse_line_abs(),\n                b'l' => self.parse_line_rel(),\n                b'H' => self.parse_h_line_abs(),\n                b'h' => self.parse_h_line_rel(),\n                b'V' => self.parse_v_line_abs(),\n                b'v' => self.parse_v_line_rel(),\n                b'C' => self.parse_curve_abs(),\n                b'c' => self.parse_curve_rel(),\n                b'S' => self.parse_smooth_curve_abs(),\n                b's' => self.parse_smooth_curve_rel(),\n                b'Q' => self.parse_quadratic_bezier_curve_abs(),\n                b'q' => self.parse_quadratic_bezier_curve_rel(),\n                b'T' => self.parse_smooth_quadratic_bezier_curve_abs(),\n                b't' => self.parse_smooth_quadratic_bezier_curve_rel(),\n                b'A' => self.parse_elliptical_arc_abs(),\n                b'a' => self.parse_elliptical_arc_rel(),\n                _ => return Err(()),\n            }?;\n        }\n        Ok(())\n    }\n\n    /// Parse \"moveto\" command.\n    fn parse_moveto(&mut self) -> Result<(), ()> {\n        let command = match self.chars.next() {\n            Some(c) if c == b'M' || c == b'm' => c,\n            _ => return Err(()),\n        };\n\n        skip_wsp(&mut self.chars);\n        let point = if command == b'M' {\n            parse_command_end_abs(&mut self.chars)\n        } else {\n            parse_command_end_rel(&mut self.chars)\n        }?;\n        self.path.push(PathCommand::Move { point });\n\n        // End of string or the next character is a possible new command.\n        if !skip_wsp(&mut self.chars) || self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic())\n        {\n            return Ok(());\n        }\n        skip_comma_wsp(&mut self.chars);\n\n        // If a moveto is followed by multiple pairs of coordinates, the subsequent\n        // pairs are treated as implicit lineto commands.\n        if point.is_abs() {\n            self.parse_line_abs()\n        } else {\n            self.parse_line_rel()\n        }\n    }\n\n    /// Parse \"closepath\" command.\n    fn parse_closepath(&mut self) -> Result<(), ()> {\n        self.path.push(PathCommand::Close);\n        Ok(())\n    }\n\n    /// Parse an absolute \"lineto\" (\"L\") command.\n    fn parse_line_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, Line, [ point => parse_command_end_abs ])\n    }\n\n    /// Parse a relative \"lineto\" (\"l\") command.\n    fn parse_line_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, Line, [ point => parse_command_end_rel ])\n    }\n\n    /// Parse an absolute horizontal \"lineto\" (\"H\") command.\n    fn parse_h_line_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, HLine, [ x => parse_axis_end_abs ])\n    }\n\n    /// Parse a relative horizontal \"lineto\" (\"h\") command.\n    fn parse_h_line_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, HLine, [ x => parse_axis_end_rel ])\n    }\n\n    /// Parse an absolute vertical \"lineto\" (\"V\") command.\n    fn parse_v_line_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, VLine, [ y => parse_axis_end_abs ])\n    }\n\n    /// Parse a relative vertical \"lineto\" (\"v\") command.\n    fn parse_v_line_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, VLine, [ y => parse_axis_end_rel ])\n    }\n\n    /// Parse an absolute cubic Bézier curve (\"C\") command.\n    fn parse_curve_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, CubicCurve, [\n            control1 => parse_control_point_abs, control2 => parse_control_point_abs, point => parse_command_end_abs\n        ])\n    }\n\n    /// Parse a relative cubic Bézier curve (\"c\") command.\n    fn parse_curve_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, CubicCurve, [\n            control1 => parse_control_point_rel, control2 => parse_control_point_rel, point => parse_command_end_rel\n        ])\n    }\n\n    /// Parse an absolute smooth \"curveto\" (\"S\") command.\n    fn parse_smooth_curve_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, SmoothCubic, [\n            control2 => parse_control_point_abs, point => parse_command_end_abs\n        ])\n    }\n\n    /// Parse a relative smooth \"curveto\" (\"s\") command.\n    fn parse_smooth_curve_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, SmoothCubic, [\n            control2 => parse_control_point_rel, point => parse_command_end_rel\n        ])\n    }\n\n    /// Parse an absolute quadratic Bézier curve (\"Q\") command.\n    fn parse_quadratic_bezier_curve_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, QuadCurve, [\n            control1 => parse_control_point_abs, point => parse_command_end_abs\n        ])\n    }\n\n    /// Parse a relative quadratic Bézier curve (\"q\") command.\n    fn parse_quadratic_bezier_curve_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, QuadCurve, [\n            control1 => parse_control_point_rel, point => parse_command_end_rel\n        ])\n    }\n\n    /// Parse an absolute smooth quadratic Bézier curveto (\"T\") command.\n    fn parse_smooth_quadratic_bezier_curve_abs(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, SmoothQuad, [ point => parse_command_end_abs ])\n    }\n\n    /// Parse a relative smooth quadratic Bézier curveto (\"t\") command.\n    fn parse_smooth_quadratic_bezier_curve_rel(&mut self) -> Result<(), ()> {\n        parse_arguments!(self, SmoothQuad, [ point => parse_command_end_rel ])\n    }\n\n    /// Parse an absolute elliptical arc curve (\"A\") command.\n    fn parse_elliptical_arc_abs(&mut self) -> Result<(), ()> {\n        let (parse_arc_size, parse_arc_sweep) = Self::arc_flag_parsers();\n        parse_arguments!(self, Arc, [\n            radii => parse_arc_radii,\n            rotate => parse_number,\n            arc_size => parse_arc_size,\n            arc_sweep => parse_arc_sweep,\n            point => parse_command_end_abs\n        ])\n    }\n\n    /// Parse a relative elliptical arc curve (\"a\") command.\n    fn parse_elliptical_arc_rel(&mut self) -> Result<(), ()> {\n        let (parse_arc_size, parse_arc_sweep) = Self::arc_flag_parsers();\n        parse_arguments!(self, Arc, [\n            radii => parse_arc_radii,\n            rotate => parse_number,\n            arc_size => parse_arc_size,\n            arc_sweep => parse_arc_sweep,\n            point => parse_command_end_rel\n        ])\n    }\n\n    /// Helper that returns parsers for the arc-size and arc-sweep flags.\n    fn arc_flag_parsers() -> (\n        impl Fn(&mut Peekable<Cloned<slice::Iter<'_, u8>>>) -> Result<ArcSize, ()>,\n        impl Fn(&mut Peekable<Cloned<slice::Iter<'_, u8>>>) -> Result<ArcSweep, ()>,\n    ) {\n        // Parse a flag whose value is '0' or '1'; otherwise, return Err(()).\n        let parse_arc_size = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {\n            Some(c) if c == b'1' => Ok(ArcSize::Large),\n            Some(c) if c == b'0' => Ok(ArcSize::Small),\n            _ => Err(()),\n        };\n        let parse_arc_sweep = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {\n            Some(c) if c == b'1' => Ok(ArcSweep::Cw),\n            Some(c) if c == b'0' => Ok(ArcSweep::Ccw),\n            _ => Err(()),\n        };\n        (parse_arc_size, parse_arc_sweep)\n    }\n}\n\n/// Parse a pair of numbers into CoordPair.\nfn parse_coord(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> Result<CoordPair, ()> {\n    let x = parse_number(iter)?;\n    skip_comma_wsp(iter);\n    let y = parse_number(iter)?;\n    Ok(CoordPair::new(x, y))\n}\n\n/// Parse a pair of numbers that describes the absolutely positioned end point.\nfn parse_command_end_abs(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<CommandEndPoint<SVGPathPosition, CSSFloat>, ()> {\n    let coord = parse_coord(iter)?;\n    Ok(CommandEndPoint::ToPosition(coord.into()))\n}\n\n/// Parse a pair of numbers that describes the relatively positioned end point.\nfn parse_command_end_rel(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<CommandEndPoint<SVGPathPosition, CSSFloat>, ()> {\n    let coord = parse_coord(iter)?;\n    Ok(CommandEndPoint::ByCoordinate(coord))\n}\n\n/// Parse a pair of values that describe the absolutely positioned curve control point.\nfn parse_control_point_abs(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<ControlPoint<SVGPathPosition, CSSFloat>, ()> {\n    let coord = parse_coord(iter)?;\n    Ok(ControlPoint::Relative(RelativeControlPoint {\n        coord,\n        reference: ControlReference::Origin,\n    }))\n}\n\n/// Parse a pair of values that describe the relatively positioned curve control point.\nfn parse_control_point_rel(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<ControlPoint<SVGPathPosition, CSSFloat>, ()> {\n    let coord = parse_coord(iter)?;\n    Ok(ControlPoint::Relative(RelativeControlPoint {\n        coord,\n        reference: ControlReference::Start,\n    }))\n}\n\n/// Parse a number that describes the absolutely positioned axis end point.\nfn parse_axis_end_abs(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<AxisEndPoint<f32>, ()> {\n    let value = parse_number(iter)?;\n    Ok(AxisEndPoint::ToPosition(AxisPosition::LengthPercent(value)))\n}\n\n/// Parse a number that describes the relatively positioned axis end point.\nfn parse_axis_end_rel(\n    iter: &mut Peekable<Cloned<slice::Iter<u8>>>,\n) -> Result<AxisEndPoint<f32>, ()> {\n    let value = parse_number(iter)?;\n    Ok(AxisEndPoint::ByCoordinate(value))\n}\n\n/// Parse a pair of numbers that describes the size of the ellipse that the arc is taken from.\nfn parse_arc_radii(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> Result<ArcRadii<CSSFloat>, ()> {\n    let coord = parse_coord(iter)?;\n    Ok(ArcRadii {\n        rx: coord.x,\n        ry: Some(coord.y).into(),\n    })\n}\n\n/// This is a special version which parses the number for SVG Path. e.g. \"M 0.6.5\" should be parsed\n/// as MoveTo with a coordinate of (\"0.6\", \".5\"), instead of treating 0.6.5 as a non-valid floating\n/// point number. In other words, the logic here is similar with that of\n/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the\n/// input is a Peekable and we only accept an integer of a floating point number.\n///\n/// The \"number\" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF\nfn parse_number(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> Result<CSSFloat, ()> {\n    // 1. Check optional sign.\n    let sign = if iter\n        .peek()\n        .map_or(false, |&sign| sign == b'+' || sign == b'-')\n    {\n        if iter.next().unwrap() == b'-' {\n            -1.\n        } else {\n            1.\n        }\n    } else {\n        1.\n    };\n\n    // 2. Check integer part.\n    let mut integral_part: f64 = 0.;\n    let got_dot = if !iter.peek().map_or(false, |&n| n == b'.') {\n        // If the first digit in integer part is neither a dot nor a digit, this is not a number.\n        if iter.peek().map_or(true, |n| !n.is_ascii_digit()) {\n            return Err(());\n        }\n\n        while iter.peek().map_or(false, |n| n.is_ascii_digit()) {\n            integral_part = integral_part * 10. + (iter.next().unwrap() - b'0') as f64;\n        }\n\n        iter.peek().map_or(false, |&n| n == b'.')\n    } else {\n        true\n    };\n\n    // 3. Check fractional part.\n    let mut fractional_part: f64 = 0.;\n    if got_dot {\n        // Consume '.'.\n        iter.next();\n        // If the first digit in fractional part is not a digit, this is not a number.\n        if iter.peek().map_or(true, |n| !n.is_ascii_digit()) {\n            return Err(());\n        }\n\n        let mut factor = 0.1;\n        while iter.peek().map_or(false, |n| n.is_ascii_digit()) {\n            fractional_part += (iter.next().unwrap() - b'0') as f64 * factor;\n            factor *= 0.1;\n        }\n    }\n\n    let mut value = sign * (integral_part + fractional_part);\n\n    // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to\n    //    treat the numbers after 'E' or 'e' are in the exponential part.\n    if iter.peek().map_or(false, |&exp| exp == b'E' || exp == b'e') {\n        // Consume 'E' or 'e'.\n        iter.next();\n        let exp_sign = if iter\n            .peek()\n            .map_or(false, |&sign| sign == b'+' || sign == b'-')\n        {\n            if iter.next().unwrap() == b'-' {\n                -1.\n            } else {\n                1.\n            }\n        } else {\n            1.\n        };\n\n        let mut exp: f64 = 0.;\n        while iter.peek().map_or(false, |n| n.is_ascii_digit()) {\n            exp = exp * 10. + (iter.next().unwrap() - b'0') as f64;\n        }\n\n        value *= f64::powf(10., exp * exp_sign);\n    }\n\n    if value.is_finite() {\n        Ok(value.min(f32::MAX as f64).max(f32::MIN as f64) as CSSFloat)\n    } else {\n        Err(())\n    }\n}\n\n/// Skip all svg whitespaces, and return true if |iter| hasn't finished.\n#[inline]\nfn skip_wsp(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> bool {\n    // Note: SVG 1.1 defines the whitespaces as \\u{9}, \\u{20}, \\u{A}, \\u{D}.\n    //       However, SVG 2 has one extra whitespace: \\u{C}.\n    //       Therefore, we follow the newest spec for the definition of whitespace,\n    //       i.e. \\u{9}, \\u{20}, \\u{A}, \\u{C}, \\u{D}.\n    while iter.peek().map_or(false, |c| c.is_ascii_whitespace()) {\n        iter.next();\n    }\n    iter.peek().is_some()\n}\n\n/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished.\n#[inline]\nfn skip_comma_wsp(iter: &mut Peekable<Cloned<slice::Iter<u8>>>) -> bool {\n    if !skip_wsp(iter) {\n        return false;\n    }\n\n    if *iter.peek().unwrap() != b',' {\n        return true;\n    }\n    iter.next();\n\n    skip_wsp(iter)\n}\n"
  },
  {
    "path": "style/values/specified/table.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values related to tables.\nuse crate::derives::*;\n\n/// Specified values for the `caption-side` property.\n///\n/// Note that despite having \"physical\" names, these are actually interpreted\n/// according to the table's writing-mode: Top and Bottom are treated as\n/// block-start and -end respectively.\n///\n/// https://drafts.csswg.org/css-tables/#propdef-caption-side\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Ord,\n    Parse,\n    PartialEq,\n    PartialOrd,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum CaptionSide {\n    Top,\n    Bottom,\n}\n"
  },
  {
    "path": "style/values/specified/text.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for text properties.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::properties::longhands::writing_mode::computed_value::T as SpecifiedWritingMode;\nuse crate::values::computed;\nuse crate::values::computed::text::TextEmphasisStyle as ComputedTextEmphasisStyle;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::generics::text::{\n    GenericHyphenateLimitChars, GenericInitialLetter, GenericTextDecorationInset,\n    GenericTextDecorationLength, GenericTextIndent,\n};\nuse crate::values::generics::NumberOrAuto;\nuse crate::values::specified::length::{Length, LengthPercentage};\nuse crate::values::specified::{AllowQuirks, Integer, Number};\nuse crate::Zero;\nuse cssparser::Parser;\nuse icu_segmenter::GraphemeClusterSegmenter;\nuse std::fmt::{self, Write};\nuse style_traits::values::SequenceWriter;\nuse style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};\nuse style_traits::{KeywordsCollectFn, SpecifiedValueInfo};\n\n/// A specified type for the `initial-letter` property.\npub type InitialLetter = GenericInitialLetter<Number, Integer>;\n\n/// A spacing value used by either the `letter-spacing` or `word-spacing` properties.\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\npub enum Spacing {\n    /// `normal`\n    Normal,\n    /// `<value>`\n    Value(LengthPercentage),\n}\n\nimpl Parse for Spacing {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(Spacing::Normal);\n        }\n        LengthPercentage::parse_quirky(context, input, AllowQuirks::Yes).map(Spacing::Value)\n    }\n}\n\n/// A specified value for the `letter-spacing` property.\n#[derive(\n    Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub struct LetterSpacing(pub Spacing);\n\nimpl ToComputedValue for LetterSpacing {\n    type ComputedValue = computed::LetterSpacing;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        use computed::text::GenericLetterSpacing;\n        match self.0 {\n            Spacing::Normal => GenericLetterSpacing(computed::LengthPercentage::zero()),\n            Spacing::Value(ref v) => GenericLetterSpacing(v.to_computed_value(context)),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        if computed.0.is_zero() {\n            return LetterSpacing(Spacing::Normal);\n        }\n        LetterSpacing(Spacing::Value(ToComputedValue::from_computed_value(\n            &computed.0,\n        )))\n    }\n}\n\n/// A specified value for the `word-spacing` property.\n#[derive(\n    Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped,\n)]\npub struct WordSpacing(pub Spacing);\n\nimpl ToComputedValue for WordSpacing {\n    type ComputedValue = computed::WordSpacing;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match self.0 {\n            Spacing::Normal => computed::LengthPercentage::zero(),\n            Spacing::Value(ref v) => v.to_computed_value(context),\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        WordSpacing(Spacing::Value(ToComputedValue::from_computed_value(\n            computed,\n        )))\n    }\n}\n\n/// A value for the `hyphenate-character` property.\n#[derive(\n    Clone,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n#[typed(todo_derive_fields)]\npub enum HyphenateCharacter {\n    /// `auto`\n    Auto,\n    /// `<string>`\n    String(crate::OwnedStr),\n}\n\n/// A value for the `hyphenate-limit-chars` property.\npub type HyphenateLimitChars = GenericHyphenateLimitChars<Integer>;\n\nimpl Parse for HyphenateLimitChars {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        type IntegerOrAuto = NumberOrAuto<Integer>;\n\n        let total_word_length = IntegerOrAuto::parse(context, input)?;\n        let pre_hyphen_length = input\n            .try_parse(|i| IntegerOrAuto::parse(context, i))\n            .unwrap_or(IntegerOrAuto::Auto);\n        let post_hyphen_length = input\n            .try_parse(|i| IntegerOrAuto::parse(context, i))\n            .unwrap_or(pre_hyphen_length);\n        Ok(Self {\n            total_word_length,\n            pre_hyphen_length,\n            post_hyphen_length,\n        })\n    }\n}\n\nimpl Parse for InitialLetter {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"normal\"))\n            .is_ok()\n        {\n            return Ok(Self::normal());\n        }\n        let size = Number::parse_at_least_one(context, input)?;\n        let sink = input\n            .try_parse(|i| Integer::parse_positive(context, i))\n            .unwrap_or_else(|_| crate::Zero::zero());\n        Ok(Self { size, sink })\n    }\n}\n\n/// A generic value for the `text-overflow` property.\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C, u8)]\npub enum TextOverflowSide {\n    /// Clip inline content.\n    Clip,\n    /// Render ellipsis to represent clipped inline content.\n    Ellipsis,\n    /// Render a given string to represent clipped inline content.\n    String(crate::values::AtomString),\n}\n\n#[derive(\n    Clone,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[typed(todo_derive_fields)]\n/// text-overflow.\n/// When the specified value only has one side, that's the \"second\"\n/// side, and the sides are logical, so \"second\" means \"end\".  The\n/// start side is Clip in that case.\n///\n/// When the specified value has two sides, those are our \"first\"\n/// and \"second\" sides, and they are physical sides (\"left\" and\n/// \"right\").\npub struct TextOverflow {\n    /// First side\n    pub first: TextOverflowSide,\n    /// Second side\n    pub second: TextOverflowSide,\n    /// True if the specified value only has one side.\n    pub sides_are_logical: bool,\n}\n\nimpl Parse for TextOverflow {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<TextOverflow, ParseError<'i>> {\n        let first = TextOverflowSide::parse(context, input)?;\n        Ok(\n            if let Ok(second) = input.try_parse(|input| TextOverflowSide::parse(context, input)) {\n                Self {\n                    first,\n                    second,\n                    sides_are_logical: false,\n                }\n            } else {\n                Self {\n                    first: TextOverflowSide::Clip,\n                    second: first,\n                    sides_are_logical: true,\n                }\n            },\n        )\n    }\n}\n\nimpl TextOverflow {\n    /// Returns the initial `text-overflow` value\n    pub fn get_initial_value() -> TextOverflow {\n        TextOverflow {\n            first: TextOverflowSide::Clip,\n            second: TextOverflowSide::Clip,\n            sides_are_logical: true,\n        }\n    }\n}\n\nimpl ToCss for TextOverflow {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.sides_are_logical {\n            debug_assert_eq!(self.first, TextOverflowSide::Clip);\n            self.second.to_css(dest)?;\n        } else {\n            self.first.to_css(dest)?;\n            dest.write_char(' ')?;\n            self.second.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(\n    feature = \"gecko\",\n    css(bitflags(\n        single = \"none,spelling-error,grammar-error\",\n        mixed = \"underline,overline,line-through,blink\",\n    ))\n)]\n#[cfg_attr(\n    not(feature = \"gecko\"),\n    css(bitflags(single = \"none\", mixed = \"underline,overline,line-through,blink\",))\n)]\n#[repr(C)]\n/// Specified keyword values for the text-decoration-line property.\npub struct TextDecorationLine(u8);\nbitflags! {\n    impl TextDecorationLine: u8 {\n        /// No text decoration line is specified.\n        const NONE = 0;\n        /// underline\n        const UNDERLINE = 1 << 0;\n        /// overline\n        const OVERLINE = 1 << 1;\n        /// line-through\n        const LINE_THROUGH = 1 << 2;\n        /// blink\n        const BLINK = 1 << 3;\n        /// spelling-error\n        const SPELLING_ERROR = 1 << 4;\n        /// grammar-error\n        const GRAMMAR_ERROR = 1 << 5;\n        /// Only set by presentation attributes\n        ///\n        /// Setting this will mean that text-decorations use the color\n        /// specified by `color` in quirks mode.\n        ///\n        /// For example, this gives <a href=foo><font color=\"red\">text</font></a>\n        /// a red text decoration\n        #[cfg(feature = \"gecko\")]\n        const COLOR_OVERRIDE = 1 << 7;\n    }\n}\n\nimpl Default for TextDecorationLine {\n    fn default() -> Self {\n        TextDecorationLine::NONE\n    }\n}\n\nimpl TextDecorationLine {\n    #[inline]\n    /// Returns the initial value of text-decoration-line\n    pub fn none() -> Self {\n        TextDecorationLine::NONE\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(C)]\n/// Specified keyword values for case transforms in the text-transform property. (These are exclusive.)\npub enum TextTransformCase {\n    /// No case transform.\n    None,\n    /// All uppercase.\n    Uppercase,\n    /// All lowercase.\n    Lowercase,\n    /// Capitalize each word.\n    Capitalize,\n    /// Automatic italicization of math variables.\n    #[cfg(feature = \"gecko\")]\n    MathAuto,\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[cfg_attr(\n    feature = \"gecko\",\n    css(bitflags(\n        single = \"none,math-auto\",\n        mixed = \"uppercase,lowercase,capitalize,full-width,full-size-kana\",\n        validate_mixed = \"Self::validate_mixed_flags\",\n    ))\n)]\n#[cfg_attr(\n    not(feature = \"gecko\"),\n    css(bitflags(\n        single = \"none\",\n        mixed = \"uppercase,lowercase,capitalize,full-width,full-size-kana\",\n        validate_mixed = \"Self::validate_mixed_flags\",\n    ))\n)]\n#[repr(C)]\n/// Specified value for the text-transform property.\n/// (The spec grammar gives\n/// `none | math-auto | [capitalize | uppercase | lowercase] || full-width || full-size-kana`.)\n/// https://drafts.csswg.org/css-text-4/#text-transform-property\npub struct TextTransform(u8);\nbitflags! {\n    impl TextTransform: u8 {\n        /// none\n        const NONE = 0;\n        /// All uppercase.\n        const UPPERCASE = 1 << 0;\n        /// All lowercase.\n        const LOWERCASE = 1 << 1;\n        /// Capitalize each word.\n        const CAPITALIZE = 1 << 2;\n        /// Automatic italicization of math variables.\n        #[cfg(feature = \"gecko\")]\n        const MATH_AUTO = 1 << 3;\n\n        /// All the case transforms, which are exclusive with each other.\n        #[cfg(feature = \"gecko\")]\n        const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0 | Self::MATH_AUTO.0;\n        /// All the case transforms, which are exclusive with each other.\n        #[cfg(feature = \"servo\")]\n        const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0;\n\n        /// full-width\n        const FULL_WIDTH = 1 << 4;\n        /// full-size-kana\n        const FULL_SIZE_KANA = 1 << 5;\n    }\n}\n\nimpl TextTransform {\n    /// Returns the initial value of text-transform\n    #[inline]\n    pub fn none() -> Self {\n        Self::NONE\n    }\n\n    /// Returns whether the value is 'none'\n    #[inline]\n    pub fn is_none(self) -> bool {\n        self == Self::NONE\n    }\n\n    fn validate_mixed_flags(&self) -> bool {\n        let case = self.intersection(Self::CASE_TRANSFORMS);\n        // Case bits are exclusive with each other.\n        case.is_empty() || case.bits().is_power_of_two()\n    }\n\n    /// Returns the corresponding TextTransformCase.\n    pub fn case(&self) -> TextTransformCase {\n        match *self & Self::CASE_TRANSFORMS {\n            Self::NONE => TextTransformCase::None,\n            Self::UPPERCASE => TextTransformCase::Uppercase,\n            Self::LOWERCASE => TextTransformCase::Lowercase,\n            Self::CAPITALIZE => TextTransformCase::Capitalize,\n            #[cfg(feature = \"gecko\")]\n            Self::MATH_AUTO => TextTransformCase::MathAuto,\n            _ => unreachable!(\"Case bits are exclusive with each other\"),\n        }\n    }\n}\n\n/// Specified and computed value of text-align-last.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum TextAlignLast {\n    Auto,\n    Start,\n    End,\n    Left,\n    Right,\n    Center,\n    Justify,\n}\n\n/// Specified value of text-align keyword value.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\n#[repr(u8)]\npub enum TextAlignKeyword {\n    Start,\n    Left,\n    Right,\n    Center,\n    Justify,\n    End,\n    #[parse(aliases = \"-webkit-center\")]\n    MozCenter,\n    #[parse(aliases = \"-webkit-left\")]\n    MozLeft,\n    #[parse(aliases = \"-webkit-right\")]\n    MozRight,\n}\n\n/// Specified value of text-align property.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToShmem,\n    ToTyped,\n)]\n#[typed(todo_derive_fields)]\npub enum TextAlign {\n    /// Keyword value of text-align property.\n    Keyword(TextAlignKeyword),\n    /// `match-parent` value of text-align property. It has a different handling\n    /// unlike other keywords.\n    MatchParent,\n    /// This is how we implement the following HTML behavior from\n    /// https://html.spec.whatwg.org/#tables-2:\n    ///\n    ///     User agents are expected to have a rule in their user agent style sheet\n    ///     that matches th elements that have a parent node whose computed value\n    ///     for the 'text-align' property is its initial value, whose declaration\n    ///     block consists of just a single declaration that sets the 'text-align'\n    ///     property to the value 'center'.\n    ///\n    /// Since selectors can't depend on the ancestor styles, we implement it with a\n    /// magic value that computes to the right thing. Since this is an\n    /// implementation detail, it shouldn't be exposed to web content.\n    #[parse(condition = \"ParserContext::chrome_rules_enabled\")]\n    MozCenterOrInherit,\n}\n\nimpl ToComputedValue for TextAlign {\n    type ComputedValue = TextAlignKeyword;\n\n    #[inline]\n    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {\n        match *self {\n            TextAlign::Keyword(key) => key,\n            TextAlign::MatchParent => {\n                // on the root <html> element we should still respect the dir\n                // but the parent dir of that element is LTR even if it's <html dir=rtl>\n                // and will only be RTL if certain prefs have been set.\n                // In that case, the default behavior here will set it to left,\n                // but we want to set it to right -- instead set it to the default (`start`),\n                // which will do the right thing in this case (but not the general case)\n                if _context.builder.is_root_element {\n                    return TextAlignKeyword::Start;\n                }\n                let parent = _context\n                    .builder\n                    .get_parent_inherited_text()\n                    .clone_text_align();\n                let ltr = _context.builder.inherited_writing_mode().is_bidi_ltr();\n                match (parent, ltr) {\n                    (TextAlignKeyword::Start, true) => TextAlignKeyword::Left,\n                    (TextAlignKeyword::Start, false) => TextAlignKeyword::Right,\n                    (TextAlignKeyword::End, true) => TextAlignKeyword::Right,\n                    (TextAlignKeyword::End, false) => TextAlignKeyword::Left,\n                    _ => parent,\n                }\n            },\n            TextAlign::MozCenterOrInherit => {\n                let parent = _context\n                    .builder\n                    .get_parent_inherited_text()\n                    .clone_text_align();\n                if parent == TextAlignKeyword::Start {\n                    TextAlignKeyword::Center\n                } else {\n                    parent\n                }\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        TextAlign::Keyword(*computed)\n    }\n}\n\nfn fill_mode_is_default_and_shape_exists(\n    fill: &TextEmphasisFillMode,\n    shape: &Option<TextEmphasisShapeKeyword>,\n) -> bool {\n    shape.is_some() && fill.is_filled()\n}\n\n/// Specified value of text-emphasis-style property.\n///\n/// https://drafts.csswg.org/css-text-decor/#propdef-text-emphasis-style\n#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, ToTyped)]\n#[allow(missing_docs)]\n#[typed(todo_derive_fields)]\npub enum TextEmphasisStyle {\n    /// [ <fill> || <shape> ]\n    Keyword {\n        #[css(contextual_skip_if = \"fill_mode_is_default_and_shape_exists\")]\n        fill: TextEmphasisFillMode,\n        shape: Option<TextEmphasisShapeKeyword>,\n    },\n    /// `none`\n    None,\n    /// `<string>` (of which only the first grapheme cluster will be used).\n    String(crate::OwnedStr),\n}\n\n/// Fill mode for the text-emphasis-style property\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum TextEmphasisFillMode {\n    /// `filled`\n    Filled,\n    /// `open`\n    Open,\n}\n\nimpl TextEmphasisFillMode {\n    /// Whether the value is `filled`.\n    #[inline]\n    pub fn is_filled(&self) -> bool {\n        matches!(*self, TextEmphasisFillMode::Filled)\n    }\n}\n\n/// Shape keyword for the text-emphasis-style property\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum TextEmphasisShapeKeyword {\n    /// `dot`\n    Dot,\n    /// `circle`\n    Circle,\n    /// `double-circle`\n    DoubleCircle,\n    /// `triangle`\n    Triangle,\n    /// `sesame`\n    Sesame,\n}\n\nimpl ToComputedValue for TextEmphasisStyle {\n    type ComputedValue = ComputedTextEmphasisStyle;\n\n    #[inline]\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            TextEmphasisStyle::Keyword { fill, shape } => {\n                let shape = shape.unwrap_or_else(|| {\n                    // FIXME(emilio, bug 1572958): This should set the\n                    // rule_cache_conditions properly.\n                    //\n                    // Also should probably use WritingMode::is_vertical rather\n                    // than the computed value of the `writing-mode` property.\n                    if context.style().get_inherited_box().clone_writing_mode()\n                        == SpecifiedWritingMode::HorizontalTb\n                    {\n                        TextEmphasisShapeKeyword::Circle\n                    } else {\n                        TextEmphasisShapeKeyword::Sesame\n                    }\n                });\n                ComputedTextEmphasisStyle::Keyword { fill, shape }\n            },\n            TextEmphasisStyle::None => ComputedTextEmphasisStyle::None,\n            TextEmphasisStyle::String(ref s) => {\n                // FIXME(emilio): Doing this at computed value time seems wrong.\n                // The spec doesn't say that this should be a computed-value\n                // time operation. This is observable from getComputedStyle().\n                //\n                // Note that the first grapheme cluster boundary should always be the start of the string.\n                let first_grapheme_end = GraphemeClusterSegmenter::new()\n                    .segment_str(s)\n                    .nth(1)\n                    .unwrap_or(0);\n                ComputedTextEmphasisStyle::String(s[0..first_grapheme_end].to_string().into())\n            },\n        }\n    }\n\n    #[inline]\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        match *computed {\n            ComputedTextEmphasisStyle::Keyword { fill, shape } => TextEmphasisStyle::Keyword {\n                fill,\n                shape: Some(shape),\n            },\n            ComputedTextEmphasisStyle::None => TextEmphasisStyle::None,\n            ComputedTextEmphasisStyle::String(ref string) => {\n                TextEmphasisStyle::String(string.clone())\n            },\n        }\n    }\n}\n\nimpl Parse for TextEmphasisStyle {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(TextEmphasisStyle::None);\n        }\n\n        if let Ok(s) = input.try_parse(|i| i.expect_string().map(|s| s.as_ref().to_owned())) {\n            // Handle <string>\n            return Ok(TextEmphasisStyle::String(s.into()));\n        }\n\n        // Handle a pair of keywords\n        let mut shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();\n        let fill = input.try_parse(TextEmphasisFillMode::parse).ok();\n        if shape.is_none() {\n            shape = input.try_parse(TextEmphasisShapeKeyword::parse).ok();\n        }\n\n        if shape.is_none() && fill.is_none() {\n            return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n        }\n\n        // If a shape keyword is specified but neither filled nor open is\n        // specified, filled is assumed.\n        let fill = fill.unwrap_or(TextEmphasisFillMode::Filled);\n\n        // We cannot do the same because the default `<shape>` depends on the\n        // computed writing-mode.\n        Ok(TextEmphasisStyle::Keyword { fill, shape })\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n#[css(bitflags(\n    single = \"auto\",\n    mixed = \"over,under,left,right\",\n    validate_mixed = \"Self::validate_and_simplify\"\n))]\n/// Values for text-emphasis-position:\n/// <https://drafts.csswg.org/css-text-decor/#text-emphasis-position-property>\npub struct TextEmphasisPosition(u8);\nbitflags! {\n    impl TextEmphasisPosition: u8 {\n        /// Automatically choose mark position based on language.\n        const AUTO = 1 << 0;\n        /// Draw marks over the text in horizontal writing mode.\n        const OVER = 1 << 1;\n        /// Draw marks under the text in horizontal writing mode.\n        const UNDER = 1 << 2;\n        /// Draw marks to the left of the text in vertical writing mode.\n        const LEFT = 1 << 3;\n        /// Draw marks to the right of the text in vertical writing mode.\n        const RIGHT = 1 << 4;\n    }\n}\n\nimpl TextEmphasisPosition {\n    fn validate_and_simplify(&mut self) -> bool {\n        // Require one but not both of 'over' and 'under'.\n        if self.intersects(Self::OVER) == self.intersects(Self::UNDER) {\n            return false;\n        }\n\n        // If 'left' is present, 'right' must be absent.\n        if self.intersects(Self::LEFT) {\n            return !self.intersects(Self::RIGHT);\n        }\n\n        self.remove(Self::RIGHT); // Right is the default\n        true\n    }\n}\n\n/// Values for the `word-break` property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum WordBreak {\n    Normal,\n    BreakAll,\n    KeepAll,\n    /// The break-word value, needed for compat.\n    ///\n    /// Specifying `word-break: break-word` makes `overflow-wrap` behave as\n    /// `anywhere`, and `word-break` behave like `normal`.\n    #[cfg(feature = \"gecko\")]\n    BreakWord,\n}\n\n/// Values for the `text-justify` CSS property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum TextJustify {\n    Auto,\n    None,\n    InterWord,\n    // See https://drafts.csswg.org/css-text-3/#valdef-text-justify-distribute\n    // and https://github.com/w3c/csswg-drafts/issues/6156 for the alias.\n    #[parse(aliases = \"distribute\")]\n    InterCharacter,\n}\n\n/// Values for the `-moz-control-character-visibility` CSS property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum MozControlCharacterVisibility {\n    Hidden,\n    Visible,\n}\n\n#[cfg(feature = \"gecko\")]\nimpl Default for MozControlCharacterVisibility {\n    fn default() -> Self {\n        if static_prefs::pref!(\"layout.css.control-characters.visible\") {\n            Self::Visible\n        } else {\n            Self::Hidden\n        }\n    }\n}\n\n/// Values for the `line-break` property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum LineBreak {\n    Auto,\n    Loose,\n    Normal,\n    Strict,\n    Anywhere,\n}\n\n/// Values for the `overflow-wrap` property.\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum OverflowWrap {\n    Normal,\n    BreakWord,\n    Anywhere,\n}\n\n/// A specified value for the `text-indent` property\n/// which takes the grammar of [<length-percentage>] && hanging? && each-line?\n///\n/// https://drafts.csswg.org/css-text/#propdef-text-indent\npub type TextIndent = GenericTextIndent<LengthPercentage>;\n\nimpl Parse for TextIndent {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut length = None;\n        let mut hanging = false;\n        let mut each_line = false;\n\n        // The length-percentage and the two possible keywords can occur in any order.\n        while !input.is_exhausted() {\n            // If we haven't seen a length yet, try to parse one.\n            if length.is_none() {\n                if let Ok(len) = input\n                    .try_parse(|i| LengthPercentage::parse_quirky(context, i, AllowQuirks::Yes))\n                {\n                    length = Some(len);\n                    continue;\n                }\n            }\n\n            // Servo doesn't support the keywords, so just break and let the caller deal with it.\n            if cfg!(feature = \"servo\") {\n                break;\n            }\n\n            // Check for the keywords (boolean flags).\n            try_match_ident_ignore_ascii_case! { input,\n                \"hanging\" if !hanging => hanging = true,\n                \"each-line\" if !each_line => each_line = true,\n            }\n        }\n\n        // The length-percentage value is required for the declaration to be valid.\n        if let Some(length) = length {\n            Ok(Self {\n                length,\n                hanging,\n                each_line,\n            })\n        } else {\n            Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n        }\n    }\n}\n\n/// Implements text-decoration-skip-ink which takes the keywords auto | none | all\n///\n/// https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property\n#[repr(u8)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum TextDecorationSkipInk {\n    Auto,\n    None,\n    All,\n}\n\n/// Implements type for `text-decoration-thickness` property\npub type TextDecorationLength = GenericTextDecorationLength<LengthPercentage>;\n\nimpl TextDecorationLength {\n    /// `Auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        GenericTextDecorationLength::Auto\n    }\n\n    /// Whether this is the `Auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, GenericTextDecorationLength::Auto)\n    }\n}\n\n/// Implements type for `text-decoration-inset` property\npub type TextDecorationInset = GenericTextDecorationInset<Length>;\n\nimpl TextDecorationInset {\n    /// `Auto` value.\n    #[inline]\n    pub fn auto() -> Self {\n        GenericTextDecorationInset::Auto\n    }\n\n    /// Whether this is the `Auto` value.\n    #[inline]\n    pub fn is_auto(&self) -> bool {\n        matches!(*self, GenericTextDecorationInset::Auto)\n    }\n}\n\nimpl Parse for TextDecorationInset {\n    fn parse<'i, 't>(\n        ctx: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if let Ok(start) = input.try_parse(|i| Length::parse(ctx, i)) {\n            let end = input.try_parse(|i| Length::parse(ctx, i));\n            let end = end.unwrap_or_else(|_| start.clone());\n            return Ok(TextDecorationInset::Length { start, end });\n        }\n        input.expect_ident_matching(\"auto\")?;\n        Ok(TextDecorationInset::Auto)\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"auto\",\n    mixed = \"from-font,under,left,right\",\n    validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\n/// Specified keyword values for the text-underline-position property.\n/// (Non-exclusive, but not all combinations are allowed: the spec grammar gives\n/// `auto | [ from-font | under ] || [ left | right ]`.)\n/// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property\npub struct TextUnderlinePosition(u8);\nbitflags! {\n    impl TextUnderlinePosition: u8 {\n        /// Use automatic positioning below the alphabetic baseline.\n        const AUTO = 0;\n        /// Use underline position from the first available font.\n        const FROM_FONT = 1 << 0;\n        /// Below the glyph box.\n        const UNDER = 1 << 1;\n        /// In vertical mode, place to the left of the text.\n        const LEFT = 1 << 2;\n        /// In vertical mode, place to the right of the text.\n        const RIGHT = 1 << 3;\n    }\n}\n\nimpl TextUnderlinePosition {\n    fn validate_mixed_flags(&self) -> bool {\n        if self.contains(Self::LEFT | Self::RIGHT) {\n            // left and right can't be mixed together.\n            return false;\n        }\n        if self.contains(Self::FROM_FONT | Self::UNDER) {\n            // from-font and under can't be mixed together either.\n            return false;\n        }\n        true\n    }\n}\n\nimpl ToCss for TextUnderlinePosition {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        if self.is_empty() {\n            return dest.write_str(\"auto\");\n        }\n\n        let mut writer = SequenceWriter::new(dest, \" \");\n        let mut any = false;\n\n        macro_rules! maybe_write {\n            ($ident:ident => $str:expr) => {\n                if self.contains(TextUnderlinePosition::$ident) {\n                    any = true;\n                    writer.raw_item($str)?;\n                }\n            };\n        }\n\n        maybe_write!(FROM_FONT => \"from-font\");\n        maybe_write!(UNDER => \"under\");\n        maybe_write!(LEFT => \"left\");\n        maybe_write!(RIGHT => \"right\");\n\n        debug_assert!(any);\n\n        Ok(())\n    }\n}\n\n/// Values for `ruby-position` property\n#[repr(u8)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[allow(missing_docs)]\npub enum RubyPosition {\n    AlternateOver,\n    AlternateUnder,\n    Over,\n    Under,\n}\n\nimpl Parse for RubyPosition {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<RubyPosition, ParseError<'i>> {\n        // Parse alternate before\n        let alternate = input\n            .try_parse(|i| i.expect_ident_matching(\"alternate\"))\n            .is_ok();\n        if alternate && input.is_exhausted() {\n            return Ok(RubyPosition::AlternateOver);\n        }\n        // Parse over / under\n        let over = try_match_ident_ignore_ascii_case! { input,\n            \"over\" => true,\n            \"under\" => false,\n        };\n        // Parse alternate after\n        let alternate = alternate\n            || input\n                .try_parse(|i| i.expect_ident_matching(\"alternate\"))\n                .is_ok();\n\n        Ok(match (over, alternate) {\n            (true, true) => RubyPosition::AlternateOver,\n            (false, true) => RubyPosition::AlternateUnder,\n            (true, false) => RubyPosition::Over,\n            (false, false) => RubyPosition::Under,\n        })\n    }\n}\n\nimpl ToCss for RubyPosition {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(match self {\n            RubyPosition::AlternateOver => \"alternate\",\n            RubyPosition::AlternateUnder => \"alternate under\",\n            RubyPosition::Over => \"over\",\n            RubyPosition::Under => \"under\",\n        })\n    }\n}\n\nimpl SpecifiedValueInfo for RubyPosition {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        f(&[\"alternate\", \"over\", \"under\"])\n    }\n}\n\n/// Specified value for the text-autospace property\n/// which takes the grammar:\n///     normal | <autospace> | auto\n/// where:\n///     <autospace> = no-autospace |\n///                   [ ideograph-alpha || ideograph-numeric || punctuation ]\n///                   || [ insert | replace ]\n///\n/// https://drafts.csswg.org/css-text-4/#text-autospace-property\n///\n/// Bug 1980111: 'replace' value is not supported yet.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(\n    single = \"normal,auto,no-autospace\",\n    // Bug 1980111: add 'replace' to 'mixed' in the future so that it parses correctly.\n    // Bug 1986500: add 'punctuation' to 'mixed' in the future so that it parses correctly.\n    mixed = \"ideograph-alpha,ideograph-numeric,insert\",\n    // Bug 1980111: Uncomment 'validate_mixed' to support 'replace' value.\n    // validate_mixed = \"Self::validate_mixed_flags\",\n))]\n#[repr(C)]\npub struct TextAutospace(u8);\nbitflags! {\n    impl TextAutospace: u8 {\n        /// No automatic space is inserted.\n        const NO_AUTOSPACE = 0;\n\n        /// The user agent chooses a set of typographically high quality spacing values.\n        const AUTO = 1 << 0;\n\n        /// Same behavior as ideograph-alpha ideograph-numeric.\n        const NORMAL = 1 << 1;\n\n        /// 1/8ic space between ideographic characters and non-ideographic letters.\n        const IDEOGRAPH_ALPHA = 1 << 2;\n\n        /// 1/8ic space between ideographic characters and non-ideographic decimal numerals.\n        const IDEOGRAPH_NUMERIC = 1 << 3;\n\n        /* Bug 1986500: Uncomment the following to support the 'punctuation' value.\n        /// Apply special spacing between letters and punctuation (French).\n        const PUNCTUATION = 1 << 4;\n        */\n\n        /// Auto-spacing is only inserted if no space character is present in the text.\n        const INSERT = 1 << 5;\n\n        /* Bug 1980111: Uncomment the following to support 'replace' value.\n        /// Auto-spacing may replace an existing U+0020 space with custom space.\n        const REPLACE = 1 << 6;\n        */\n    }\n}\n\n/* Bug 1980111: Uncomment the following to support 'replace' value.\nimpl TextAutospace {\n    fn validate_mixed_flags(&self) -> bool {\n        // It's not valid to have both INSERT and REPLACE set.\n        !self.contains(TextAutospace::INSERT | TextAutospace::REPLACE)\n    }\n}\n*/\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\n/// Identifies specific font metrics for use in the <text-edge> typedef.\n///\n/// https://drafts.csswg.org/css-inline-3/#typedef-text-edge\npub enum TextEdgeKeyword {\n    /// Use the text-over baseline/text-under baseline as the over/under edge.\n    Text,\n    /// Use the ideographic-over baseline/ideographic-under baseline as the over/under edge.\n    Ideographic,\n    /// Use the ideographic-ink-over baseline/ideographic-ink-under baseline as the over/under edge.\n    IdeographicInk,\n    /// Use the cap-height baseline as the over edge.\n    Cap,\n    /// Use the x-height baseline as the over edge.\n    Ex,\n    /// Use the alphabetic baseline as the under edge.\n    Alphabetic,\n}\n\nimpl TextEdgeKeyword {\n    fn is_valid_for_over(&self) -> bool {\n        match self {\n            TextEdgeKeyword::Text\n            | TextEdgeKeyword::Ideographic\n            | TextEdgeKeyword::IdeographicInk\n            | TextEdgeKeyword::Cap\n            | TextEdgeKeyword::Ex => true,\n            _ => false,\n        }\n    }\n\n    fn is_valid_for_under(&self) -> bool {\n        match self {\n            TextEdgeKeyword::Text\n            | TextEdgeKeyword::Ideographic\n            | TextEdgeKeyword::IdeographicInk\n            | TextEdgeKeyword::Alphabetic => true,\n            _ => false,\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C)]\n/// The <text-edge> typedef, used by the `line-fit-edge` and\n/// `text-box-edge` properties.\n///\n/// The first value specifies the text over edge; the second value\n/// specifies the text under edge. If only one value is specified,\n/// both edges are assigned that same keyword if possible; else\n/// text is assumed as the missing value.\n///\n/// https://drafts.csswg.org/css-inline-3/#typedef-text-edge\npub struct TextEdge {\n    /// Font metric to use for the text over edge.\n    pub over: TextEdgeKeyword,\n    /// Font metric to use for the text under edge.\n    pub under: TextEdgeKeyword,\n}\n\nimpl Parse for TextEdge {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<TextEdge, ParseError<'i>> {\n        let first = TextEdgeKeyword::parse(input)?;\n\n        if let Ok(second) = input.try_parse(TextEdgeKeyword::parse) {\n            if !first.is_valid_for_over() || !second.is_valid_for_under() {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            return Ok(TextEdge {\n                over: first,\n                under: second,\n            });\n        }\n\n        // https://drafts.csswg.org/css-inline-3/#typedef-text-edge\n        // > If only one value is specified, both edges are assigned that same\n        // > keyword if possible; else 'text' is assumed as the missing value.\n        match (first.is_valid_for_over(), first.is_valid_for_under()) {\n            (true, true) => Ok(TextEdge {\n                over: first,\n                under: first,\n            }),\n            (true, false) => Ok(TextEdge {\n                over: first,\n                under: TextEdgeKeyword::Text,\n            }),\n            (false, true) => Ok(TextEdge {\n                over: TextEdgeKeyword::Text,\n                under: first,\n            }),\n            _ => unreachable!(\"Parsed keyword will be valid for at least one edge\"),\n        }\n    }\n}\n\nimpl ToCss for TextEdge {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        match (self.over, self.under) {\n            (over, TextEdgeKeyword::Text) if !over.is_valid_for_under() => over.to_css(dest),\n            (TextEdgeKeyword::Text, under) if !under.is_valid_for_over() => under.to_css(dest),\n            (over, under) => {\n                over.to_css(dest)?;\n\n                if over != under {\n                    dest.write_char(' ')?;\n                    self.under.to_css(dest)?;\n                }\n\n                Ok(())\n            },\n        }\n    }\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    Hash,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(C, u8)]\n/// Specified value for the `text-box-edge` property.\n///\n/// https://drafts.csswg.org/css-inline-3/#text-box-edge\npub enum TextBoxEdge {\n    /// Uses the value of `line-fit-edge`, interpreting `leading` (the initial value) as `text`.\n    Auto,\n    /// Uses the specified font metrics.\n    TextEdge(TextEdge),\n}\n\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    PartialEq,\n    Parse,\n    Serialize,\n    SpecifiedValueInfo,\n    ToCss,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[css(bitflags(single = \"none,trim-start,trim-end,trim-both\"))]\n#[repr(C)]\n/// Specified value for the `text-box-trim` property.\n///\n/// https://drafts.csswg.org/css-inline-3/#text-box-edge\npub struct TextBoxTrim(u8);\nbitflags! {\n    impl TextBoxTrim: u8 {\n        /// NONE\n        const NONE = 0;\n        /// TRIM_START\n        const TRIM_START = 1 << 0;\n        /// TRIM_END\n        const TRIM_END = 1 << 1;\n        /// TRIM_BOTH\n        const TRIM_BOTH = Self::TRIM_START.0 | Self::TRIM_END.0;\n    }\n}\n\nimpl TextBoxTrim {\n    /// Returns the initial value of text-box-trim\n    #[inline]\n    pub fn none() -> Self {\n        TextBoxTrim::NONE\n    }\n}\n"
  },
  {
    "path": "style/values/specified/time.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified time values.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::time::Time as ComputedTime;\nuse crate::values::computed::{Context, ToComputedValue};\nuse crate::values::specified::calc::CalcNode;\nuse crate::values::CSSFloat;\nuse crate::Zero;\nuse cssparser::{match_ignore_ascii_case, Parser, Token};\nuse std::fmt::{self, Write};\nuse style_traits::values::specified::AllowedNumericType;\nuse style_traits::{\n    CssString, CssWriter, MathSum, NumericValue, ParseError, SpecifiedValueInfo,\n    StyleParseErrorKind, ToCss, ToTyped, TypedValue, UnitValue,\n};\nuse thin_vec::ThinVec;\n\n/// A time value according to CSS-VALUES § 6.2.\n#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem)]\npub struct Time {\n    seconds: CSSFloat,\n    unit: TimeUnit,\n    calc_clamping_mode: Option<AllowedNumericType>,\n}\n\n/// A time unit.\n#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub enum TimeUnit {\n    /// `s`\n    Second,\n    /// `ms`\n    Millisecond,\n}\n\nimpl Time {\n    /// Returns a time value that represents `seconds` seconds.\n    pub fn from_seconds_with_calc_clamping_mode(\n        seconds: CSSFloat,\n        calc_clamping_mode: Option<AllowedNumericType>,\n    ) -> Self {\n        Time {\n            seconds,\n            unit: TimeUnit::Second,\n            calc_clamping_mode,\n        }\n    }\n\n    /// Returns a time value that represents `seconds` seconds.\n    pub fn from_seconds(seconds: CSSFloat) -> Self {\n        Self::from_seconds_with_calc_clamping_mode(seconds, None)\n    }\n\n    /// Returns the time in fractional seconds.\n    pub fn seconds(self) -> CSSFloat {\n        self.seconds\n    }\n\n    /// Returns the unit of the time.\n    #[inline]\n    pub fn unit(&self) -> &'static str {\n        match self.unit {\n            TimeUnit::Second => \"s\",\n            TimeUnit::Millisecond => \"ms\",\n        }\n    }\n\n    /// Return the unitless, raw value.\n    #[inline]\n    pub fn unitless_value(&self) -> CSSFloat {\n        match self.unit {\n            TimeUnit::Second => self.seconds,\n            TimeUnit::Millisecond => self.seconds * 1000.,\n        }\n    }\n\n    /// Return the canonical unit for this value.\n    pub fn canonical_unit(&self) -> Option<&'static str> {\n        Some(\"s\")\n    }\n\n    /// Convert this value to the specified unit, if possible.\n    pub fn to(&self, unit: &str) -> Result<Self, ()> {\n        let unit = match_ignore_ascii_case! { unit,\n            \"s\" => TimeUnit::Second,\n            \"ms\" => TimeUnit::Millisecond,\n             _ => return Err(()),\n        };\n\n        Ok(Time {\n            seconds: self.seconds,\n            unit,\n            calc_clamping_mode: None,\n        })\n    }\n\n    /// Parses a time according to CSS-VALUES § 6.2.\n    pub fn parse_dimension(value: CSSFloat, unit: &str) -> Result<Time, ()> {\n        let (seconds, unit) = match_ignore_ascii_case! { unit,\n            \"s\" => (value, TimeUnit::Second),\n            \"ms\" => (value / 1000.0, TimeUnit::Millisecond),\n            _ => return Err(())\n        };\n\n        Ok(Time {\n            seconds,\n            unit,\n            calc_clamping_mode: None,\n        })\n    }\n\n    fn parse_with_clamping_mode<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        clamping_mode: AllowedNumericType,\n    ) -> Result<Self, ParseError<'i>> {\n        use style_traits::ParsingMode;\n\n        let location = input.current_source_location();\n        match *input.next()? {\n            // Note that we generally pass ParserContext to is_ok() to check\n            // that the ParserMode of the ParserContext allows all numeric\n            // values for SMIL regardless of clamping_mode, but in this Time\n            // value case, the value does not animate for SMIL at all, so we use\n            // ParsingMode::DEFAULT directly.\n            Token::Dimension {\n                value, ref unit, ..\n            } if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {\n                Time::parse_dimension(value, unit)\n                    .map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))\n            },\n            Token::Function(ref name) => {\n                let function = CalcNode::math_function(context, name, location)?;\n                CalcNode::parse_time(context, input, clamping_mode, function)\n            },\n            ref t => return Err(location.new_unexpected_token_error(t.clone())),\n        }\n    }\n\n    /// Parses a non-negative time value.\n    pub fn parse_non_negative<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)\n    }\n}\n\nimpl Zero for Time {\n    #[inline]\n    fn zero() -> Self {\n        Self::from_seconds(0.0)\n    }\n\n    #[inline]\n    fn is_zero(&self) -> bool {\n        // The unit doesn't matter, i.e. `s` and `ms` are the same for zero.\n        self.seconds == 0.0 && self.calc_clamping_mode.is_none()\n    }\n}\n\nimpl ToComputedValue for Time {\n    type ComputedValue = ComputedTime;\n\n    fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {\n        let seconds = self\n            .calc_clamping_mode\n            .map_or(self.seconds(), |mode| mode.clamp(self.seconds()));\n\n        ComputedTime::from_seconds(crate::values::normalize(seconds))\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        Time {\n            seconds: computed.seconds(),\n            unit: TimeUnit::Second,\n            calc_clamping_mode: None,\n        }\n    }\n}\n\nimpl Parse for Time {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)\n    }\n}\n\nimpl ToCss for Time {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        crate::values::serialize_specified_dimension(\n            self.unitless_value(),\n            self.unit(),\n            self.calc_clamping_mode.is_some(),\n            dest,\n        )\n    }\n}\n\nimpl ToTyped for Time {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        let numeric_value = NumericValue::Unit(UnitValue {\n            value: self.unitless_value(),\n            unit: CssString::from(self.unit()),\n        });\n\n        // https://drafts.css-houdini.org/css-typed-om-1/#reify-a-math-expression\n        if self.calc_clamping_mode.is_some() {\n            dest.push(TypedValue::Numeric(NumericValue::Sum(MathSum {\n                values: ThinVec::from([numeric_value]),\n            })));\n        } else {\n            dest.push(TypedValue::Numeric(numeric_value));\n        }\n\n        Ok(())\n    }\n}\n\nimpl SpecifiedValueInfo for Time {}\n"
  },
  {
    "path": "style/values/specified/transform.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for CSS values that are related to transformations.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::computed::{Context, LengthPercentage as ComputedLengthPercentage};\nuse crate::values::computed::{Percentage as ComputedPercentage, ToComputedValue};\nuse crate::values::generics::transform as generic;\nuse crate::values::generics::transform::{Matrix, Matrix3D};\nuse crate::values::specified::position::{\n    HorizontalPositionKeyword, Side, VerticalPositionKeyword,\n};\nuse crate::values::specified::{\n    self, AllowQuirks, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,\n};\nuse crate::Zero;\nuse cssparser::{match_ignore_ascii_case, Parser};\nuse style_traits::{ParseError, StyleParseErrorKind};\n\npub use crate::values::generics::transform::TransformStyle;\n\n/// A single operation in a specified CSS `transform`\npub type TransformOperation =\n    generic::TransformOperation<Angle, Number, Length, Integer, LengthPercentage>;\n\n/// A specified CSS `transform`\npub type Transform = generic::Transform<TransformOperation>;\n\n/// The specified value of a CSS `<transform-origin>`\npub type TransformOrigin = generic::TransformOrigin<\n    OriginComponent<HorizontalPositionKeyword>,\n    OriginComponent<VerticalPositionKeyword>,\n    Length,\n>;\n\n#[cfg(feature = \"gecko\")]\nfn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {\n    true\n}\n\n#[cfg(feature = \"servo\")]\nfn all_transform_boxes_are_enabled(_context: &ParserContext) -> bool {\n    false\n}\n\n/// The specified value of `transform-box`.\n/// https://drafts.csswg.org/css-transforms-1/#transform-box\n// Note: Once we ship everything, we can drop this and just use single_keyword for tranform-box.\n#[allow(missing_docs)]\n#[derive(\n    Animate,\n    Clone,\n    ComputeSquaredDistance,\n    Copy,\n    Debug,\n    Deserialize,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    Serialize,\n    SpecifiedValueInfo,\n    ToAnimatedValue,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum TransformBox {\n    #[parse(condition = \"all_transform_boxes_are_enabled\")]\n    ContentBox,\n    BorderBox,\n    FillBox,\n    #[parse(condition = \"all_transform_boxes_are_enabled\")]\n    StrokeBox,\n    ViewBox,\n}\n\nimpl TransformOrigin {\n    /// Returns the initial specified value for `transform-origin`.\n    #[inline]\n    pub fn initial_value() -> Self {\n        Self::new(\n            OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),\n            OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage(0.5))),\n            Length::zero(),\n        )\n    }\n\n    /// Returns the `0 0` value.\n    pub fn zero_zero() -> Self {\n        Self::new(\n            OriginComponent::Length(LengthPercentage::zero()),\n            OriginComponent::Length(LengthPercentage::zero()),\n            Length::zero(),\n        )\n    }\n}\n\n/// Whether to allow unitless values for perspective in prefixed transform properties.\n///\n/// See: https://github.com/whatwg/compat/issues/100\n#[allow(missing_docs)]\npub enum AllowUnitlessPerspective {\n    No,\n    Yes,\n}\n\nimpl Transform {\n    /// Parse the transform property value, allowing unitless perspective values.\n    ///\n    /// This is used for `-webkit-transform` which allows unitless values for perspective.\n    #[inline]\n    pub(crate) fn parse_legacy<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Self::parse_internal(context, input, AllowUnitlessPerspective::Yes)\n    }\n    /// Internal parse function for deciding if we wish to accept prefixed values or not\n    ///\n    /// `transform` allows unitless zero angles as an exception, see:\n    /// https://github.com/w3c/csswg-drafts/issues/1162\n    fn parse_internal<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n        allow_unitless_perspective: AllowUnitlessPerspective,\n    ) -> Result<Self, ParseError<'i>> {\n        use style_traits::{Separator, Space};\n\n        if input\n            .try_parse(|input| input.expect_ident_matching(\"none\"))\n            .is_ok()\n        {\n            return Ok(generic::Transform::none());\n        }\n\n        Ok(generic::Transform(\n            Space::parse(input, |input| {\n                let function = input.expect_function()?.clone();\n                input.parse_nested_block(|input| {\n                    let location = input.current_source_location();\n                    let result = match_ignore_ascii_case! { &function,\n                        \"matrix\" => {\n                            let a = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let b = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let c = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let d = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            // Standard matrix parsing.\n                            let e = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let f = Number::parse(context, input)?;\n                            Ok(generic::TransformOperation::Matrix(Matrix { a, b, c, d, e, f }))\n                        },\n                        \"matrix3d\" => {\n                            let m11 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m12 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m13 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m14 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m21 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m22 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m23 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m24 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m31 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m32 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m33 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m34 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            // Standard matrix3d parsing.\n                            let m41 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m42 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m43 = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let m44 = Number::parse(context, input)?;\n                            Ok(generic::TransformOperation::Matrix3D(Matrix3D {\n                                m11, m12, m13, m14,\n                                m21, m22, m23, m24,\n                                m31, m32, m33, m34,\n                                m41, m42, m43, m44,\n                            }))\n                        },\n                        \"translate\" => {\n                            let sx = specified::LengthPercentage::parse(context, input)?;\n                            if input.try_parse(|input| input.expect_comma()).is_ok() {\n                                let sy = specified::LengthPercentage::parse(context, input)?;\n                                Ok(generic::TransformOperation::Translate(sx, sy))\n                            } else {\n                                Ok(generic::TransformOperation::Translate(sx, Zero::zero()))\n                            }\n                        },\n                        \"translatex\" => {\n                            let tx = specified::LengthPercentage::parse(context, input)?;\n                            Ok(generic::TransformOperation::TranslateX(tx))\n                        },\n                        \"translatey\" => {\n                            let ty = specified::LengthPercentage::parse(context, input)?;\n                            Ok(generic::TransformOperation::TranslateY(ty))\n                        },\n                        \"translatez\" => {\n                            let tz = specified::Length::parse(context, input)?;\n                            Ok(generic::TransformOperation::TranslateZ(tz))\n                        },\n                        \"translate3d\" => {\n                            let tx = specified::LengthPercentage::parse(context, input)?;\n                            input.expect_comma()?;\n                            let ty = specified::LengthPercentage::parse(context, input)?;\n                            input.expect_comma()?;\n                            let tz = specified::Length::parse(context, input)?;\n                            Ok(generic::TransformOperation::Translate3D(tx, ty, tz))\n                        },\n                        \"scale\" => {\n                            let sx = NumberOrPercentage::parse(context, input)?.to_number();\n                            if input.try_parse(|input| input.expect_comma()).is_ok() {\n                                let sy = NumberOrPercentage::parse(context, input)?.to_number();\n                                Ok(generic::TransformOperation::Scale(sx, sy))\n                            } else {\n                                Ok(generic::TransformOperation::Scale(sx, sx))\n                            }\n                        },\n                        \"scalex\" => {\n                            let sx = NumberOrPercentage::parse(context, input)?.to_number();\n                            Ok(generic::TransformOperation::ScaleX(sx))\n                        },\n                        \"scaley\" => {\n                            let sy = NumberOrPercentage::parse(context, input)?.to_number();\n                            Ok(generic::TransformOperation::ScaleY(sy))\n                        },\n                        \"scalez\" => {\n                            let sz = NumberOrPercentage::parse(context, input)?.to_number();\n                            Ok(generic::TransformOperation::ScaleZ(sz))\n                        },\n                        \"scale3d\" => {\n                            let sx = NumberOrPercentage::parse(context, input)?.to_number();\n                            input.expect_comma()?;\n                            let sy = NumberOrPercentage::parse(context, input)?.to_number();\n                            input.expect_comma()?;\n                            let sz = NumberOrPercentage::parse(context, input)?.to_number();\n                            Ok(generic::TransformOperation::Scale3D(sx, sy, sz))\n                        },\n                        \"rotate\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::Rotate(theta))\n                        },\n                        \"rotatex\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::RotateX(theta))\n                        },\n                        \"rotatey\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::RotateY(theta))\n                        },\n                        \"rotatez\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::RotateZ(theta))\n                        },\n                        \"rotate3d\" => {\n                            let ax = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let ay = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let az = Number::parse(context, input)?;\n                            input.expect_comma()?;\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            // TODO(gw): Check that the axis can be normalized.\n                            Ok(generic::TransformOperation::Rotate3D(ax, ay, az, theta))\n                        },\n                        \"skew\" => {\n                            let ax = specified::Angle::parse_with_unitless(context, input)?;\n                            if input.try_parse(|input| input.expect_comma()).is_ok() {\n                                let ay = specified::Angle::parse_with_unitless(context, input)?;\n                                Ok(generic::TransformOperation::Skew(ax, ay))\n                            } else {\n                                Ok(generic::TransformOperation::Skew(ax, Zero::zero()))\n                            }\n                        },\n                        \"skewx\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::SkewX(theta))\n                        },\n                        \"skewy\" => {\n                            let theta = specified::Angle::parse_with_unitless(context, input)?;\n                            Ok(generic::TransformOperation::SkewY(theta))\n                        },\n                        \"perspective\" => {\n                            let p = match input.try_parse(|input| {\n                                if matches!(allow_unitless_perspective, AllowUnitlessPerspective::Yes) {\n                                    specified::Length::parse_non_negative_quirky(context, input, AllowQuirks::Always)\n                                } else {\n                                    specified::Length::parse_non_negative(context, input)\n                                }\n                            }) {\n                                Ok(p) => generic::PerspectiveFunction::Length(p),\n                                Err(..) => {\n                                    input.expect_ident_matching(\"none\")?;\n                                    generic::PerspectiveFunction::None\n                                }\n                            };\n                            Ok(generic::TransformOperation::Perspective(p))\n                        },\n                        _ => Err(()),\n                    };\n                    result.map_err(|()| {\n                        location.new_custom_error(StyleParseErrorKind::UnexpectedFunction(\n                            function.clone(),\n                        ))\n                    })\n                })\n            })?\n            .into(),\n        ))\n    }\n}\n\nimpl Parse for Transform {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        Transform::parse_internal(context, input, AllowUnitlessPerspective::No)\n    }\n}\n\n/// The specified value of a component of a CSS `<transform-origin>`.\n#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]\npub enum OriginComponent<S> {\n    /// `center`\n    Center,\n    /// `<length-percentage>`\n    Length(LengthPercentage),\n    /// `<side>`\n    Side(S),\n}\n\nimpl Parse for TransformOrigin {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let parse_depth = |input: &mut Parser| {\n            input\n                .try_parse(|i| Length::parse(context, i))\n                .unwrap_or(Length::zero())\n        };\n        match input.try_parse(|i| OriginComponent::parse(context, i)) {\n            Ok(x_origin @ OriginComponent::Center) => {\n                if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {\n                    let depth = parse_depth(input);\n                    return Ok(Self::new(x_origin, y_origin, depth));\n                }\n                let y_origin = OriginComponent::Center;\n                if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {\n                    let x_origin = OriginComponent::Side(x_keyword);\n                    let depth = parse_depth(input);\n                    return Ok(Self::new(x_origin, y_origin, depth));\n                }\n                let depth = Length::from_px(0.);\n                return Ok(Self::new(x_origin, y_origin, depth));\n            },\n            Ok(x_origin) => {\n                if let Ok(y_origin) = input.try_parse(|i| OriginComponent::parse(context, i)) {\n                    let depth = parse_depth(input);\n                    return Ok(Self::new(x_origin, y_origin, depth));\n                }\n                let y_origin = OriginComponent::Center;\n                let depth = Length::from_px(0.);\n                return Ok(Self::new(x_origin, y_origin, depth));\n            },\n            Err(_) => {},\n        }\n        let y_keyword = VerticalPositionKeyword::parse(input)?;\n        let y_origin = OriginComponent::Side(y_keyword);\n        if let Ok(x_keyword) = input.try_parse(HorizontalPositionKeyword::parse) {\n            let x_origin = OriginComponent::Side(x_keyword);\n            let depth = parse_depth(input);\n            return Ok(Self::new(x_origin, y_origin, depth));\n        }\n        if input\n            .try_parse(|i| i.expect_ident_matching(\"center\"))\n            .is_ok()\n        {\n            let x_origin = OriginComponent::Center;\n            let depth = parse_depth(input);\n            return Ok(Self::new(x_origin, y_origin, depth));\n        }\n        let x_origin = OriginComponent::Center;\n        let depth = Length::from_px(0.);\n        Ok(Self::new(x_origin, y_origin, depth))\n    }\n}\n\nimpl<S> ToComputedValue for OriginComponent<S>\nwhere\n    S: Side,\n{\n    type ComputedValue = ComputedLengthPercentage;\n\n    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {\n        match *self {\n            OriginComponent::Center => {\n                ComputedLengthPercentage::new_percent(ComputedPercentage(0.5))\n            },\n            OriginComponent::Length(ref length) => length.to_computed_value(context),\n            OriginComponent::Side(ref keyword) => {\n                let p = ComputedPercentage(if keyword.is_start() { 0. } else { 1. });\n                ComputedLengthPercentage::new_percent(p)\n            },\n        }\n    }\n\n    fn from_computed_value(computed: &Self::ComputedValue) -> Self {\n        OriginComponent::Length(ToComputedValue::from_computed_value(computed))\n    }\n}\n\nimpl<S> OriginComponent<S> {\n    /// `0%`\n    pub fn zero() -> Self {\n        OriginComponent::Length(LengthPercentage::Percentage(ComputedPercentage::zero()))\n    }\n}\n\n/// A specified CSS `rotate`\npub type Rotate = generic::Rotate<Number, Angle>;\n\nimpl Parse for Rotate {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(generic::Rotate::None);\n        }\n\n        // Parse <angle> or [ x | y | z | <number>{3} ] && <angle>.\n        //\n        // The rotate axis and angle could be in any order, so we parse angle twice to cover\n        // two cases. i.e. `<number>{3} <angle>` or `<angle> <number>{3}`\n        let angle = input\n            .try_parse(|i| specified::Angle::parse(context, i))\n            .ok();\n        let axis = input\n            .try_parse(|i| {\n                Ok(try_match_ident_ignore_ascii_case! { i,\n                    \"x\" => (Number::new(1.), Number::new(0.), Number::new(0.)),\n                    \"y\" => (Number::new(0.), Number::new(1.), Number::new(0.)),\n                    \"z\" => (Number::new(0.), Number::new(0.), Number::new(1.)),\n                })\n            })\n            .or_else(|_: ParseError| -> Result<_, ParseError> {\n                input.try_parse(|i| {\n                    Ok((\n                        Number::parse(context, i)?,\n                        Number::parse(context, i)?,\n                        Number::parse(context, i)?,\n                    ))\n                })\n            })\n            .ok();\n        let angle = match angle {\n            Some(a) => a,\n            None => specified::Angle::parse(context, input)?,\n        };\n\n        Ok(match axis {\n            Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),\n            None => generic::Rotate::Rotate(angle),\n        })\n    }\n}\n\n/// A specified CSS `translate`\npub type Translate = generic::Translate<LengthPercentage, Length>;\n\nimpl Parse for Translate {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(generic::Translate::None);\n        }\n\n        let tx = specified::LengthPercentage::parse(context, input)?;\n        if let Ok(ty) = input.try_parse(|i| specified::LengthPercentage::parse(context, i)) {\n            if let Ok(tz) = input.try_parse(|i| specified::Length::parse(context, i)) {\n                // 'translate: <length-percentage> <length-percentage> <length>'\n                return Ok(generic::Translate::Translate(tx, ty, tz));\n            }\n\n            // translate: <length-percentage> <length-percentage>'\n            return Ok(generic::Translate::Translate(\n                tx,\n                ty,\n                specified::Length::zero(),\n            ));\n        }\n\n        // 'translate: <length-percentage> '\n        Ok(generic::Translate::Translate(\n            tx,\n            specified::LengthPercentage::zero(),\n            specified::Length::zero(),\n        ))\n    }\n}\n\n/// A specified CSS `scale`\npub type Scale = generic::Scale<Number>;\n\nimpl Parse for Scale {\n    /// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,\n    /// and then convert into an Number if it's a Percentage.\n    /// https://github.com/w3c/csswg-drafts/pull/4396\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"none\")).is_ok() {\n            return Ok(generic::Scale::None);\n        }\n\n        let sx = NumberOrPercentage::parse(context, input)?.to_number();\n        if let Ok(sy) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {\n            let sy = sy.to_number();\n            if let Ok(sz) = input.try_parse(|i| NumberOrPercentage::parse(context, i)) {\n                // 'scale: <number> <number> <number>'\n                return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));\n            }\n\n            // 'scale: <number> <number>'\n            return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));\n        }\n\n        // 'scale: <number>'\n        Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))\n    }\n}\n"
  },
  {
    "path": "style/values/specified/ui.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Specified types for UI properties.\n\nuse crate::derives::*;\nuse crate::parser::{Parse, ParserContext};\nuse crate::values::generics::ui as generics;\nuse crate::values::specified::color::Color;\nuse crate::values::specified::image::Image;\nuse crate::values::specified::Number;\nuse cssparser::Parser;\nuse std::fmt::{self, Write};\nuse style_traits::{\n    CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss,\n};\n\n/// A specified value for the `cursor` property.\npub type Cursor = generics::GenericCursor<CursorImage>;\n\n/// A specified value for item of `image cursors`.\npub type CursorImage = generics::GenericCursorImage<Image, Number>;\n\nimpl Parse for Cursor {\n    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        let mut images = vec![];\n        loop {\n            match input.try_parse(|input| CursorImage::parse(context, input)) {\n                Ok(image) => images.push(image),\n                Err(_) => break,\n            }\n            input.expect_comma()?;\n        }\n        Ok(Self {\n            images: images.into(),\n            keyword: CursorKind::parse(input)?,\n        })\n    }\n}\n\nimpl Parse for CursorImage {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        use crate::Zero;\n\n        let image = Image::parse_only_url(context, input)?;\n        let mut has_hotspot = false;\n        let mut hotspot_x = Number::zero();\n        let mut hotspot_y = Number::zero();\n\n        if let Ok(x) = input.try_parse(|input| Number::parse(context, input)) {\n            has_hotspot = true;\n            hotspot_x = x;\n            hotspot_y = Number::parse(context, input)?;\n        }\n\n        Ok(Self {\n            image,\n            has_hotspot,\n            hotspot_x,\n            hotspot_y,\n        })\n    }\n}\n\n// This trait is manually implemented because we don't support the whole <image>\n// syntax for cursors\nimpl SpecifiedValueInfo for CursorImage {\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        f(&[\"url\", \"image-set\"]);\n    }\n}\n/// Specified value of `-moz-force-broken-image-icon`\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    MallocSizeOf,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(transparent)]\n#[typed(todo_derive_fields)]\npub struct BoolInteger(pub bool);\n\nimpl BoolInteger {\n    /// Returns 0\n    #[inline]\n    pub fn zero() -> Self {\n        Self(false)\n    }\n}\n\nimpl Parse for BoolInteger {\n    fn parse<'i, 't>(\n        _context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        // We intentionally don't support calc values here.\n        match input.expect_integer()? {\n            0 => Ok(Self(false)),\n            1 => Ok(Self(true)),\n            _ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),\n        }\n    }\n}\n\nimpl ToCss for BoolInteger {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        dest.write_str(if self.0 { \"1\" } else { \"0\" })\n    }\n}\n\n/// A specified value for `scrollbar-color` property\npub type ScrollbarColor = generics::ScrollbarColor<Color>;\n\nimpl Parse for ScrollbarColor {\n    fn parse<'i, 't>(\n        context: &ParserContext,\n        input: &mut Parser<'i, 't>,\n    ) -> Result<Self, ParseError<'i>> {\n        if input.try_parse(|i| i.expect_ident_matching(\"auto\")).is_ok() {\n            return Ok(generics::ScrollbarColor::Auto);\n        }\n        Ok(generics::ScrollbarColor::Colors {\n            thumb: Color::parse(context, input)?,\n            track: Color::parse(context, input)?,\n        })\n    }\n}\n\n/// The specified value for the `user-select` property.\n///\n/// https://drafts.csswg.org/css-ui-4/#propdef-user-select\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum UserSelect {\n    Auto,\n    Text,\n    #[parse(aliases = \"-moz-none\")]\n    None,\n    /// Force selection of all children.\n    All,\n}\n\n/// The keywords allowed in the Cursor property.\n///\n/// https://drafts.csswg.org/css-ui-4/#propdef-cursor\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n)]\n#[repr(u8)]\npub enum CursorKind {\n    None,\n    Default,\n    Pointer,\n    ContextMenu,\n    Help,\n    Progress,\n    Wait,\n    Cell,\n    Crosshair,\n    Text,\n    VerticalText,\n    Alias,\n    Copy,\n    Move,\n    NoDrop,\n    NotAllowed,\n    #[parse(aliases = \"-moz-grab\")]\n    Grab,\n    #[parse(aliases = \"-moz-grabbing\")]\n    Grabbing,\n    EResize,\n    NResize,\n    NeResize,\n    NwResize,\n    SResize,\n    SeResize,\n    SwResize,\n    WResize,\n    EwResize,\n    NsResize,\n    NeswResize,\n    NwseResize,\n    ColResize,\n    RowResize,\n    AllScroll,\n    #[parse(aliases = \"-moz-zoom-in\")]\n    ZoomIn,\n    #[parse(aliases = \"-moz-zoom-out\")]\n    ZoomOut,\n    Auto,\n}\n\n/// The keywords allowed in the -moz-theme property.\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum MozTheme {\n    /// Choose the default (maybe native) rendering.\n    Auto,\n    /// Choose the non-native rendering.\n    NonNative,\n}\n\n/// The pointer-events property\n/// https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum PointerEvents {\n    Auto,\n    None,\n    #[cfg(feature = \"gecko\")]\n    Visiblepainted,\n    #[cfg(feature = \"gecko\")]\n    Visiblefill,\n    #[cfg(feature = \"gecko\")]\n    Visiblestroke,\n    #[cfg(feature = \"gecko\")]\n    Visible,\n    #[cfg(feature = \"gecko\")]\n    Painted,\n    #[cfg(feature = \"gecko\")]\n    Fill,\n    #[cfg(feature = \"gecko\")]\n    Stroke,\n    #[cfg(feature = \"gecko\")]\n    All,\n}\n\n/// Internal property to represent the inert attribute state:\n/// https://html.spec.whatwg.org/multipage/#inert-subtrees\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum Inert {\n    None,\n    Inert,\n}\n\n/// Internal -moz-user-focus property.\n/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus\n#[allow(missing_docs)]\n#[derive(\n    Clone,\n    Copy,\n    Debug,\n    Eq,\n    FromPrimitive,\n    MallocSizeOf,\n    Parse,\n    PartialEq,\n    SpecifiedValueInfo,\n    ToComputedValue,\n    ToCss,\n    ToResolvedValue,\n    ToShmem,\n    ToTyped,\n)]\n#[repr(u8)]\npub enum UserFocus {\n    Normal,\n    None,\n    Ignore,\n}\n"
  },
  {
    "path": "style/values/specified/url.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Common handling for the specified value CSS url() values.\n\nuse crate::values::generics::url::GenericUrlOrNone;\n\n#[cfg(feature = \"gecko\")]\npub use crate::gecko::url::SpecifiedUrl;\n#[cfg(feature = \"servo\")]\npub use crate::servo::url::SpecifiedUrl;\n\n/// Specified <url> | <none>\npub type UrlOrNone = GenericUrlOrNone<SpecifiedUrl>;\n"
  },
  {
    "path": "style.paths",
    "content": "# Filters and renames use git-filter-repo(1) --paths-from-file:\n# https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#_filtering_based_on_many_paths\n\nservo/components/\nservo/rustfmt.toml\n\nregex:servo/components/(.+)==>\\1\nservo/rustfmt.toml==>rustfmt.toml\n"
  },
  {
    "path": "style_derive/Cargo.toml",
    "content": "[package]\nname = \"stylo_derive\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/servo/stylo\"\nedition = \"2021\"\ndescription = \"Derive crate for Stylo CSS engine\"\nreadme = \"../README.md\"\n\n[lib]\npath = \"lib.rs\"\nproc-macro = true\n\n[dependencies]\ndarling = { version = \"0.20\", default-features = false }\nproc-macro2 = \"1\"\nquote = \"1\"\nsyn = { version = \"2\", default-features = false, features = [\"clone-impls\", \"derive\", \"parsing\"] }\nsynstructure = \"0.13\"\n"
  },
  {
    "path": "style_derive/animate.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse darling::util::PathList;\nuse proc_macro2::TokenStream;\nuse quote::TokenStreamExt;\nuse syn::{DeriveInput, WhereClause};\nuse synstructure::{Structure, VariantInfo};\n\npub fn derive(mut input: DeriveInput) -> TokenStream {\n    let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);\n\n    let no_bound = animation_input_attrs.no_bound.unwrap_or_default();\n    let mut where_clause = input.generics.where_clause.take();\n    for param in input.generics.type_params() {\n        if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {\n            cg::add_predicate(\n                &mut where_clause,\n                parse_quote!(#param: crate::values::animated::Animate),\n            );\n        }\n    }\n    let (mut match_body, needs_catchall_branch) = {\n        let s = Structure::new(&input);\n        let needs_catchall_branch = s.variants().len() > 1;\n        let match_body = s.variants().iter().fold(quote!(), |body, variant| {\n            let arm = derive_variant_arm(variant, &mut where_clause);\n            quote! { #body #arm }\n        });\n        (match_body, needs_catchall_branch)\n    };\n\n    input.generics.where_clause = where_clause;\n\n    if needs_catchall_branch {\n        // This ideally shouldn't be needed, but see\n        // https://github.com/rust-lang/rust/issues/68867\n        match_body.append_all(quote! { _ => unsafe {\n            use ::debug_unreachable::debug_unreachable;\n            debug_unreachable!()\n        } });\n    }\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    quote! {\n        impl #impl_generics crate::values::animated::Animate for #name #ty_generics #where_clause {\n            #[allow(unused_variables, unused_imports)]\n            #[inline]\n            fn animate(\n                &self,\n                other: &Self,\n                procedure: crate::values::animated::Procedure,\n            ) -> Result<Self, ()> {\n                if std::mem::discriminant(self) != std::mem::discriminant(other) {\n                    return Err(());\n                }\n                match (self, other) {\n                    #match_body\n                }\n            }\n        }\n    }\n}\n\nfn derive_variant_arm(\n    variant: &VariantInfo,\n    where_clause: &mut Option<WhereClause>,\n) -> TokenStream {\n    let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());\n    let (this_pattern, this_info) = cg::ref_pattern(&variant, \"this\");\n    let (other_pattern, other_info) = cg::ref_pattern(&variant, \"other\");\n\n    if variant_attrs.error {\n        return quote! {\n            (&#this_pattern, &#other_pattern) => Err(()),\n        };\n    }\n\n    let (result_value, result_info) = cg::value(&variant, \"result\");\n    let mut computations = quote!();\n    let iter = result_info.iter().zip(this_info.iter().zip(&other_info));\n    computations.append_all(iter.map(|(result, (this, other))| {\n        let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&result.ast());\n        if field_attrs.field_bound {\n            let ty = &this.ast().ty;\n            cg::add_predicate(\n                where_clause,\n                parse_quote!(#ty: crate::values::animated::Animate),\n            );\n        }\n        if field_attrs.constant {\n            quote! {\n                if #this != #other {\n                    return Err(());\n                }\n                let #result = std::clone::Clone::clone(#this);\n            }\n        } else {\n            quote! {\n                let #result =\n                    crate::values::animated::Animate::animate(#this, #other, procedure)?;\n            }\n        }\n    }));\n\n    quote! {\n        (&#this_pattern, &#other_pattern) => {\n            #computations\n            Ok(#result_value)\n        }\n    }\n}\n\n#[derive(Default, FromDeriveInput)]\n#[darling(attributes(animation), default)]\npub struct AnimationInputAttrs {\n    pub no_bound: Option<PathList>,\n}\n\n#[derive(Default, FromVariant)]\n#[darling(attributes(animation), default)]\npub struct AnimationVariantAttrs {\n    pub error: bool,\n    // Only here because of structs, where the struct definition acts as a\n    // variant itself.\n    pub no_bound: Option<PathList>,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(animation), default)]\npub struct AnimationFieldAttrs {\n    pub constant: bool,\n    pub field_bound: bool,\n}\n"
  },
  {
    "path": "style_derive/cg.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse darling::{FromDeriveInput, FromField, FromVariant};\nuse proc_macro2::{Span, TokenStream};\nuse quote::{quote, TokenStreamExt};\nuse syn::parse_quote;\nuse syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field};\nuse syn::{GenericArgument, GenericParam, Ident, Path};\nuse syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup};\nuse syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};\nuse syn::{Variant, WherePredicate};\nuse synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};\n\n/// Given an input type which has some where clauses already, like:\n///\n/// struct InputType<T>\n/// where\n///     T: Zero,\n/// {\n///     ...\n/// }\n///\n/// Add the necessary `where` clauses so that the output type of a trait\n/// fulfils them.\n///\n/// For example:\n///\n/// ```ignore\n///     <T as ToComputedValue>::ComputedValue: Zero,\n/// ```\n///\n/// This needs to run before adding other bounds to the type parameters.\npub(crate) fn propagate_clauses_to_output_type(\n    where_clause: &mut Option<syn::WhereClause>,\n    generics: &syn::Generics,\n    trait_path: &Path,\n    trait_output: &Ident,\n) {\n    let where_clause = match *where_clause {\n        Some(ref mut clause) => clause,\n        None => return,\n    };\n    let mut extra_bounds = vec![];\n    for pred in &where_clause.predicates {\n        let ty = match *pred {\n            syn::WherePredicate::Type(ref ty) => ty,\n            ref predicate => panic!(\"Unhanded complex where predicate: {:?}\", predicate),\n        };\n\n        let path = match ty.bounded_ty {\n            syn::Type::Path(ref p) => &p.path,\n            ref ty => panic!(\"Unhanded complex where type: {:?}\", ty),\n        };\n\n        assert!(\n            ty.lifetimes.is_none(),\n            \"Unhanded complex lifetime bound: {:?}\",\n            ty,\n        );\n\n        let ident = match path_to_ident(path) {\n            Some(i) => i,\n            None => panic!(\"Unhanded complex where type path: {:?}\", path),\n        };\n\n        if generics.type_params().any(|param| param.ident == *ident) {\n            extra_bounds.push(ty.clone());\n        }\n    }\n\n    for bound in extra_bounds {\n        let ty = bound.bounded_ty;\n        let bounds = bound.bounds;\n        where_clause\n            .predicates\n            .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))\n    }\n}\n\npub(crate) fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {\n    where_clause\n        .get_or_insert(parse_quote!(where))\n        .predicates\n        .push(pred);\n}\n\npub(crate) fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream\nwhere\n    F: FnMut(&BindingInfo) -> TokenStream,\n{\n    fmap2_match(input, bind_style, f, |_| None)\n}\n\npub(crate) fn fmap2_match<F, G>(\n    input: &DeriveInput,\n    bind_style: BindStyle,\n    mut f: F,\n    mut g: G,\n) -> TokenStream\nwhere\n    F: FnMut(&BindingInfo) -> TokenStream,\n    G: FnMut(&BindingInfo) -> Option<TokenStream>,\n{\n    let mut s = synstructure::Structure::new(input);\n    s.variants_mut().iter_mut().for_each(|v| {\n        v.bind_with(|_| bind_style);\n    });\n    s.each_variant(|variant| {\n        let (mapped, mapped_fields) = value(variant, \"mapped\");\n        let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());\n        let mut computations = quote!();\n        computations.append_all(fields_pairs.map(|(field, mapped_field)| {\n            let expr = f(field);\n            quote! { let #mapped_field = #expr; }\n        }));\n        computations.append_all(\n            mapped_fields\n                .iter()\n                .map(|mapped_field| match g(mapped_field) {\n                    Some(expr) => quote! { let #mapped_field = #expr; },\n                    None => quote!(),\n                }),\n        );\n        computations.append_all(mapped);\n        Some(computations)\n    })\n}\n\npub(crate) fn fmap_trait_output(\n    input: &DeriveInput,\n    trait_path: &Path,\n    trait_output: &Ident,\n) -> Path {\n    let segment = PathSegment {\n        ident: input.ident.clone(),\n        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {\n            args: input\n                .generics\n                .params\n                .iter()\n                .map(|arg| match arg {\n                    &GenericParam::Lifetime(ref data) => {\n                        GenericArgument::Lifetime(data.lifetime.clone())\n                    },\n                    &GenericParam::Type(ref data) => {\n                        let ident = &data.ident;\n                        GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))\n                    },\n                    &GenericParam::Const(ref inner) => {\n                        let ident = &inner.ident;\n                        GenericArgument::Const(parse_quote!(#ident))\n                    },\n                })\n                .collect(),\n            colon2_token: Default::default(),\n            gt_token: Default::default(),\n            lt_token: Default::default(),\n        }),\n    };\n    segment.into()\n}\n\npub(crate) fn map_type_params<F>(\n    ty: &Type,\n    params: &[&TypeParam],\n    self_type: &Path,\n    f: &mut F,\n) -> Type\nwhere\n    F: FnMut(&Ident) -> Type,\n{\n    match *ty {\n        Type::Slice(ref inner) => Type::from(TypeSlice {\n            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),\n            ..inner.clone()\n        }),\n        Type::Array(ref inner) => {\n            //ref ty, ref expr) => {\n            Type::from(TypeArray {\n                elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),\n                ..inner.clone()\n            })\n        },\n        ref ty @ Type::Never(_) => ty.clone(),\n        Type::Tuple(ref inner) => Type::from(TypeTuple {\n            elems: inner\n                .elems\n                .iter()\n                .map(|ty| map_type_params(&ty, params, self_type, f))\n                .collect(),\n            ..inner.clone()\n        }),\n        Type::Path(TypePath {\n            qself: None,\n            ref path,\n        }) => {\n            if let Some(ident) = path_to_ident(path) {\n                if params.iter().any(|ref param| &param.ident == ident) {\n                    return f(ident);\n                }\n                if ident == \"Self\" {\n                    return Type::from(TypePath {\n                        qself: None,\n                        path: self_type.clone(),\n                    });\n                }\n            }\n            Type::from(TypePath {\n                qself: None,\n                path: map_type_params_in_path(path, params, self_type, f),\n            })\n        },\n        Type::Path(TypePath {\n            ref qself,\n            ref path,\n        }) => Type::from(TypePath {\n            qself: qself.as_ref().map(|qself| QSelf {\n                ty: Box::new(map_type_params(&qself.ty, params, self_type, f)),\n                position: qself.position,\n                ..qself.clone()\n            }),\n            path: map_type_params_in_path(path, params, self_type, f),\n        }),\n        Type::Paren(ref inner) => Type::from(TypeParen {\n            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),\n            ..inner.clone()\n        }),\n        Type::Group(ref inner) => Type::from(TypeGroup {\n            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),\n            ..inner.clone()\n        }),\n        ref ty => panic!(\"type {:?} cannot be mapped yet\", ty),\n    }\n}\n\nfn map_type_params_in_path<F>(\n    path: &Path,\n    params: &[&TypeParam],\n    self_type: &Path,\n    f: &mut F,\n) -> Path\nwhere\n    F: FnMut(&Ident) -> Type,\n{\n    Path {\n        leading_colon: path.leading_colon,\n        segments: path\n            .segments\n            .iter()\n            .map(|segment| PathSegment {\n                ident: segment.ident.clone(),\n                arguments: match segment.arguments {\n                    PathArguments::AngleBracketed(ref data) => {\n                        PathArguments::AngleBracketed(AngleBracketedGenericArguments {\n                            args: data\n                                .args\n                                .iter()\n                                .map(|arg| match arg {\n                                    ty @ &GenericArgument::Lifetime(_) => ty.clone(),\n                                    &GenericArgument::Type(ref data) => GenericArgument::Type(\n                                        map_type_params(data, params, self_type, f),\n                                    ),\n                                    &GenericArgument::AssocType(ref data) => {\n                                        GenericArgument::AssocType(AssocType {\n                                            ty: map_type_params(&data.ty, params, self_type, f),\n                                            ..data.clone()\n                                        })\n                                    },\n                                    ref arg => panic!(\"arguments {:?} cannot be mapped yet\", arg),\n                                })\n                                .collect(),\n                            ..data.clone()\n                        })\n                    },\n                    ref arg @ PathArguments::None => arg.clone(),\n                    ref parameters => panic!(\"parameters {:?} cannot be mapped yet\", parameters),\n                },\n            })\n            .collect(),\n    }\n}\n\nfn path_to_ident(path: &Path) -> Option<&Ident> {\n    match *path {\n        Path {\n            leading_colon: None,\n            ref segments,\n        } if segments.len() == 1 => {\n            if segments[0].arguments.is_empty() {\n                Some(&segments[0].ident)\n            } else {\n                None\n            }\n        },\n        _ => None,\n    }\n}\n\npub(crate) fn parse_field_attrs<A>(field: &Field) -> A\nwhere\n    A: FromField,\n{\n    match A::from_field(field) {\n        Ok(attrs) => attrs,\n        Err(e) => panic!(\"failed to parse field attributes: {}\", e),\n    }\n}\n\npub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A\nwhere\n    A: FromDeriveInput,\n{\n    match A::from_derive_input(input) {\n        Ok(attrs) => attrs,\n        Err(e) => panic!(\"failed to parse input attributes: {}\", e),\n    }\n}\n\npub(crate) fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A\nwhere\n    A: FromVariant,\n{\n    let v = Variant {\n        ident: variant.ident.clone(),\n        attrs: variant.attrs.to_vec(),\n        fields: variant.fields.clone(),\n        discriminant: variant.discriminant.clone(),\n    };\n    parse_variant_attrs(&v)\n}\n\npub(crate) fn parse_variant_attrs<A>(variant: &Variant) -> A\nwhere\n    A: FromVariant,\n{\n    match A::from_variant(variant) {\n        Ok(attrs) => attrs,\n        Err(e) => panic!(\"failed to parse variant attributes: {}\", e),\n    }\n}\n\npub(crate) fn ref_pattern<'a>(\n    variant: &'a VariantInfo,\n    prefix: &str,\n) -> (TokenStream, Vec<BindingInfo<'a>>) {\n    let mut v = variant.clone();\n    v.bind_with(|_| BindStyle::Ref);\n    v.bindings_mut().iter_mut().for_each(|b| {\n        b.binding = Ident::new(&format!(\"{}_{}\", b.binding, prefix), Span::call_site())\n    });\n    (v.pat(), v.bindings().to_vec())\n}\n\npub(crate) fn value<'a>(\n    variant: &'a VariantInfo,\n    prefix: &str,\n) -> (TokenStream, Vec<BindingInfo<'a>>) {\n    let mut v = variant.clone();\n    v.bindings_mut().iter_mut().for_each(|b| {\n        b.binding = Ident::new(&format!(\"{}_{}\", b.binding, prefix), Span::call_site())\n    });\n    v.bind_with(|_| BindStyle::Move);\n    (v.pat(), v.bindings().to_vec())\n}\n\n/// Transforms \"FooBar\" to \"foo-bar\".\n///\n/// If the first Camel segment is \"Moz\", \"Webkit\", or \"Servo\", the result string\n/// is prepended with \"-\".\npub(crate) fn to_css_identifier(mut camel_case: &str) -> String {\n    camel_case = camel_case.trim_end_matches('_');\n    let mut first = true;\n    let mut result = String::with_capacity(camel_case.len());\n    while let Some(segment) = split_camel_segment(&mut camel_case) {\n        if first {\n            match segment {\n                \"Moz\" | \"Webkit\" | \"Servo\" => first = false,\n                _ => {},\n            }\n        }\n        if !first {\n            result.push('-');\n        }\n        first = false;\n        result.push_str(&segment.to_lowercase());\n    }\n    result\n}\n\n/// Transforms foo-bar to FOO_BAR.\npub(crate) fn to_scream_case(css_case: &str) -> String {\n    css_case.to_uppercase().replace('-', \"_\")\n}\n\n/// Given \"FooBar\", returns \"Foo\" and sets `camel_case` to \"Bar\".\nfn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {\n    let index = match camel_case.chars().next() {\n        None => return None,\n        Some(ch) => ch.len_utf8(),\n    };\n    let end_position = camel_case[index..]\n        .find(char::is_uppercase)\n        .map_or(camel_case.len(), |pos| index + pos);\n    let result = &camel_case[..end_position];\n    *camel_case = &camel_case[end_position..];\n    Some(result)\n}\n"
  },
  {
    "path": "style_derive/compute_squared_distance.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantAttrs};\nuse crate::cg;\nuse proc_macro2::TokenStream;\nuse quote::TokenStreamExt;\nuse syn::{DeriveInput, WhereClause};\nuse synstructure;\n\npub fn derive(mut input: DeriveInput) -> TokenStream {\n    let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);\n    let no_bound = animation_input_attrs.no_bound.unwrap_or_default();\n    let mut where_clause = input.generics.where_clause.take();\n    for param in input.generics.type_params() {\n        if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {\n            cg::add_predicate(\n                &mut where_clause,\n                parse_quote!(#param: crate::values::distance::ComputeSquaredDistance),\n            );\n        }\n    }\n\n    let (mut match_body, needs_catchall_branch) = {\n        let s = synstructure::Structure::new(&input);\n        let needs_catchall_branch = s.variants().len() > 1;\n\n        let match_body = s.variants().iter().fold(quote!(), |body, variant| {\n            let arm = derive_variant_arm(variant, &mut where_clause);\n            quote! { #body #arm }\n        });\n\n        (match_body, needs_catchall_branch)\n    };\n\n    input.generics.where_clause = where_clause;\n\n    if needs_catchall_branch {\n        // This ideally shouldn't be needed, but see:\n        // https://github.com/rust-lang/rust/issues/68867\n\n        match_body.append_all(quote! { _ => unsafe {\n            use ::debug_unreachable::debug_unreachable;\n            debug_unreachable!()\n        } });\n    }\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    quote! {\n        impl #impl_generics crate::values::distance::ComputeSquaredDistance for #name #ty_generics #where_clause {\n            #[allow(unused_variables, unused_imports)]\n            #[inline]\n            fn compute_squared_distance(\n                &self,\n                other: &Self,\n            ) -> Result<crate::values::distance::SquaredDistance, ()> {\n                if std::mem::discriminant(self) != std::mem::discriminant(other) {\n                    return Err(());\n                }\n                match (self, other) {\n                    #match_body\n                }\n            }\n        }\n    }\n}\n\nfn derive_variant_arm(\n    variant: &synstructure::VariantInfo,\n    mut where_clause: &mut Option<WhereClause>,\n) -> TokenStream {\n    let variant_attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());\n    let (this_pattern, this_info) = cg::ref_pattern(&variant, \"this\");\n    let (other_pattern, other_info) = cg::ref_pattern(&variant, \"other\");\n\n    if variant_attrs.error {\n        return quote! {\n            (&#this_pattern, &#other_pattern) => Err(()),\n        };\n    }\n\n    let sum = if this_info.is_empty() {\n        quote! { crate::values::distance::SquaredDistance::from_sqrt(0.) }\n    } else {\n        let mut sum = quote!();\n        sum.append_separated(this_info.iter().zip(&other_info).map(|(this, other)| {\n            let field_attrs = cg::parse_field_attrs::<DistanceFieldAttrs>(&this.ast());\n            if field_attrs.field_bound {\n                let ty = &this.ast().ty;\n                cg::add_predicate(\n                    &mut where_clause,\n                    parse_quote!(#ty: crate::values::distance::ComputeSquaredDistance),\n                );\n            }\n\n            let animation_field_attrs =\n                cg::parse_field_attrs::<AnimationFieldAttrs>(&this.ast());\n\n            if animation_field_attrs.constant {\n                quote! {\n                    {\n                        if #this != #other {\n                            return Err(());\n                        }\n                        crate::values::distance::SquaredDistance::from_sqrt(0.)\n                    }\n                }\n            } else {\n                quote! {\n                    crate::values::distance::ComputeSquaredDistance::compute_squared_distance(#this, #other)?\n                }\n            }\n        }), quote!(+));\n        sum\n    };\n\n    return quote! {\n        (&#this_pattern, &#other_pattern) => Ok(#sum),\n    };\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(distance), default)]\nstruct DistanceFieldAttrs {\n    field_bound: bool,\n}\n"
  },
  {
    "path": "style_derive/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![recursion_limit = \"128\"]\n\n#[macro_use]\nextern crate darling;\nextern crate proc_macro;\nextern crate proc_macro2;\n#[macro_use]\nextern crate quote;\n#[macro_use]\nextern crate syn;\nextern crate synstructure;\n\nuse proc_macro::TokenStream;\n\nmod animate;\nmod cg;\nmod compute_squared_distance;\nmod parse;\nmod specified_value_info;\nmod to_animated_value;\nmod to_animated_zero;\nmod to_computed_value;\nmod to_css;\nmod to_resolved_value;\nmod to_typed;\n\n#[proc_macro_derive(Animate, attributes(animate, animation))]\npub fn derive_animate(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    animate::derive(input).into()\n}\n\n#[proc_macro_derive(ComputeSquaredDistance, attributes(animation, distance))]\npub fn derive_compute_squared_distance(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    compute_squared_distance::derive(input).into()\n}\n\n#[proc_macro_derive(ToAnimatedValue)]\npub fn derive_to_animated_value(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_animated_value::derive(input).into()\n}\n\n#[proc_macro_derive(Parse, attributes(css, parse))]\npub fn derive_parse(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    parse::derive(input).into()\n}\n\n#[proc_macro_derive(ToAnimatedZero, attributes(animation, zero))]\npub fn derive_to_animated_zero(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_animated_zero::derive(input).into()\n}\n\n#[proc_macro_derive(ToComputedValue, attributes(compute))]\npub fn derive_to_computed_value(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_computed_value::derive(input).into()\n}\n\n#[proc_macro_derive(ToResolvedValue, attributes(resolve))]\npub fn derive_to_resolved_value(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_resolved_value::derive(input).into()\n}\n\n#[proc_macro_derive(ToCss, attributes(css))]\npub fn derive_to_css(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_css::derive(input).into()\n}\n\n#[proc_macro_derive(SpecifiedValueInfo, attributes(css, parse, value_info))]\npub fn derive_specified_value_info(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    specified_value_info::derive(input).into()\n}\n\n#[proc_macro_derive(ToTyped, attributes(css, typed))]\npub fn derive_to_typed(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_typed::derive(input).into()\n}\n"
  },
  {
    "path": "style_derive/parse.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse crate::to_css::{CssBitflagAttrs, CssVariantAttrs};\nuse proc_macro2::{Span, TokenStream};\nuse quote::TokenStreamExt;\nuse syn::{self, DeriveInput, Ident, Path};\nuse synstructure::{Structure, VariantInfo};\n\n#[derive(Default, FromVariant)]\n#[darling(attributes(parse), default)]\npub struct ParseVariantAttrs {\n    pub aliases: Option<String>,\n    pub condition: Option<Path>,\n    pub parse_fn: Option<Path>,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(parse), default)]\npub struct ParseFieldAttrs {\n    field_bound: bool,\n}\n\nfn parse_bitflags(bitflags: &CssBitflagAttrs) -> TokenStream {\n    let mut match_arms = TokenStream::new();\n    for (rust_name, css_name) in bitflags.single_flags() {\n        let rust_ident = Ident::new(&rust_name, Span::call_site());\n        match_arms.append_all(quote! {\n            #css_name if result.is_empty() => {\n                single_flag = true;\n                Self::#rust_ident\n            },\n        });\n    }\n\n    for (rust_name, css_name) in bitflags.mixed_flags() {\n        let rust_ident = Ident::new(&rust_name, Span::call_site());\n        match_arms.append_all(quote! {\n            #css_name => Self::#rust_ident,\n        });\n    }\n\n    let mut validate_condition = quote! { !result.is_empty() };\n    if let Some(ref function) = bitflags.validate_mixed {\n        validate_condition.append_all(quote! {\n            && #function(&mut result)\n        });\n    }\n\n    // NOTE(emilio): this loop has this weird structure because we run this code\n    // to parse stuff like text-decoration-line in the text-decoration\n    // shorthand, so we need to be a bit careful that we don't error if we don't\n    // consume the whole thing because we find an invalid identifier or other\n    // kind of token. Instead, we should leave it unconsumed.\n    quote! {\n        let mut result = Self::empty();\n        loop {\n            let mut single_flag = false;\n            let flag: Result<_, style_traits::ParseError<'i>> = input.try_parse(|input| {\n                Ok(try_match_ident_ignore_ascii_case! { input,\n                    #match_arms\n                })\n            });\n\n            let flag = match flag {\n                Ok(flag) => flag,\n                Err(..) => break,\n            };\n\n            if single_flag {\n                return Ok(flag);\n            }\n\n            if result.intersects(flag) {\n                return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));\n            }\n\n            result.insert(flag);\n        }\n        if #validate_condition {\n            Ok(result)\n        } else {\n            Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))\n        }\n    }\n}\n\nfn parse_non_keyword_variant(\n    where_clause: &mut Option<syn::WhereClause>,\n    variant: &VariantInfo,\n    variant_attrs: &CssVariantAttrs,\n    parse_attrs: &ParseVariantAttrs,\n    skip_try: bool,\n) -> TokenStream {\n    let bindings = variant.bindings();\n    assert!(parse_attrs.aliases.is_none());\n    assert!(variant_attrs.function.is_none());\n    assert!(variant_attrs.keyword.is_none());\n    assert_eq!(\n        bindings.len(),\n        1,\n        \"We only support deriving parse for simple variants\"\n    );\n    let binding_ast = &bindings[0].ast();\n    let ty = &binding_ast.ty;\n\n    if let Some(ref bitflags) = variant_attrs.bitflags {\n        assert!(skip_try, \"Should be the only variant\");\n        assert!(parse_attrs.parse_fn.is_none(), \"should not be needed\");\n        assert!(\n            parse_attrs.condition.is_none(),\n            \"Should be the only variant\"\n        );\n        assert!(where_clause.is_none(), \"Generic bitflags?\");\n        return parse_bitflags(bitflags);\n    }\n\n    let field_attrs = cg::parse_field_attrs::<ParseFieldAttrs>(binding_ast);\n\n    if field_attrs.field_bound {\n        cg::add_predicate(where_clause, parse_quote!(#ty: crate::parser::Parse));\n    }\n\n    let mut parse = if let Some(ref parse_fn) = parse_attrs.parse_fn {\n        quote! { #parse_fn(context, input) }\n    } else {\n        quote! { <#ty as crate::parser::Parse>::parse(context, input) }\n    };\n\n    let variant_name = &variant.ast().ident;\n    let variant_name = match variant.prefix {\n        Some(p) => quote! { #p::#variant_name },\n        None => quote! { #variant_name },\n    };\n\n    parse = if skip_try {\n        quote! {\n            let v = #parse?;\n            return Ok(#variant_name(v));\n        }\n    } else {\n        quote! {\n            if let Ok(v) = input.try_parse(|input| #parse) {\n                return Ok(#variant_name(v));\n            }\n        }\n    };\n\n    if let Some(ref condition) = parse_attrs.condition {\n        parse = quote! {\n            if #condition(context) {\n                #parse\n            }\n        };\n\n        if skip_try {\n            // We're the last variant and we can fail to parse due to the\n            // condition clause. If that happens, we need to return an error.\n            parse = quote! {\n                #parse\n                Err(input.new_custom_error(style_traits::StyleParseErrorKind::UnspecifiedError))\n            };\n        }\n    }\n\n    parse\n}\n\npub fn derive(mut input: DeriveInput) -> TokenStream {\n    let mut where_clause = input.generics.where_clause.take();\n    for param in input.generics.type_params() {\n        cg::add_predicate(\n            &mut where_clause,\n            parse_quote!(#param: crate::parser::Parse),\n        );\n    }\n\n    let name = &input.ident;\n    let s = Structure::new(&input);\n\n    let mut saw_condition = false;\n    let mut match_keywords = quote! {};\n    let mut non_keywords = vec![];\n\n    let mut effective_variants = 0;\n    for variant in s.variants().iter() {\n        let css_variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&variant.ast());\n        if css_variant_attrs.skip {\n            continue;\n        }\n        effective_variants += 1;\n\n        let parse_attrs = cg::parse_variant_attrs_from_ast::<ParseVariantAttrs>(&variant.ast());\n\n        saw_condition |= parse_attrs.condition.is_some();\n\n        if !variant.bindings().is_empty() {\n            non_keywords.push((variant, css_variant_attrs, parse_attrs));\n            continue;\n        }\n\n        assert!(parse_attrs.parse_fn.is_none());\n\n        let identifier = cg::to_css_identifier(\n            &css_variant_attrs\n                .keyword\n                .unwrap_or_else(|| variant.ast().ident.to_string()),\n        );\n        let ident = &variant.ast().ident;\n\n        let condition = match parse_attrs.condition {\n            Some(ref p) => quote! { if #p(context) },\n            None => quote! {},\n        };\n\n        match_keywords.extend(quote! {\n            #identifier #condition => Ok(#name::#ident),\n        });\n\n        let aliases = match parse_attrs.aliases {\n            Some(aliases) => aliases,\n            None => continue,\n        };\n\n        for alias in aliases.split(',') {\n            match_keywords.extend(quote! {\n                #alias #condition => Ok(#name::#ident),\n            });\n        }\n    }\n\n    let needs_context = saw_condition || !non_keywords.is_empty();\n\n    let context_ident = if needs_context {\n        quote! { context }\n    } else {\n        quote! { _ }\n    };\n\n    let has_keywords = non_keywords.len() != effective_variants;\n\n    let mut parse_non_keywords = quote! {};\n    for (i, (variant, css_attrs, parse_attrs)) in non_keywords.iter().enumerate() {\n        let skip_try = !has_keywords && i == non_keywords.len() - 1;\n        let parse_variant =\n            parse_non_keyword_variant(&mut where_clause, variant, css_attrs, parse_attrs, skip_try);\n        parse_non_keywords.extend(parse_variant);\n    }\n\n    let parse_body = if needs_context {\n        let parse_keywords = if has_keywords {\n            quote! {\n                let location = input.current_source_location();\n                let ident = input.expect_ident()?;\n                cssparser::match_ignore_ascii_case! { &ident,\n                    #match_keywords\n                    _ => Err(location.new_unexpected_token_error(\n                        cssparser::Token::Ident(ident.clone())\n                    ))\n                }\n            }\n        } else {\n            quote! {}\n        };\n\n        quote! {\n            #parse_non_keywords\n            #parse_keywords\n        }\n    } else {\n        quote! { Self::parse(input) }\n    };\n\n    let has_non_keywords = !non_keywords.is_empty();\n\n    input.generics.where_clause = where_clause;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    let parse_trait_impl = quote! {\n        impl #impl_generics crate::parser::Parse for #name #ty_generics #where_clause {\n            #[inline]\n            fn parse<'i, 't>(\n                #context_ident: &crate::parser::ParserContext,\n                input: &mut cssparser::Parser<'i, 't>,\n            ) -> Result<Self, style_traits::ParseError<'i>> {\n                #parse_body\n            }\n        }\n    };\n\n    if needs_context {\n        return parse_trait_impl;\n    }\n\n    assert!(!has_non_keywords);\n\n    // TODO(emilio): It'd be nice to get rid of these, but that makes the\n    // conversion harder...\n    let methods_impl = quote! {\n        impl #name {\n            /// Parse this keyword.\n            #[inline]\n            pub fn parse<'i, 't>(\n                input: &mut cssparser::Parser<'i, 't>,\n            ) -> Result<Self, style_traits::ParseError<'i>> {\n                let location = input.current_source_location();\n                let ident = input.expect_ident()?;\n                Self::from_ident(ident.as_ref()).map_err(|()| {\n                    location.new_unexpected_token_error(\n                        cssparser::Token::Ident(ident.clone())\n                    )\n                })\n            }\n\n            /// Parse this keyword from a string slice.\n            #[inline]\n            pub fn from_ident(ident: &str) -> Result<Self, ()> {\n                cssparser::match_ignore_ascii_case! { ident,\n                    #match_keywords\n                    _ => Err(()),\n                }\n            }\n        }\n    };\n\n    quote! {\n        #parse_trait_impl\n        #methods_impl\n    }\n}\n"
  },
  {
    "path": "style_derive/specified_value_info.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse crate::parse::ParseVariantAttrs;\nuse crate::to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};\nuse proc_macro2::TokenStream;\nuse quote::TokenStreamExt;\nuse syn::{Data, DeriveInput, Fields, Ident, Type};\n\npub fn derive(mut input: DeriveInput) -> TokenStream {\n    let css_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);\n    let mut types = vec![];\n    let mut values = vec![];\n\n    let input_ident = &input.ident;\n    let input_name = || cg::to_css_identifier(&input_ident.to_string());\n    if let Some(function) = css_attrs.function {\n        values.push(function.explicit().unwrap_or_else(input_name));\n    // If the whole value is wrapped in a function, value types of\n    // its fields should not be propagated.\n    } else {\n        let mut where_clause = input.generics.where_clause.take();\n        for param in input.generics.type_params() {\n            cg::add_predicate(\n                &mut where_clause,\n                parse_quote!(#param: style_traits::SpecifiedValueInfo),\n            );\n        }\n        input.generics.where_clause = where_clause;\n\n        match input.data {\n            Data::Enum(ref e) => {\n                for v in e.variants.iter() {\n                    let css_attrs = cg::parse_variant_attrs::<CssVariantAttrs>(&v);\n                    let info_attrs = cg::parse_variant_attrs::<ValueInfoVariantAttrs>(&v);\n                    let parse_attrs = cg::parse_variant_attrs::<ParseVariantAttrs>(&v);\n                    if css_attrs.skip {\n                        continue;\n                    }\n                    if let Some(aliases) = parse_attrs.aliases {\n                        for alias in aliases.split(',') {\n                            values.push(alias.to_string());\n                        }\n                    }\n                    if let Some(other_values) = info_attrs.other_values {\n                        for value in other_values.split(',') {\n                            values.push(value.to_string());\n                        }\n                    }\n                    let ident = &v.ident;\n                    let variant_name = || cg::to_css_identifier(&ident.to_string());\n                    if info_attrs.starts_with_keyword {\n                        values.push(variant_name());\n                        continue;\n                    }\n                    if let Some(keyword) = css_attrs.keyword {\n                        values.push(keyword);\n                        continue;\n                    }\n                    if let Some(function) = css_attrs.function {\n                        values.push(function.explicit().unwrap_or_else(variant_name));\n                    } else if !derive_struct_fields(&v.fields, &mut types, &mut values) {\n                        values.push(variant_name());\n                    }\n                }\n            },\n            Data::Struct(ref s) => {\n                if let Some(ref bitflags) = css_attrs.bitflags {\n                    for (_rust_name, css_name) in bitflags.single_flags() {\n                        values.push(css_name)\n                    }\n                    for (_rust_name, css_name) in bitflags.mixed_flags() {\n                        values.push(css_name)\n                    }\n                } else if !derive_struct_fields(&s.fields, &mut types, &mut values) {\n                    values.push(input_name());\n                }\n            },\n            Data::Union(_) => unreachable!(\"union is not supported\"),\n        }\n    }\n\n    let info_attrs = cg::parse_input_attrs::<ValueInfoInputAttrs>(&input);\n    if let Some(other_values) = info_attrs.other_values {\n        for value in other_values.split(',') {\n            values.push(value.to_string());\n        }\n    }\n\n    let mut types_value = quote!(0);\n    types_value.append_all(types.iter().map(|ty| {\n        quote! {\n            | <#ty as style_traits::SpecifiedValueInfo>::SUPPORTED_TYPES\n        }\n    }));\n\n    let mut nested_collects = quote!();\n    nested_collects.append_all(types.iter().map(|ty| {\n        quote! {\n            <#ty as style_traits::SpecifiedValueInfo>::collect_completion_keywords(_f);\n        }\n    }));\n\n    if let Some(ty) = info_attrs.ty {\n        types_value.append_all(quote! {\n            | style_traits::CssType::#ty\n        });\n    }\n\n    let append_values = if values.is_empty() {\n        quote!()\n    } else {\n        let mut value_list = quote!();\n        value_list.append_separated(values.iter(), quote! { , });\n        quote! { _f(&[#value_list]); }\n    };\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    quote! {\n        impl #impl_generics style_traits::SpecifiedValueInfo for #name #ty_generics\n        #where_clause\n        {\n            const SUPPORTED_TYPES: u8 = #types_value;\n\n            fn collect_completion_keywords(_f: &mut dyn FnMut(&[&'static str])) {\n                #nested_collects\n                #append_values\n            }\n        }\n    }\n}\n\n/// Derive from the given fields. Return false if the fields is a Unit,\n/// true otherwise.\nfn derive_struct_fields<'a>(\n    fields: &'a Fields,\n    types: &mut Vec<&'a Type>,\n    values: &mut Vec<String>,\n) -> bool {\n    let fields = match *fields {\n        Fields::Unit => return false,\n        Fields::Named(ref fields) => fields.named.iter(),\n        Fields::Unnamed(ref fields) => fields.unnamed.iter(),\n    };\n    types.extend(fields.filter_map(|field| {\n        let info_attrs = cg::parse_field_attrs::<ValueInfoFieldAttrs>(field);\n        if let Some(other_values) = info_attrs.other_values {\n            for value in other_values.split(',') {\n                values.push(value.to_string());\n            }\n        }\n        let css_attrs = cg::parse_field_attrs::<CssFieldAttrs>(field);\n        if css_attrs.represents_keyword {\n            let ident = field\n                .ident\n                .as_ref()\n                .expect(\"only named field should use represents_keyword\");\n            values.push(cg::to_css_identifier(&ident.to_string()).replace(\"_\", \"-\"));\n            return None;\n        }\n        if let Some(if_empty) = css_attrs.if_empty {\n            values.push(if_empty);\n        }\n        if !css_attrs.skip {\n            Some(&field.ty)\n        } else {\n            None\n        }\n    }));\n    true\n}\n\n#[derive(Default, FromDeriveInput)]\n#[darling(attributes(value_info), default)]\nstruct ValueInfoInputAttrs {\n    ty: Option<Ident>,\n    other_values: Option<String>,\n}\n\n#[derive(Default, FromVariant)]\n#[darling(attributes(value_info), default)]\nstruct ValueInfoVariantAttrs {\n    starts_with_keyword: bool,\n    other_values: Option<String>,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(value_info), default)]\nstruct ValueInfoFieldAttrs {\n    other_values: Option<String>,\n}\n"
  },
  {
    "path": "style_derive/to_animated_value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::animate::AnimationFieldAttrs;\nuse crate::{cg, to_computed_value};\nuse proc_macro2::TokenStream;\nuse syn::DeriveInput;\nuse synstructure::BindStyle;\n\npub fn derive(input: DeriveInput) -> TokenStream {\n    let trait_impl = |from_body, to_body| {\n        quote! {\n             #[inline]\n             fn from_animated_value(from: Self::AnimatedValue) -> Self {\n                 #from_body\n             }\n\n             #[inline]\n             fn to_animated_value(self, context: &crate::values::animated::Context) -> Self::AnimatedValue {\n                 #to_body\n             }\n        }\n    };\n\n    to_computed_value::derive_to_value(\n        input,\n        parse_quote!(crate::values::animated::ToAnimatedValue),\n        parse_quote!(AnimatedValue),\n        BindStyle::Move,\n        |binding| {\n            let attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());\n            to_computed_value::ToValueAttrs {\n                field_bound: attrs.field_bound,\n                no_field_bound: false,\n            }\n        },\n        |binding| quote!(crate::values::animated::ToAnimatedValue::from_animated_value(#binding)),\n        |binding| quote!(crate::values::animated::ToAnimatedValue::to_animated_value(#binding, context)),\n        trait_impl,\n    )\n}\n"
  },
  {
    "path": "style_derive/to_animated_zero.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::animate::{AnimationFieldAttrs, AnimationInputAttrs, AnimationVariantAttrs};\nuse crate::cg;\nuse proc_macro2::TokenStream;\nuse quote::TokenStreamExt;\nuse syn;\nuse synstructure;\n\npub fn derive(mut input: syn::DeriveInput) -> TokenStream {\n    let animation_input_attrs = cg::parse_input_attrs::<AnimationInputAttrs>(&input);\n    let no_bound = animation_input_attrs.no_bound.unwrap_or_default();\n    let mut where_clause = input.generics.where_clause.take();\n    for param in input.generics.type_params() {\n        if !no_bound.iter().any(|name| name.is_ident(&param.ident)) {\n            cg::add_predicate(\n                &mut where_clause,\n                parse_quote!(#param: crate::values::animated::ToAnimatedZero),\n            );\n        }\n    }\n\n    let to_body = synstructure::Structure::new(&input).each_variant(|variant| {\n        let attrs = cg::parse_variant_attrs_from_ast::<AnimationVariantAttrs>(&variant.ast());\n        if attrs.error {\n            return Some(quote! { Err(()) });\n        }\n        let (mapped, mapped_bindings) = cg::value(variant, \"mapped\");\n        let bindings_pairs = variant.bindings().iter().zip(mapped_bindings);\n        let mut computations = quote!();\n        computations.append_all(bindings_pairs.map(|(binding, mapped_binding)| {\n            let field_attrs = cg::parse_field_attrs::<AnimationFieldAttrs>(&binding.ast());\n            if field_attrs.constant {\n                quote! {\n                    let #mapped_binding = std::clone::Clone::clone(#binding);\n                }\n            } else {\n                quote! {\n                    let #mapped_binding =\n                        crate::values::animated::ToAnimatedZero::to_animated_zero(#binding)?;\n                }\n            }\n        }));\n        computations.append_all(quote! { Ok(#mapped) });\n        Some(computations)\n    });\n    input.generics.where_clause = where_clause;\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    quote! {\n        impl #impl_generics crate::values::animated::ToAnimatedZero for #name #ty_generics #where_clause {\n            #[allow(unused_variables)]\n            #[inline]\n            fn to_animated_zero(&self) -> Result<Self, ()> {\n                match *self {\n                    #to_body\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "style_derive/to_computed_value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse proc_macro2::TokenStream;\nuse syn::{DeriveInput, Ident, Path};\nuse synstructure::{BindStyle, BindingInfo};\n\npub fn derive_to_value(\n    mut input: DeriveInput,\n    trait_path: Path,\n    output_type_name: Ident,\n    bind_style: BindStyle,\n    // Returns whether to apply the field bound for a given item.\n    mut binding_attrs: impl FnMut(&BindingInfo) -> ToValueAttrs,\n    // Returns a token stream of the form: trait_path::from_foo(#binding)\n    mut call_from: impl FnMut(&BindingInfo) -> TokenStream,\n    mut call_to: impl FnMut(&BindingInfo) -> TokenStream,\n    // Returns a tokenstream of the form:\n    // fn from_function_syntax(foobar) -> Baz {\n    //     #first_arg\n    // }\n    //\n    // fn to_function_syntax(foobar) -> Baz {\n    //     #second_arg\n    // }\n    mut trait_impl: impl FnMut(TokenStream, TokenStream) -> TokenStream,\n) -> TokenStream {\n    let name = &input.ident;\n\n    let mut where_clause = input.generics.where_clause.take();\n    cg::propagate_clauses_to_output_type(\n        &mut where_clause,\n        &input.generics,\n        &trait_path,\n        &output_type_name,\n    );\n\n    let moves = match bind_style {\n        BindStyle::Move | BindStyle::MoveMut => true,\n        BindStyle::Ref | BindStyle::RefMut => false,\n    };\n\n    let params = input.generics.type_params().collect::<Vec<_>>();\n    for param in &params {\n        cg::add_predicate(&mut where_clause, parse_quote!(#param: #trait_path));\n    }\n\n    let computed_value_type = cg::fmap_trait_output(&input, &trait_path, &output_type_name);\n\n    let mut add_field_bound = |binding: &BindingInfo| {\n        let ty = &binding.ast().ty;\n\n        let output_type = cg::map_type_params(\n            ty,\n            &params,\n            &computed_value_type,\n            &mut |ident| parse_quote!(<#ident as #trait_path>::#output_type_name),\n        );\n\n        cg::add_predicate(\n            &mut where_clause,\n            parse_quote!(\n                #ty: #trait_path<#output_type_name = #output_type>\n            ),\n        );\n    };\n\n    let (to_body, from_body) = if params.is_empty() {\n        let mut s = synstructure::Structure::new(&input);\n        s.variants_mut().iter_mut().for_each(|v| {\n            v.bind_with(|_| bind_style);\n        });\n\n        for variant in s.variants() {\n            for binding in variant.bindings() {\n                let attrs = binding_attrs(&binding);\n                assert!(\n                    !attrs.field_bound,\n                    \"It is default on a non-generic implementation\",\n                );\n                if !attrs.no_field_bound {\n                    // Add field bounds to all bindings except the manually\n                    // excluded. This ensures the correctness of the clone() /\n                    // move based implementation.\n                    add_field_bound(binding);\n                }\n            }\n        }\n\n        let to_body = if moves {\n            quote! { self }\n        } else {\n            quote! { std::clone::Clone::clone(self) }\n        };\n\n        let from_body = if moves {\n            quote! { from }\n        } else {\n            quote! { std::clone::Clone::clone(from) }\n        };\n\n        (to_body, from_body)\n    } else {\n        let to_body = cg::fmap_match(&input, bind_style, |binding| {\n            let attrs = binding_attrs(&binding);\n            assert!(\n                !attrs.no_field_bound,\n                \"It doesn't make sense on a generic implementation\"\n            );\n            if attrs.field_bound {\n                add_field_bound(&binding);\n            }\n            call_to(&binding)\n        });\n\n        let from_body = cg::fmap_match(&input, bind_style, |binding| call_from(&binding));\n\n        let self_ = if moves {\n            quote! { self }\n        } else {\n            quote! { *self }\n        };\n        let from_ = if moves {\n            quote! { from }\n        } else {\n            quote! { *from }\n        };\n\n        let to_body = quote! {\n            match #self_ {\n                #to_body\n            }\n        };\n\n        let from_body = quote! {\n            match #from_ {\n                #from_body\n            }\n        };\n\n        (to_body, from_body)\n    };\n\n    input.generics.where_clause = where_clause;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    let impl_ = trait_impl(from_body, to_body);\n\n    quote! {\n        impl #impl_generics #trait_path for #name #ty_generics #where_clause {\n            type #output_type_name = #computed_value_type;\n\n            #impl_\n        }\n    }\n}\n\npub fn derive(input: DeriveInput) -> TokenStream {\n    let trait_impl = |from_body, to_body| {\n        quote! {\n             #[inline]\n             fn from_computed_value(from: &Self::ComputedValue) -> Self {\n                 #from_body\n             }\n\n             #[allow(unused_variables)]\n             #[inline]\n             fn to_computed_value(&self, context: &crate::values::computed::Context) -> Self::ComputedValue {\n                 #to_body\n             }\n        }\n    };\n\n    derive_to_value(\n        input,\n        parse_quote!(crate::values::computed::ToComputedValue),\n        parse_quote!(ComputedValue),\n        BindStyle::Ref,\n        |binding| {\n            let attrs = cg::parse_field_attrs::<ComputedValueAttrs>(&binding.ast());\n            ToValueAttrs {\n                field_bound: attrs.field_bound,\n                no_field_bound: attrs.no_field_bound,\n            }\n        },\n        |binding| quote!(crate::values::computed::ToComputedValue::from_computed_value(#binding)),\n        |binding| quote!(crate::values::computed::ToComputedValue::to_computed_value(#binding, context)),\n        trait_impl,\n    )\n}\n\n#[derive(Default)]\npub struct ToValueAttrs {\n    pub field_bound: bool,\n    pub no_field_bound: bool,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(compute), default)]\nstruct ComputedValueAttrs {\n    field_bound: bool,\n    no_field_bound: bool,\n}\n"
  },
  {
    "path": "style_derive/to_css.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse darling::util::Override;\nuse proc_macro2::{Span, TokenStream};\nuse quote::{ToTokens, TokenStreamExt};\nuse syn::{self, Data, Ident, Path, WhereClause};\nuse synstructure::{BindingInfo, Structure, VariantInfo};\n\nfn derive_bitflags(input: &syn::DeriveInput, bitflags: &CssBitflagAttrs) -> TokenStream {\n    let name = &input.ident;\n    let mut body = TokenStream::new();\n    for (rust_name, css_name) in bitflags.single_flags() {\n        let rust_ident = Ident::new(&rust_name, Span::call_site());\n        body.append_all(quote! {\n            if *self == Self::#rust_ident {\n                return dest.write_str(#css_name);\n            }\n        });\n    }\n\n    body.append_all(quote! {\n        let mut has_any = false;\n    });\n\n    if bitflags.overlapping_bits {\n        body.append_all(quote! {\n            let mut serialized = Self::empty();\n        });\n    }\n\n    for (rust_name, css_name) in bitflags.mixed_flags() {\n        let rust_ident = Ident::new(&rust_name, Span::call_site());\n        let serialize = quote! {\n            if has_any {\n                dest.write_char(' ')?;\n            }\n            has_any = true;\n            dest.write_str(#css_name)?;\n        };\n        if bitflags.overlapping_bits {\n            body.append_all(quote! {\n                if self.contains(Self::#rust_ident) && !serialized.intersects(Self::#rust_ident) {\n                    #serialize\n                    serialized.insert(Self::#rust_ident);\n                }\n            });\n        } else {\n            body.append_all(quote! {\n                if self.intersects(Self::#rust_ident) {\n                    #serialize\n                }\n            });\n        }\n    }\n\n    body.append_all(quote! {\n        Ok(())\n    });\n\n    quote! {\n        impl style_traits::ToCss for #name {\n            #[allow(unused_variables)]\n            #[inline]\n            fn to_css<W>(\n                &self,\n                dest: &mut style_traits::CssWriter<W>,\n            ) -> std::fmt::Result\n            where\n                W: std::fmt::Write,\n            {\n                #body\n            }\n        }\n    }\n}\n\npub fn derive(mut input: syn::DeriveInput) -> TokenStream {\n    let mut where_clause = input.generics.where_clause.take();\n    for param in input.generics.type_params() {\n        cg::add_predicate(&mut where_clause, parse_quote!(#param: style_traits::ToCss));\n    }\n\n    let input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);\n    if matches!(input.data, Data::Enum(..)) || input_attrs.bitflags.is_some() {\n        assert!(\n            input_attrs.function.is_none(),\n            \"#[css(function)] is not allowed on enums or bitflags\"\n        );\n        assert!(\n            !input_attrs.comma,\n            \"#[css(comma)] is not allowed on enums or bitflags\"\n        );\n    }\n\n    if let Some(ref bitflags) = input_attrs.bitflags {\n        assert!(\n            !input_attrs.derive_debug,\n            \"Bitflags can derive debug on their own\"\n        );\n        assert!(where_clause.is_none(), \"Generic bitflags?\");\n        return derive_bitflags(&input, bitflags);\n    }\n\n    let match_body = {\n        let s = Structure::new(&input);\n        s.each_variant(|variant| derive_variant_arm(variant, &mut where_clause))\n    };\n    input.generics.where_clause = where_clause;\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    let mut impls = quote! {\n        impl #impl_generics style_traits::ToCss for #name #ty_generics #where_clause {\n            #[allow(unused_variables)]\n            #[inline]\n            fn to_css<W>(\n                &self,\n                dest: &mut style_traits::CssWriter<W>,\n            ) -> std::fmt::Result\n            where\n                W: std::fmt::Write,\n            {\n                match *self {\n                    #match_body\n                }\n            }\n        }\n    };\n\n    if input_attrs.derive_debug {\n        impls.append_all(quote! {\n            impl #impl_generics std::fmt::Debug for #name #ty_generics #where_clause {\n                fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n                    style_traits::ToCss::to_css(\n                        self,\n                        &mut style_traits::CssWriter::new(f),\n                    )\n                }\n            }\n        });\n    }\n\n    impls\n}\n\nfn derive_variant_arm(variant: &VariantInfo, generics: &mut Option<WhereClause>) -> TokenStream {\n    let bindings = variant.bindings();\n    let identifier = cg::to_css_identifier(&variant.ast().ident.to_string());\n    let ast = variant.ast();\n    let variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&ast);\n    let separator = if variant_attrs.comma { \", \" } else { \" \" };\n\n    if variant_attrs.skip {\n        return quote!(Ok(()));\n    }\n    if variant_attrs.dimension {\n        assert_eq!(bindings.len(), 1);\n        assert!(\n            variant_attrs.function.is_none() && variant_attrs.keyword.is_none(),\n            \"That makes no sense\"\n        );\n    }\n\n    let mut expr = if let Some(keyword) = variant_attrs.keyword {\n        assert!(bindings.is_empty());\n        quote! {\n            std::fmt::Write::write_str(dest, #keyword)\n        }\n    } else if !bindings.is_empty() {\n        derive_variant_fields_expr(bindings, generics, separator)\n    } else {\n        quote! {\n            std::fmt::Write::write_str(dest, #identifier)\n        }\n    };\n\n    if variant_attrs.dimension {\n        expr = quote! {\n            #expr?;\n            std::fmt::Write::write_str(dest, #identifier)\n        }\n    } else if let Some(function) = variant_attrs.function {\n        let mut identifier = function.explicit().map_or(identifier, |name| name);\n        identifier.push('(');\n        expr = quote! {\n            std::fmt::Write::write_str(dest, #identifier)?;\n            #expr?;\n            std::fmt::Write::write_str(dest, \")\")\n        }\n    }\n    expr\n}\n\nfn derive_variant_fields_expr(\n    bindings: &[BindingInfo],\n    where_clause: &mut Option<WhereClause>,\n    separator: &str,\n) -> TokenStream {\n    let mut iter = bindings\n        .iter()\n        .filter_map(|binding| {\n            let attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());\n            if attrs.skip {\n                return None;\n            }\n            Some((binding, attrs))\n        })\n        .peekable();\n\n    let (first, attrs) = match iter.next() {\n        Some(pair) => pair,\n        None => return quote! { Ok(()) },\n    };\n    if attrs.field_bound {\n        let ty = &first.ast().ty;\n        // TODO(emilio): IntoIterator might not be enough for every type of\n        // iterable thing (like ArcSlice<> or what not). We might want to expose\n        // an `item = \"T\"` attribute to handle that in the future.\n        let predicate = if attrs.iterable {\n            parse_quote!(<#ty as IntoIterator>::Item: style_traits::ToCss)\n        } else {\n            parse_quote!(#ty: style_traits::ToCss)\n        };\n        cg::add_predicate(where_clause, predicate);\n    }\n    if !attrs.iterable && iter.peek().is_none() {\n        let mut expr = quote! { style_traits::ToCss::to_css(#first, dest) };\n        if let Some(condition) = attrs.skip_if {\n            expr = quote! {\n                if !#condition(#first) {\n                    #expr\n                }\n            }\n        }\n\n        if let Some(condition) = attrs.contextual_skip_if {\n            expr = quote! {\n                if !#condition(#(#bindings), *) {\n                    #expr\n                }\n            }\n        }\n        return expr;\n    }\n\n    let mut expr = derive_single_field_expr(first, attrs, where_clause, bindings);\n    for (binding, attrs) in iter {\n        derive_single_field_expr(binding, attrs, where_clause, bindings).to_tokens(&mut expr)\n    }\n\n    quote! {{\n        let mut writer = style_traits::values::SequenceWriter::new(dest, #separator);\n        #expr\n        Ok(())\n    }}\n}\n\nfn derive_single_field_expr(\n    field: &BindingInfo,\n    attrs: CssFieldAttrs,\n    where_clause: &mut Option<WhereClause>,\n    bindings: &[BindingInfo],\n) -> TokenStream {\n    let mut expr = if attrs.iterable {\n        if let Some(if_empty) = attrs.if_empty {\n            return quote! {\n                {\n                    let mut iter = #field.iter().peekable();\n                    if iter.peek().is_none() {\n                        writer.raw_item(#if_empty)?;\n                    } else {\n                        for item in iter {\n                            writer.item(&item)?;\n                        }\n                    }\n                }\n            };\n        }\n        quote! {\n            for item in #field.iter() {\n                writer.item(&item)?;\n            }\n        }\n    } else if attrs.represents_keyword {\n        let ident = field\n            .ast()\n            .ident\n            .as_ref()\n            .expect(\"Unnamed field with represents_keyword?\");\n        let ident = cg::to_css_identifier(&ident.to_string()).replace(\"_\", \"-\");\n        quote! {\n            if *#field {\n                writer.raw_item(#ident)?;\n            }\n        }\n    } else {\n        if attrs.field_bound {\n            let ty = &field.ast().ty;\n            cg::add_predicate(where_clause, parse_quote!(#ty: style_traits::ToCss));\n        }\n        quote! { writer.item(#field)?; }\n    };\n\n    if let Some(condition) = attrs.skip_if {\n        expr = quote! {\n            if !#condition(#field) {\n                #expr\n            }\n        }\n    }\n\n    if let Some(condition) = attrs.contextual_skip_if {\n        expr = quote! {\n            if !#condition(#(#bindings), *) {\n                #expr\n            }\n        }\n    }\n\n    expr\n}\n\n#[derive(Default, FromMeta)]\n#[darling(default)]\npub struct CssBitflagAttrs {\n    /// Flags that can only go on their own, comma-separated.\n    pub single: Option<String>,\n    /// Flags that can go mixed with each other, comma-separated.\n    pub mixed: Option<String>,\n    /// Extra validation of the resulting mixed flags.\n    pub validate_mixed: Option<Path>,\n    /// Whether there are overlapping bits we need to take care of when\n    /// serializing.\n    pub overlapping_bits: bool,\n}\n\nimpl CssBitflagAttrs {\n    /// Returns a vector of (rust_name, css_name) of a given flag list.\n    fn names(s: &Option<String>) -> Vec<(String, String)> {\n        let s = match s {\n            Some(s) => s,\n            None => return vec![],\n        };\n        s.split(',')\n            .map(|css_name| (cg::to_scream_case(css_name), css_name.to_owned()))\n            .collect()\n    }\n\n    pub fn single_flags(&self) -> Vec<(String, String)> {\n        Self::names(&self.single)\n    }\n\n    pub fn mixed_flags(&self) -> Vec<(String, String)> {\n        Self::names(&self.mixed)\n    }\n}\n\n#[derive(Default, FromDeriveInput)]\n#[darling(attributes(css), default)]\npub struct CssInputAttrs {\n    pub derive_debug: bool,\n    // Here because structs variants are also their whole type definition.\n    pub function: Option<Override<String>>,\n    // Here because structs variants are also their whole type definition.\n    pub comma: bool,\n    pub bitflags: Option<CssBitflagAttrs>,\n}\n\n#[derive(Default, FromVariant)]\n#[darling(attributes(css), default)]\npub struct CssVariantAttrs {\n    pub function: Option<Override<String>>,\n    // Here because structs variants are also their whole type definition.\n    pub derive_debug: bool,\n    pub comma: bool,\n    pub bitflags: Option<CssBitflagAttrs>,\n    pub dimension: bool,\n    pub keyword: Option<String>,\n    pub skip: bool,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(css), default)]\npub struct CssFieldAttrs {\n    pub if_empty: Option<String>,\n    pub field_bound: bool,\n    pub iterable: bool,\n    pub skip: bool,\n    pub represents_keyword: bool,\n    pub contextual_skip_if: Option<Path>,\n    pub skip_if: Option<Path>,\n}\n"
  },
  {
    "path": "style_derive/to_resolved_value.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse crate::to_computed_value;\nuse proc_macro2::TokenStream;\nuse syn::DeriveInput;\nuse synstructure::BindStyle;\n\npub fn derive(input: DeriveInput) -> TokenStream {\n    let trait_impl = |from_body, to_body| {\n        quote! {\n             #[inline]\n             fn from_resolved_value(from: Self::ResolvedValue) -> Self {\n                 #from_body\n             }\n\n             #[inline]\n             fn to_resolved_value(\n                 self,\n                 context: &crate::values::resolved::Context,\n             ) -> Self::ResolvedValue {\n                 #to_body\n             }\n        }\n    };\n\n    to_computed_value::derive_to_value(\n        input,\n        parse_quote!(crate::values::resolved::ToResolvedValue),\n        parse_quote!(ResolvedValue),\n        BindStyle::Move,\n        |binding| {\n            let attrs = cg::parse_field_attrs::<ResolvedValueAttrs>(&binding.ast());\n            to_computed_value::ToValueAttrs {\n                field_bound: attrs.field_bound,\n                no_field_bound: attrs.no_field_bound,\n            }\n        },\n        |binding| quote!(crate::values::resolved::ToResolvedValue::from_resolved_value(#binding)),\n        |binding| quote!(crate::values::resolved::ToResolvedValue::to_resolved_value(#binding, context)),\n        trait_impl,\n    )\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(resolve), default)]\nstruct ResolvedValueAttrs {\n    field_bound: bool,\n    no_field_bound: bool,\n}\n"
  },
  {
    "path": "style_derive/to_typed.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::cg;\nuse crate::to_css::{CssFieldAttrs, CssInputAttrs, CssVariantAttrs};\nuse proc_macro2::TokenStream;\nuse quote::quote;\nuse quote::ToTokens;\nuse syn::{Data, DataEnum, DeriveInput, Fields, Path, WhereClause};\nuse synstructure::{BindingInfo, Structure};\n\n/// Derive implementation of the `ToTyped` trait.\n///\n/// This derive supports both enums and structs:\n///\n/// * Enums\n///   * Pure keyword enums: Enums made up entirely of unit variants\n///     (e.g. `enum Visibility { Visible, Hidden, Collapse }`). In this case,\n///     the derive optimizes by delegating to `ToCss` once for the whole value,\n///     then wrapping the result in `TypedValue::Keyword`.\n///\n///   * Mixed enums with unit variants: Enums that contain both unit\n///     variants (keywords) and data-carrying variants. The derive generates a\n///     `match` implementation where unit variants are reified as\n///     `TypedValue::Keyword`, and fielded variants reify by calling\n///     `.to_typed()` on their inner values by default. Variants or types\n///     marked with `skip_derive_fields` return `Err(())` instead.\n///\n/// * Structs\n///   * Structs are handled similarly to data-carrying variants in mixed enums.\n///     Unless `skip_derive_fields` is set, and as long as the type is not\n///     marked as a bitflags type, `.to_typed()` is generated for their inner\n///     values; otherwise, they return `Err(())`.\n///\n/// Unit variants are mapped to keywords using their Rust identifier converted\n/// via `to_css_identifier`. Attributes like `#[css(keyword = \"...\")]` will\n/// override the behavior and use the provided keyword instead.\n///\n/// For other kinds of types (e.g. unions), no `to_typed` method is generated;\n/// the default implementation applies, which always returns `Err(())`.\n///\n/// This allows keywords to be reified automatically into `CSSKeywordValue`\n/// objects, while leaving more complex value types to be implemented\n/// incrementally as Typed OM support expands.\n///\n/// Summary of derive attributes recognized by this derive:\n///\n/// * `#[typed(skip_derive_fields)]` on the type disables field recursion for\n///   structs and data-carrying enum variants.\n///\n/// * `#[css(skip)]`, `#[typed(skip)]`, or `#[typed(todo)]` on a variant mark\n///   it as unsupported and cause the generated arm to return\n///   `Err(())`.\n///\n/// * `#[css(skip)]` on a field disables reification for that field.\n///\n/// * `#[typed(skip_if = \"...\")]` on a field conditionally disables reification\n///   for that field. If the provided function returns `true` for the field\n///   value, the field is ignored.\n///\n/// * `#[css(keyword = \"...\")]` on a unit variant overrides the keyword\n///   string.\n///\n/// * `#[css(comma)]` on the variant indicates that fields may reify to\n///   multiple separate values. When present, multiple `TypedValue`s may be\n///   produced across the supported fields. If it is not present and the\n///   derived implementation would produce more than one item, it treats the\n///   value as unsupported and returns `Err(())`.\n///\n/// * `#[css(iterable)]` on a field indicates that the field is an iterable\n///   collection whose elements should be reified individually.\n///\n/// * `#[css(if_empty = \"...\")]` on an iterable field causes the provided\n///   keyword to be emitted when the iterable contains no elements.\npub fn derive(mut input: DeriveInput) -> TokenStream {\n    // The mutable `where_clause` is passed down to helper functions so they\n    // can append trait bounds only when necessary. In particular, a bound of\n    // the form `T: ToTyped` is added only if the generated code actually calls\n    // `.to_typed()` on that inner value. This avoids forcing unrelated types\n    // to implement `ToTyped` when field recursion is disabled, keeping\n    // compilation requirements minimal and isolated to cases where reification\n    // recursion is actually performed.\n    let mut where_clause = input.generics.where_clause.take();\n\n    let css_input_attrs = cg::parse_input_attrs::<CssInputAttrs>(&input);\n\n    let input_attrs = cg::parse_input_attrs::<TypedInputAttrs>(&input);\n\n    let body = match &input.data {\n        // Handle enums.\n        Data::Enum(DataEnum { variants, .. }) => {\n            // Check if this enum consists entirely of unit variants (no fields).\n            let all_unit = variants.iter().all(|v| matches!(v.fields, Fields::Unit));\n\n            if all_unit {\n                // Optimization: for all-unit enums, reuse `ToCss` once and\n                // wrap the result in a `TypedValue::Keyword`, instead of\n                // generating a full match. This avoids code bloat while\n                // producing the same runtime behavior.\n                quote! {\n                    fn to_typed(&self, dest: &mut thin_vec::ThinVec<style_traits::TypedValue>) -> Result<(), ()> {\n                      let s = style_traits::ToCss::to_css_cssstring(self);\n                      dest.push(style_traits::TypedValue::Keyword(style_traits::KeywordValue(s)));\n                      Ok(())\n                    }\n                }\n            } else {\n                // Mixed enums: generate a `match` where unit variants map to\n                // `TypedValue::Keyword` and all other variants return\n                // `Err(())`. This is more verbose in code size, but allows\n                // selective handling of individual variants.\n                let s = Structure::new(&input);\n                let match_body = s.each_variant(|variant| {\n                    derive_variant_arm(\n                        variant,\n                        input_attrs.skip_derive_fields || input_attrs.todo_derive_fields,\n                        &mut where_clause,\n                    )\n                });\n\n                quote! {\n                    fn to_typed(&self, dest: &mut thin_vec::ThinVec<style_traits::TypedValue>) -> Result<(), ()> {\n                        match *self {\n                            #match_body\n                        }\n                    }\n                }\n            }\n        },\n\n        // Handle structs that are not bitflags.\n        Data::Struct(_) => {\n            if css_input_attrs.bitflags.is_none() {\n                let s = Structure::new(&input);\n                let match_body = s.each_variant(|variant| {\n                    derive_variant_arm(\n                        variant,\n                        input_attrs.skip_derive_fields || input_attrs.todo_derive_fields,\n                        &mut where_clause,\n                    )\n                });\n\n                quote! {\n                    fn to_typed(&self, dest: &mut thin_vec::ThinVec<style_traits::TypedValue>) -> Result<(), ()> {\n                        match *self {\n                            #match_body\n                        }\n                    }\n                }\n            } else {\n                quote! {}\n            }\n        },\n\n        // Otherwise, don’t emit any `to_typed` method body. The default\n        // implementation (returning `Err(())`) will apply.\n        _ => quote! {},\n    };\n\n    input.generics.where_clause = where_clause;\n\n    let name = &input.ident;\n\n    // Split the input type’s generics into pieces we can use for impl.\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    // Put it all together into the impl block.\n    quote! {\n        impl #impl_generics style_traits::ToTyped for #name #ty_generics #where_clause {\n            #body\n        }\n    }\n}\n\n/// Generate the match arm expression for a struct or enum variant in a derived\n/// `ToTyped` implementation.\n///\n/// * Unit variants are reified into `TypedValue::Keyword`, using the variant’s\n///   identifier converted with `cg::to_css_identifier` or a custom keyword if\n///   provided through `#[css(keyword = \"...\")]`.\n/// * Variants marked with `#[css(skip)]` or `#[typed(skip)]` or\n///   `#[typed(todo)]` return `Err(())`.\n/// * Variants with fields delegate to `derive_variant_fields_expr()` by\n///   default; if `skip_derive_fields` is set, they return `Err(())`.\n///\n/// Note: `#[css(keyword = \"...\")]` overrides are now recognized in this\n/// `derive_variant_arm` path, but the support is not yet exercised because we\n/// currently have no mixed enums that use keyword overrides together with\n/// `ToTyped`. This keeps the behavior the same as before, all existing enums\n/// with keyword overrides (e.g. `#[css(keyword = \"preserve-3d\")]`) are still\n/// pure keyword enums and are handled through the all-unit `ToCss` path.\nfn derive_variant_arm(\n    variant: &synstructure::VariantInfo,\n    skip_derive_fields: bool,\n    where_clause: &mut Option<WhereClause>,\n) -> TokenStream {\n    let bindings = variant.bindings();\n    // Get the underlying syn AST node for this variant.\n    let ast = variant.ast();\n    let identifier = &ast.ident;\n\n    // Parse any #[css(...)] attributes attached to this variant.\n    let css_variant_attrs = cg::parse_variant_attrs_from_ast::<CssVariantAttrs>(&ast);\n\n    // Parse any #[typed(...)] attributes attached to this variant.\n    let variant_attrs = cg::parse_variant_attrs_from_ast::<TypedVariantAttrs>(&ast);\n\n    // If the variant is explicitly marked #[css(skip)], don’t generate\n    // anything for it, always return Err(()).\n    if css_variant_attrs.skip {\n        return quote! {Err(())};\n    }\n\n    // If the variant is explicitly marked #[typed(skip)] or #[typed(todo)],\n    // don’t generate anything for it, always return Err(()).\n    if variant_attrs.skip || variant_attrs.todo {\n        return quote! {Err(())};\n    }\n\n    // If the variant has no bindings (i.e. no data fields), treat it as a unit\n    // variant and reify it as a keyword.\n    if bindings.is_empty() {\n        // If #[css(keyword = \"...\")] is present, use it.\n        // Else convert the Rust variant name into its CSS identifier form\n        // (e.g. AvoidColumn -> \"avoid-column\").\n        let keyword = css_variant_attrs\n            .keyword\n            .unwrap_or_else(|| cg::to_css_identifier(&identifier.to_string()));\n\n        // Emit code to wrap this keyword into a TypedValue.\n        quote! {\n            dest.push(style_traits::TypedValue::Keyword(\n                style_traits::KeywordValue(style_traits::CssString::from(#keyword))\n            ));\n            Ok(())\n        }\n    } else if !skip_derive_fields {\n        derive_variant_fields_expr(bindings, where_clause, css_variant_attrs.comma)\n    } else {\n        // This variant has one or more fields, but field reification is\n        // disabled. With `skip_derive_fields`, this variant simply returns\n        // `Err(())`.\n        quote! {\n            Err(())\n        }\n    }\n}\n\n/// Generate the match arm expression for fields of a struct or enum variant\n/// in a derived `ToTyped` implementation.\n///\n/// This helper examines the variant’s fields and generates reification code\n/// for the usable fields.\n///\n/// * If the variant has exactly one non-iterable field, it emits a direct\n///   call to that field’s `ToTyped` implementation and adds the corresponding\n///   trait bound (e.g. `T: ToTyped`) to the `where` clause.\n///\n/// * Otherwise, it appends the reified output of the supported fields to the\n///   destination and then validates the combined result against the enclosing\n///   variant’s `#[css(comma)]` setting.\n///\n/// Fields marked with `#[css(skip)]`, or skipped by\n/// `#[typed(skip_if = \"...\")]`, are ignored.\nfn derive_variant_fields_expr(\n    bindings: &[BindingInfo],\n    where_clause: &mut Option<WhereClause>,\n    comma: bool,\n) -> TokenStream {\n    // Filter out fields marked with #[css(skip)] so they are ignored during\n    // reification.\n    let mut iter = bindings\n        .iter()\n        .filter_map(|binding| {\n            let css_field_attrs = cg::parse_field_attrs::<CssFieldAttrs>(&binding.ast());\n            let field_attrs = cg::parse_field_attrs::<TypedFieldAttrs>(&binding.ast());\n            if css_field_attrs.skip {\n                return None;\n            }\n            Some((binding, css_field_attrs, field_attrs))\n        })\n        .peekable();\n\n    // If no usable fields remain, generate code that just returns Err(()).\n    let (first, css_field_attrs, field_attrs) = match iter.next() {\n        Some(triple) => triple,\n        None => return quote! { Err(()) },\n    };\n\n    // At this point we have at least one usable field in `first`.\n\n    // Handle the simple case of exactly one non-iterable field.\n    if !css_field_attrs.iterable && iter.peek().is_none() {\n        // Add a trait bound `T: ToTyped` to ensure the field type implements\n        // the required conversion, and emit a call to its `.to_typed()`\n        // method.\n        let ty = &first.ast().ty;\n        cg::add_predicate(where_clause, parse_quote!(#ty: style_traits::ToTyped));\n\n        let mut expr = quote! { style_traits::ToTyped::to_typed(#first, dest) };\n\n        if let Some(condition) = field_attrs.skip_if {\n            expr = quote! {\n                if !#condition(#first) {\n                    #expr\n                }\n            }\n        }\n\n        return expr;\n    }\n\n    // Handle the general case by appending reified output from the supported\n    // fields directly to the destination.\n    let mut expr = derive_single_field_expr(first, css_field_attrs, field_attrs, where_clause);\n    for (binding, css_field_attrs, field_attrs) in iter {\n        derive_single_field_expr(binding, css_field_attrs, field_attrs, where_clause)\n            .to_tokens(&mut expr)\n    }\n\n    quote! {{\n        let old_len = dest.len();\n        #expr\n        if !#comma && dest.len() - old_len > 1 {\n            dest.truncate(old_len);\n            return Err(());\n        }\n        Ok(())\n    }}\n}\n\n/// Generate the expression used to reify a single field in a derived\n/// `ToTyped` implementation.\n///\n/// For fields marked with `#[css(iterable)]`, this helper generates code that\n/// iterates over the field and calls `ToTyped::to_typed` for each element. If\n/// `#[css(if_empty = \"...\")]` is present, the generated code emits the\n/// specified keyword when the iterable is empty.\n///\n/// For non-iterable fields, it generates a direct `ToTyped::to_typed` call\n/// for the field value.\n///\n/// If `#[typed(skip_if = \"...\")]` is present and the provided function returns\n/// `true` for the field value, the field contributes no reified output.\n///\n/// The appropriate `T: ToTyped` bounds for the field type or iterable element\n/// type(s) are added to the `where` clause.\nfn derive_single_field_expr(\n    field: &BindingInfo,\n    css_field_attrs: CssFieldAttrs,\n    field_attrs: TypedFieldAttrs,\n    where_clause: &mut Option<WhereClause>,\n) -> TokenStream {\n    let mut expr = if css_field_attrs.iterable {\n        // We add `ToTyped` bounds for the iterable's element type(s), rather\n        // than for the container type itself. This avoids ToTyped forcing\n        // unrelated container types to implement ToTyped.\n        //\n        // This is a bit more involved than other derives, but it matches how\n        // Typed OM reification is structured today. If this approach works\n        // well, the helper that extracts the field types\n        // (field_generic_arguments) can be moved into cg.rs alongside other\n        // derive helpers.\n        //\n        // See also the comment in the beginning of the main `derive` fn.\n        for item_ty in field_generic_arguments(field) {\n            cg::add_predicate(where_clause, parse_quote!(#item_ty: style_traits::ToTyped));\n        }\n\n        if let Some(if_empty) = css_field_attrs.if_empty {\n            quote! {\n                let mut iter = #field.iter().peekable();\n                if iter.peek().is_none() {\n                    dest.push(style_traits::TypedValue::Keyword(\n                        style_traits::KeywordValue(style_traits::CssString::from(#if_empty)),\n                    ));\n                } else {\n                    for item in iter {\n                        style_traits::ToTyped::to_typed(&item, dest)?;\n                    }\n                }\n            }\n        } else {\n            quote! {\n                for item in #field.iter() {\n                    style_traits::ToTyped::to_typed(&item, dest)?;\n                }\n            }\n        }\n    } else {\n        // Add a trait bound `T: ToTyped` to ensure the field type implements\n        // the required conversion, and emit a call to its `.to_typed()`\n        // method.\n        let ty = &field.ast().ty;\n        cg::add_predicate(where_clause, parse_quote!(#ty: style_traits::ToTyped));\n\n        quote! {\n           style_traits::ToTyped::to_typed(#field, dest)?;\n        }\n    };\n\n    if let Some(condition) = field_attrs.skip_if {\n        expr = quote! {\n            if !#condition(#field) {\n                #expr\n            }\n        }\n    }\n\n    expr\n}\n\n/// Extract generic type arguments from a field type.\n///\n/// This helper is used by the `derive_variant_fields_expr` when handling\n/// iterable fields. The function needs to add `T: ToTyped` bounds for the\n/// item type produced by iteration (since the generated code calls\n/// `.to_typed()` on each item).\n///\n/// For example:\n///   * `Vec<T>` / `OwnedSlice<T>` -> `T`\n///   * `SmallVec<[T; N]>` -> `T`\n///\n/// The function inspects the last path segment of the field’s type and\n/// returns any generic type arguments it finds, unwrapping array forms such\n/// as `[T; N]` used by containers like `SmallVec`.\n///\n/// This is intentionally minimal and currently supports the container shapes\n/// used in style structs.\npub(crate) fn field_generic_arguments(field: &BindingInfo) -> Vec<syn::Type> {\n    use syn::{GenericArgument, PathArguments, Type};\n\n    let ty = &field.ast().ty;\n\n    let Type::Path(type_path) = ty else {\n        return vec![];\n    };\n    let Some(seg) = type_path.path.segments.last() else {\n        return vec![];\n    };\n    let PathArguments::AngleBracketed(args) = &seg.arguments else {\n        return vec![];\n    };\n\n    let mut result = Vec::new();\n    for arg in &args.args {\n        let GenericArgument::Type(arg_ty) = arg else {\n            continue;\n        };\n\n        // If it's something like SmallVec<[T; N]>, take T.\n        match arg_ty {\n            Type::Array(arr) => result.push((*arr.elem).clone()),\n            _ => result.push(arg_ty.clone()),\n        }\n    }\n    result\n}\n\n#[derive(Default, FromDeriveInput)]\n#[darling(attributes(typed), default)]\npub struct TypedInputAttrs {\n    /// Disables field-level recursion when deriving `ToTyped`.\n    ///\n    /// When set, the derive will not call `.to_typed()` on inner values (for\n    /// example, struct fields or data-carrying enum variants), and instead\n    /// the generated code will return `Err(())` for those cases.\n    ///\n    /// This is useful to avoid requiring inner types to implement `ToTyped`\n    /// when reification of those fields is not yet supported.\n    pub skip_derive_fields: bool,\n\n    /// Temporarily disables field-level recursion while marking it as TODO.\n    ///\n    /// When set, the derive will not call `.to_typed()` on inner values and\n    /// instead return `Err(())` for those cases.\n    ///\n    /// Unlike `skip_derive_fields`, this indicates that reification is\n    /// expected to be revisited later, either to implement it or to switch to\n    /// `skip_derive_fields` if it turns out to be unsupported.\n    pub todo_derive_fields: bool,\n}\n\n#[derive(Default, FromVariant)]\n#[darling(attributes(typed), default)]\npub struct TypedVariantAttrs {\n    /// Same as the top-level `skip_derive_fields`, but included here because\n    /// struct variants are represented as both a variant and a type\n    /// definition.\n    ///\n    /// When set, field-level reification for this variant is disabled and the\n    /// generated code returns `Err(())`.\n    pub skip_derive_fields: bool,\n\n    /// Same as the top-level `todo_derive_fields`, but included here because\n    /// struct variants are represented as both a variant and a type\n    /// definition.\n    ///\n    /// When set, field-level reification for this variant is disabled and the\n    /// generated code returns `Err(())`.\n    pub todo_derive_fields: bool,\n\n    /// If present, this variant is excluded from generated reification code.\n    /// `to_typed()` will always return `Err(())` for it.\n    pub skip: bool,\n\n    /// Marks this variant as a placeholder for a future implementation.\n    /// Behavior is the same as `skip`, but used to indicate that reification\n    /// is intentionally left unimplemented for now.\n    pub todo: bool,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(typed), default)]\npub struct TypedFieldAttrs {\n    /// Conditionally skips reification of this field.\n    ///\n    /// The provided function is called with the field value. If it returns\n    /// `true`, the field is ignored and produces no `TypedValue` items.\n    pub skip_if: Option<Path>,\n}\n"
  },
  {
    "path": "style_traits/Cargo.toml",
    "content": "[package]\nname = \"stylo_traits\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/servo/stylo\"\nedition = \"2021\"\ndescription = \"Types used by the Stylo CSS engine\"\nreadme = \"../README.md\"\n\n[lib]\nname = \"style_traits\"\npath = \"lib.rs\"\n\n[features]\ndefault = [\"servo\"]\nservo = [\"stylo_atoms\", \"cssparser/serde\", \"url\", \"euclid/serde\"]\ngecko = []\n\n[dependencies]\napp_units = \"0.7\"\nbitflags = \"2\"\ncssparser = \"0.37\"\neuclid = \"0.22\"\nmalloc_size_of = { workspace = true}\nmalloc_size_of_derive = \"0.1\"\nselectors = { workspace = true}\nserde = \"1.0\"\nservo_arc = { workspace = true}\nstylo_atoms = { workspace = true, optional = true }\nthin-vec = \"0.2\"\nto_shmem = { workspace = true}\nto_shmem_derive = { workspace = true}\nurl = { version = \"2.5\", optional = true }\n"
  },
  {
    "path": "style_traits/arc_slice.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! A thin atomically-reference-counted slice.\n\nuse serde::de::{Deserialize, Deserializer};\nuse serde::ser::{Serialize, Serializer};\nuse servo_arc::ThinArc;\nuse std::ops::Deref;\nuse std::ptr::NonNull;\nuse std::sync::LazyLock;\nuse std::{\n    hash::{Hash, Hasher},\n    iter, mem,\n};\n\nuse malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf};\n\n/// A canary that we stash in ArcSlices.\n///\n/// Given we cannot use a zero-sized-type for the header, since well, C++\n/// doesn't have zsts, and we want to use cbindgen for this type, we may as well\n/// assert some sanity at runtime.\n///\n/// We use an u64, to guarantee that we can use a single singleton for every\n/// empty slice, even if the types they hold are aligned differently.\nconst ARC_SLICE_CANARY: u64 = 0xf3f3f3f3f3f3f3f3;\n\n/// A wrapper type for a refcounted slice using ThinArc.\n#[repr(C)]\n#[derive(Debug, Eq, PartialEq, ToShmem)]\npub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u64, T>);\n\nimpl<T> Deref for ArcSlice<T> {\n    type Target = [T];\n\n    #[inline]\n    fn deref(&self) -> &Self::Target {\n        debug_assert_eq!(self.0.header, ARC_SLICE_CANARY);\n        self.0.slice()\n    }\n}\n\nimpl<T> Clone for ArcSlice<T> {\n    fn clone(&self) -> Self {\n        ArcSlice(self.0.clone())\n    }\n}\n\n// ThinArc doesn't support alignments greater than align_of::<u64>.\nstatic EMPTY_ARC_SLICE: LazyLock<ArcSlice<u64>> =\n    LazyLock::new(|| ArcSlice::from_iter_leaked(iter::empty()));\n\nimpl<T> Default for ArcSlice<T> {\n    #[allow(unsafe_code)]\n    fn default() -> Self {\n        debug_assert!(\n            mem::align_of::<T>() <= mem::align_of::<u64>(),\n            \"Need to increase the alignment of EMPTY_ARC_SLICE\"\n        );\n        unsafe {\n            let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();\n            let empty: Self = mem::transmute(empty);\n            debug_assert_eq!(empty.len(), 0);\n            empty\n        }\n    }\n}\n\nimpl<T: Serialize> Serialize for ArcSlice<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        self.deref().serialize(serializer)\n    }\n}\n\nimpl<'de, T: Deserialize<'de>> Deserialize<'de> for ArcSlice<T> {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        let r = Vec::deserialize(deserializer)?;\n        Ok(ArcSlice::from_iter(r.into_iter()))\n    }\n}\n\nimpl<T> ArcSlice<T> {\n    /// Creates an Arc for a slice using the given iterator to generate the\n    /// slice.\n    #[inline]\n    pub fn from_iter<I>(items: I) -> Self\n    where\n        I: Iterator<Item = T> + ExactSizeIterator,\n    {\n        if items.len() == 0 {\n            return Self::default();\n        }\n        ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))\n    }\n\n    /// Creates an Arc for a slice using the given iterator to generate the\n    /// slice, and marks the arc as intentionally leaked from the refcount\n    /// logging point of view.\n    #[inline]\n    pub fn from_iter_leaked<I>(items: I) -> Self\n    where\n        I: Iterator<Item = T> + ExactSizeIterator,\n    {\n        let arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items);\n        arc.mark_as_intentionally_leaked();\n        ArcSlice(arc)\n    }\n\n    /// Creates a value that can be passed via FFI, and forgets this value\n    /// altogether.\n    #[inline]\n    pub fn forget(self) -> ForgottenArcSlicePtr<T> {\n        let ret = ForgottenArcSlicePtr(self.0.raw_ptr().cast());\n        mem::forget(self);\n        ret\n    }\n\n    /// Leaks an empty arc slice pointer, and returns it. Only to be used to\n    /// construct ArcSlices from FFI.\n    #[inline]\n    pub fn leaked_empty_ptr() -> *mut std::os::raw::c_void {\n        let empty: ArcSlice<_> = EMPTY_ARC_SLICE.clone();\n        let ptr = empty.0.raw_ptr();\n        std::mem::forget(empty);\n        ptr.cast().as_ptr()\n    }\n\n    /// Returns whether there's only one reference to this ArcSlice.\n    pub fn is_unique(&self) -> bool {\n        self.0.is_unique()\n    }\n}\n\nimpl<T: MallocSizeOf> MallocUnconditionalSizeOf for ArcSlice<T> {\n    #[allow(unsafe_code)]\n    fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) };\n        for el in self.iter() {\n            size += el.size_of(ops);\n        }\n        size\n    }\n}\n\nimpl<T: Hash> Hash for ArcSlice<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        T::hash_slice(&**self, state)\n    }\n}\n\n/// The inner pointer of an ArcSlice<T>, to be sent via FFI.\n/// The type of the pointer is a bit of a lie, we just want to preserve the type\n/// but these pointers cannot be constructed outside of this crate, so we're\n/// good.\n#[repr(C)]\npub struct ForgottenArcSlicePtr<T>(NonNull<T>);\n"
  },
  {
    "path": "style_traits/dom.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Types used to access the DOM from style calculation.\n\n/// An opaque handle to a node, which, unlike UnsafeNode, cannot be transformed\n/// back into a non-opaque representation. The only safe operation that can be\n/// performed on this node is to compare it to another opaque handle or to another\n/// OpaqueNode.\n///\n/// Layout and Graphics use this to safely represent nodes for comparison purposes.\n/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout\n/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for\n/// locality reasons. Using `OpaqueNode` enforces this invariant.\n#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize))]\npub struct OpaqueNode(pub usize);\n\nimpl OpaqueNode {\n    /// Returns the address of this node, for debugging purposes.\n    #[inline]\n    pub fn id(&self) -> usize {\n        self.0\n    }\n}\n"
  },
  {
    "path": "style_traits/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! This module contains shared types and messages for use by devtools/script.\n//! The traits are here instead of in script so that the devtools crate can be\n//! modified independently of the rest of Servo.\n\n#![crate_name = \"style_traits\"]\n#![crate_type = \"rlib\"]\n#![deny(unsafe_code, missing_docs)]\n\n#[macro_use]\nextern crate malloc_size_of_derive;\n#[macro_use]\nextern crate serde;\n#[macro_use]\nextern crate to_shmem_derive;\n#[cfg(feature = \"servo\")]\nextern crate url;\n\nuse bitflags::bitflags;\nuse cssparser::{CowRcStr, Token};\nuse selectors::parser::SelectorParseErrorKind;\n#[cfg(feature = \"servo\")]\nuse stylo_atoms::Atom;\n\n/// One hardware pixel.\n///\n/// This unit corresponds to the smallest addressable element of the display hardware.\n#[derive(Clone, Copy, Debug)]\npub enum DevicePixel {}\n\n/// Represents a mobile style pinch zoom factor.\n#[derive(Clone, Copy, Debug, PartialEq)]\n#[cfg_attr(feature = \"servo\", derive(Deserialize, Serialize, MallocSizeOf))]\npub struct PinchZoomFactor(f32);\n\nimpl PinchZoomFactor {\n    /// Construct a new pinch zoom factor.\n    pub fn new(scale: f32) -> PinchZoomFactor {\n        PinchZoomFactor(scale)\n    }\n\n    /// Get the pinch zoom factor as an untyped float.\n    pub fn get(&self) -> f32 {\n        self.0\n    }\n}\n\n/// One CSS \"px\" in the coordinate system of the \"initial viewport\":\n/// <http://www.w3.org/TR/css-device-adapt/#initial-viewport>\n///\n/// `CSSPixel` is equal to `DeviceIndependentPixel` times a \"page zoom\" factor controlled by the user.  This is\n/// the desktop-style \"full page\" zoom that enlarges content but then reflows the layout viewport\n/// so it still exactly fits the visible area.\n///\n/// At the default zoom level of 100%, one `CSSPixel` is equal to one `DeviceIndependentPixel`.  However, if the\n/// document is zoomed in or out then this scale may be larger or smaller.\n#[derive(Clone, Copy, Debug)]\npub enum CSSPixel {}\n\n// In summary, the hierarchy of pixel units and the factors to convert from one to the next:\n//\n// DevicePixel\n//   / hidpi_ratio => DeviceIndependentPixel\n//     / desktop_zoom => CSSPixel\n\npub mod arc_slice;\npub mod dom;\npub mod specified_value_info;\n#[macro_use]\npub mod values;\npub mod owned_slice;\npub mod owned_str;\n\npub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};\npub use crate::values::{\n    Comma, CommaWithSpace, CssString, CssStringWriter, CssWriter, KeywordValue, MathSum,\n    NumericValue, OneOrMoreSeparated, Separator, Space, ToCss, ToTyped, TypedValue, TypedValueList,\n    UnitValue, UnparsedSegment, UnparsedValue, VariableReferenceValue,\n};\n\n/// The error type for all CSS parsing routines.\npub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>;\n\n/// Error in property value parsing\npub type ValueParseError<'i> = cssparser::ParseError<'i, ValueParseErrorKind<'i>>;\n\n#[derive(Clone, Debug, PartialEq)]\n/// Errors that can be encountered while parsing CSS values.\npub enum StyleParseErrorKind<'i> {\n    /// A bad URL token in a DVB.\n    BadUrlInDeclarationValueBlock(CowRcStr<'i>),\n    /// A bad string token in a DVB.\n    BadStringInDeclarationValueBlock(CowRcStr<'i>),\n    /// Unexpected closing parenthesis in a DVB.\n    UnbalancedCloseParenthesisInDeclarationValueBlock,\n    /// Unexpected closing bracket in a DVB.\n    UnbalancedCloseSquareBracketInDeclarationValueBlock,\n    /// Unexpected closing curly bracket in a DVB.\n    UnbalancedCloseCurlyBracketInDeclarationValueBlock,\n    /// A property declaration value had input remaining after successfully parsing.\n    PropertyDeclarationValueNotExhausted,\n    /// An unexpected dimension token was encountered.\n    UnexpectedDimension(CowRcStr<'i>),\n    /// Missing or invalid media feature name.\n    MediaQueryExpectedFeatureName(CowRcStr<'i>),\n    /// Missing or invalid media feature value.\n    MediaQueryExpectedFeatureValue,\n    /// A media feature range operator was not expected.\n    MediaQueryUnexpectedOperator,\n    /// min- or max- properties must have a value.\n    RangedExpressionWithNoValue,\n    /// A function was encountered that was not expected.\n    UnexpectedFunction(CowRcStr<'i>),\n    /// Error encountered parsing a @property's `syntax` descriptor\n    PropertySyntaxField(PropertySyntaxParseError),\n    /// Error encountered parsing a @property's `inherits` descriptor.\n    ///\n    /// TODO(zrhoffman, bug 1920365): Include the custom property name in error messages.\n    PropertyInheritsField(PropertyInheritsParseError),\n    /// @namespace must be before any rule but @charset and @import\n    UnexpectedNamespaceRule,\n    /// @import must be before any rule but @charset\n    UnexpectedImportRule,\n    /// @import rules are disallowed in the parser.\n    DisallowedImportRule,\n    /// Unexpected @charset rule encountered.\n    UnexpectedCharsetRule,\n    /// The @property `<custom-property-name>` must start with `--`\n    UnexpectedIdent(CowRcStr<'i>),\n    /// A placeholder for many sources of errors that require more specific variants.\n    UnspecifiedError,\n    /// An unexpected token was found within a namespace rule.\n    UnexpectedTokenWithinNamespace(Token<'i>),\n    /// An error was encountered while parsing a property value.\n    ValueError(ValueParseErrorKind<'i>),\n    /// An error was encountered while parsing a selector\n    SelectorError(SelectorParseErrorKind<'i>),\n    /// The property declaration was for an unknown property.\n    UnknownProperty(CowRcStr<'i>),\n    /// The property declaration was for a disabled experimental property.\n    ExperimentalProperty,\n    /// The property declaration contained an invalid color value.\n    InvalidColor(CowRcStr<'i>, Token<'i>),\n    /// The property declaration contained an invalid filter value.\n    InvalidFilter(CowRcStr<'i>, Token<'i>),\n    /// The property declaration contained an invalid value.\n    OtherInvalidValue(CowRcStr<'i>),\n    /// `!important` declarations are disallowed in `@position-try` or keyframes.\n    UnexpectedImportantDeclaration,\n}\n\nimpl<'i> From<ValueParseErrorKind<'i>> for StyleParseErrorKind<'i> {\n    fn from(this: ValueParseErrorKind<'i>) -> Self {\n        StyleParseErrorKind::ValueError(this)\n    }\n}\n\nimpl<'i> From<SelectorParseErrorKind<'i>> for StyleParseErrorKind<'i> {\n    fn from(this: SelectorParseErrorKind<'i>) -> Self {\n        StyleParseErrorKind::SelectorError(this)\n    }\n}\n\n/// Specific errors that can be encountered while parsing property values.\n#[derive(Clone, Debug, PartialEq)]\npub enum ValueParseErrorKind<'i> {\n    /// An invalid token was encountered while parsing a color value.\n    InvalidColor(Token<'i>),\n    /// An invalid filter value was encountered.\n    InvalidFilter(Token<'i>),\n}\n\nimpl<'i> StyleParseErrorKind<'i> {\n    /// Create an InvalidValue parse error\n    pub fn new_invalid<S>(name: S, value_error: ParseError<'i>) -> ParseError<'i>\n    where\n        S: Into<CowRcStr<'i>>,\n    {\n        let name = name.into();\n        let variant = match value_error.kind {\n            cssparser::ParseErrorKind::Custom(StyleParseErrorKind::ValueError(e)) => match e {\n                ValueParseErrorKind::InvalidColor(token) => {\n                    StyleParseErrorKind::InvalidColor(name, token)\n                },\n                ValueParseErrorKind::InvalidFilter(token) => {\n                    StyleParseErrorKind::InvalidFilter(name, token)\n                },\n            },\n            _ => StyleParseErrorKind::OtherInvalidValue(name),\n        };\n        cssparser::ParseError {\n            kind: cssparser::ParseErrorKind::Custom(variant),\n            location: value_error.location,\n        }\n    }\n}\n\n/// Errors that can be encountered while parsing the @property rule's syntax descriptor.\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertySyntaxParseError {\n    /// The syntax descriptor is required for the @property rule to be valid; if it’s missing, the\n    /// @property rule is invalid.\n    ///\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#ref-for-descdef-property-syntax②>\n    NoSyntax,\n    /// The string's length was 0.\n    EmptyInput,\n    /// A non-whitespace, non-pipe character was fount after parsing a component.\n    ExpectedPipeBetweenComponents,\n    /// The start of an identifier was expected but not found.\n    ///\n    /// <https://drafts.csswg.org/css-syntax-3/#name-start-code-point>\n    InvalidNameStart,\n    /// The name is not a valid `<ident>`.\n    InvalidName,\n    /// The data type name was not closed.\n    ///\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#consume-data-type-name>\n    UnclosedDataTypeName,\n    /// The next byte was expected while parsing, but EOF was found instead.\n    UnexpectedEOF,\n    /// The data type is not a supported syntax component name.\n    ///\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#supported-names>\n    UnknownDataTypeName,\n}\n\n/// Errors that can be encountered while parsing the @property rule's inherits descriptor.\n#[derive(Clone, Debug, PartialEq)]\npub enum PropertyInheritsParseError {\n    /// The inherits descriptor is required for the @property rule to be valid; if it’s missing,\n    /// the @property rule is invalid.\n    ///\n    /// <https://drafts.css-houdini.org/css-properties-values-api-1/#ref-for-descdef-property-inherits②>\n    NoInherits,\n\n    /// The inherits descriptor must successfully parse as `true` or `false`.\n    InvalidInherits,\n}\n\nbitflags! {\n    /// The mode to use when parsing values.\n    #[derive(Clone, Copy, Eq, PartialEq)]\n    #[repr(C)]\n    pub struct ParsingMode: u8 {\n        /// In CSS; lengths must have units, except for zero values, where the unit can be omitted.\n        /// <https://www.w3.org/TR/css3-values/#lengths>\n        const DEFAULT = 0;\n        /// In SVG; a coordinate or length value without a unit identifier (e.g., \"25\") is assumed\n        /// to be in user units (px).\n        /// <https://www.w3.org/TR/SVG/coords.html#Units>\n        const ALLOW_UNITLESS_LENGTH = 1;\n        /// In SVG; out-of-range values are not treated as an error in parsing.\n        /// <https://www.w3.org/TR/SVG/implnote.html#RangeClamping>\n        const ALLOW_ALL_NUMERIC_VALUES = 1 << 1;\n        /// In CSS Properties and Values, the initial value must be computationally\n        /// independent.\n        /// <https://drafts.css-houdini.org/css-properties-values-api-1/#ref-for-computationally-independent%E2%91%A0>\n        const DISALLOW_COMPUTATIONALLY_DEPENDENT = 1 << 2;\n    }\n}\n\nimpl ParsingMode {\n    /// Whether the parsing mode allows unitless lengths for non-zero values to be intpreted as px.\n    #[inline]\n    pub fn allows_unitless_lengths(&self) -> bool {\n        self.intersects(ParsingMode::ALLOW_UNITLESS_LENGTH)\n    }\n\n    /// Whether the parsing mode allows all numeric values.\n    #[inline]\n    pub fn allows_all_numeric_values(&self) -> bool {\n        self.intersects(ParsingMode::ALLOW_ALL_NUMERIC_VALUES)\n    }\n\n    /// Whether the parsing mode allows units or functions that are not computationally independent.\n    #[inline]\n    pub fn allows_computational_dependence(&self) -> bool {\n        !self.intersects(ParsingMode::DISALLOW_COMPUTATIONALLY_DEPENDENT)\n    }\n}\n\n#[cfg(feature = \"servo\")]\n/// Speculatively execute paint code in the worklet thread pool.\npub trait SpeculativePainter: Send + Sync {\n    /// <https://drafts.css-houdini.org/css-paint-api/#draw-a-paint-image>\n    fn speculatively_draw_a_paint_image(\n        &self,\n        properties: Vec<(Atom, String)>,\n        arguments: Vec<String>,\n    );\n}\n"
  },
  {
    "path": "style_traits/owned_slice.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n\n//! A replacement for `Box<[T]>` that cbindgen can understand.\n\nuse malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};\nuse serde::de::{Deserialize, Deserializer};\nuse serde::ser::{Serialize, Serializer};\nuse std::marker::PhantomData;\nuse std::ops::{Deref, DerefMut};\nuse std::ptr::NonNull;\nuse std::{\n    fmt,\n    hash::{Hash, Hasher},\n    iter, mem, slice,\n};\nuse to_shmem::{SharedMemoryBuilder, ToShmem};\n\n/// A struct that basically replaces a `Box<[T]>`, but which cbindgen can\n/// understand.\n///\n/// We could rely on the struct layout of `Box<[T]>` per:\n///\n///   https://github.com/rust-lang/unsafe-code-guidelines/blob/master/reference/src/layout/pointers.md\n///\n/// But handling fat pointers with cbindgen both in structs and argument\n/// positions more generally is a bit tricky.\n///\n/// cbindgen:derive-eq=false\n/// cbindgen:derive-neq=false\n#[repr(C)]\npub struct OwnedSlice<T: Sized> {\n    ptr: NonNull<T>,\n    len: usize,\n    _phantom: PhantomData<T>,\n}\n\nimpl<T: Sized> Default for OwnedSlice<T> {\n    #[inline]\n    fn default() -> Self {\n        Self {\n            len: 0,\n            ptr: NonNull::dangling(),\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<T: Sized> Drop for OwnedSlice<T> {\n    #[inline]\n    fn drop(&mut self) {\n        if self.len != 0 {\n            let _ = mem::replace(self, Self::default()).into_vec();\n        }\n    }\n}\n\nunsafe impl<T: Sized + Send> Send for OwnedSlice<T> {}\nunsafe impl<T: Sized + Sync> Sync for OwnedSlice<T> {}\n\nimpl<T: Clone> Clone for OwnedSlice<T> {\n    #[inline]\n    fn clone(&self) -> Self {\n        Self::from_slice(&**self)\n    }\n}\n\nimpl<T: fmt::Debug> fmt::Debug for OwnedSlice<T> {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        self.deref().fmt(formatter)\n    }\n}\n\nimpl<T: PartialEq> PartialEq for OwnedSlice<T> {\n    fn eq(&self, other: &Self) -> bool {\n        self.deref().eq(other.deref())\n    }\n}\n\nimpl<T: Eq> Eq for OwnedSlice<T> {}\n\nimpl<T: Sized> OwnedSlice<T> {\n    /// Convert the OwnedSlice into a boxed slice.\n    #[inline]\n    pub fn into_box(self) -> Box<[T]> {\n        self.into_vec().into_boxed_slice()\n    }\n\n    /// Convert the OwnedSlice into a Vec.\n    #[inline]\n    pub fn into_vec(self) -> Vec<T> {\n        let ret = unsafe { Vec::from_raw_parts(self.ptr.as_ptr(), self.len, self.len) };\n        mem::forget(self);\n        ret\n    }\n\n    /// Convert the regular slice into an owned slice.\n    #[inline]\n    pub fn from_slice(s: &[T]) -> Self\n    where\n        T: Clone,\n    {\n        Self::from(s.to_vec())\n    }\n}\n\nimpl<T> IntoIterator for OwnedSlice<T> {\n    type Item = T;\n    type IntoIter = <Vec<T> as IntoIterator>::IntoIter;\n\n    #[inline]\n    fn into_iter(self) -> Self::IntoIter {\n        self.into_vec().into_iter()\n    }\n}\n\nimpl<T> Deref for OwnedSlice<T> {\n    type Target = [T];\n\n    #[inline(always)]\n    fn deref(&self) -> &Self::Target {\n        unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }\n    }\n}\n\nimpl<T> DerefMut for OwnedSlice<T> {\n    #[inline(always)]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }\n    }\n}\n\nimpl<T> From<Box<[T]>> for OwnedSlice<T> {\n    #[inline]\n    fn from(mut b: Box<[T]>) -> Self {\n        let len = b.len();\n        let ptr = unsafe { NonNull::new_unchecked(b.as_mut_ptr()) };\n        mem::forget(b);\n        Self {\n            len,\n            ptr,\n            _phantom: PhantomData,\n        }\n    }\n}\n\nimpl<T> From<Vec<T>> for OwnedSlice<T> {\n    #[inline]\n    fn from(b: Vec<T>) -> Self {\n        Self::from(b.into_boxed_slice())\n    }\n}\n\nimpl<T: Sized> MallocShallowSizeOf for OwnedSlice<T> {\n    fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        unsafe { ops.malloc_size_of(self.ptr.as_ptr()) }\n    }\n}\n\nimpl<T: MallocSizeOf + Sized> MallocSizeOf for OwnedSlice<T> {\n    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {\n        self.shallow_size_of(ops) + (**self).size_of(ops)\n    }\n}\n\nimpl<T: ToShmem + Sized> ToShmem for OwnedSlice<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {\n        unsafe {\n            let dest = to_shmem::to_shmem_slice(self.iter(), builder)?;\n            Ok(mem::ManuallyDrop::new(Self::from(Box::from_raw(dest))))\n        }\n    }\n}\n\nimpl<T> iter::FromIterator<T> for OwnedSlice<T> {\n    #[inline]\n    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {\n        Vec::from_iter(iter).into()\n    }\n}\n\nimpl<T: Serialize> Serialize for OwnedSlice<T> {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        self.deref().serialize(serializer)\n    }\n}\n\nimpl<'de, T: Deserialize<'de>> Deserialize<'de> for OwnedSlice<T> {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        let r = Box::<[T]>::deserialize(deserializer)?;\n        Ok(r.into())\n    }\n}\n\nimpl<T: Hash> Hash for OwnedSlice<T> {\n    fn hash<H: Hasher>(&self, state: &mut H) {\n        T::hash_slice(&**self, state)\n    }\n}\n"
  },
  {
    "path": "style_traits/owned_str.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![allow(unsafe_code)]\n\n//! A replacement for `Box<str>` that has a defined layout for FFI.\n\nuse crate::owned_slice::OwnedSlice;\nuse std::fmt;\nuse std::ops::{Deref, DerefMut};\n\n/// A struct that basically replaces a Box<str>, but with a defined layout,\n/// suitable for FFI.\n#[repr(C)]\n#[derive(Clone, Default, Eq, MallocSizeOf, PartialEq, ToShmem)]\npub struct OwnedStr(OwnedSlice<u8>);\n\nimpl fmt::Debug for OwnedStr {\n    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {\n        self.deref().fmt(formatter)\n    }\n}\n\nimpl Deref for OwnedStr {\n    type Target = str;\n\n    #[inline(always)]\n    fn deref(&self) -> &Self::Target {\n        unsafe { std::str::from_utf8_unchecked(&*self.0) }\n    }\n}\n\nimpl DerefMut for OwnedStr {\n    #[inline(always)]\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        unsafe { std::str::from_utf8_unchecked_mut(&mut *self.0) }\n    }\n}\n\nimpl OwnedStr {\n    /// Convert the OwnedStr into a boxed str.\n    #[inline]\n    pub fn into_box(self) -> Box<str> {\n        self.into_string().into_boxed_str()\n    }\n\n    /// Convert the OwnedStr into a `String`.\n    #[inline]\n    pub fn into_string(self) -> String {\n        unsafe { String::from_utf8_unchecked(self.0.into_vec()) }\n    }\n}\n\nimpl From<OwnedStr> for String {\n    #[inline]\n    fn from(b: OwnedStr) -> Self {\n        b.into_string()\n    }\n}\n\nimpl From<OwnedStr> for Box<str> {\n    #[inline]\n    fn from(b: OwnedStr) -> Self {\n        b.into_box()\n    }\n}\n\nimpl From<Box<str>> for OwnedStr {\n    #[inline]\n    fn from(b: Box<str>) -> Self {\n        Self::from(b.into_string())\n    }\n}\n\nimpl From<String> for OwnedStr {\n    #[inline]\n    fn from(s: String) -> Self {\n        OwnedStr(s.into_bytes().into())\n    }\n}\n"
  },
  {
    "path": "style_traits/specified_value_info.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Value information for devtools.\n\nuse crate::arc_slice::ArcSlice;\nuse crate::owned_slice::OwnedSlice;\nuse servo_arc::Arc;\nuse std::ops::Range;\nuse std::sync::Arc as StdArc;\nuse thin_vec::ThinVec;\n\n/// Type of value that a property supports. This is used by Gecko's\n/// devtools to make sense about value it parses, and types listed\n/// here should match InspectorPropertyType in InspectorUtils.webidl.\n///\n/// XXX This should really be a bitflags rather than a namespace mod,\n/// but currently we cannot use bitflags in const.\n#[allow(non_snake_case)]\npub mod CssType {\n    /// <color>\n    pub const COLOR: u8 = 1 << 0;\n    /// <gradient>\n    pub const GRADIENT: u8 = 1 << 1;\n    /// <timing-function>\n    pub const TIMING_FUNCTION: u8 = 1 << 2;\n}\n\n/// See SpecifiedValueInfo::collect_completion_keywords.\npub type KeywordsCollectFn<'a> = &'a mut dyn FnMut(&[&'static str]);\n\n/// Information of values of a given specified value type.\n///\n/// This trait is derivable with `#[derive(SpecifiedValueInfo)]`.\n///\n/// The algorithm traverses the type definition. For `SUPPORTED_TYPES`,\n/// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds.\n/// For `collect_completion_keywords`, it recursively invokes this\n/// method on types found, and lists all keyword values and function\n/// names following the same rule as `ToCss` in that method.\n///\n/// Some attributes of `ToCss` can affect the behavior, specifically:\n/// * If `#[css(function)]` is found, the content inside the annotated\n///   variant (or the whole type) isn't traversed, only the function\n///   name is listed in `collect_completion_keywords`.\n/// * If `#[css(skip)]` is found, the content inside the variant or\n///   field is ignored.\n/// * Values listed in `#[css(if_empty)]`, `#[parse(aliases)]`, and\n///   `#[css(keyword)]` are added into `collect_completion_keywords`.\n///\n/// In addition to `css` attributes, it also has `value_info` helper\n/// attributes, including:\n/// * `#[value_info(ty = \"TYPE\")]` can be used to specify a constant\n///   from `CssType` to `SUPPORTED_TYPES`.\n/// * `#[value_info(other_values = \"value1,value2\")]` can be used to\n///   add other values related to a field, variant, or the type itself\n///   into `collect_completion_keywords`.\n/// * `#[value_info(starts_with_keyword)]` can be used on variants to\n///   add the name of a non-unit variant (serialized like `ToCss`) into\n///   `collect_completion_keywords`.\npub trait SpecifiedValueInfo {\n    /// Supported CssTypes by the given value type.\n    ///\n    /// XXX This should be typed CssType when that becomes a bitflags.\n    /// Currently we cannot do so since bitflags cannot be used in constant.\n    const SUPPORTED_TYPES: u8 = 0;\n\n    /// Collect value starting words for the given specified value type.\n    /// This includes keyword and function names which can appear at the\n    /// beginning of a value of this type.\n    ///\n    /// Caller should pass in a callback function to accept the list of\n    /// values. The callback function can be called multiple times, and\n    /// some values passed to the callback may be duplicate.\n    fn collect_completion_keywords(_f: KeywordsCollectFn) {}\n}\n\nimpl SpecifiedValueInfo for bool {}\nimpl SpecifiedValueInfo for f32 {}\nimpl SpecifiedValueInfo for i8 {}\nimpl SpecifiedValueInfo for i32 {}\nimpl SpecifiedValueInfo for u8 {}\nimpl SpecifiedValueInfo for u16 {}\nimpl SpecifiedValueInfo for u32 {}\nimpl SpecifiedValueInfo for usize {}\nimpl SpecifiedValueInfo for str {}\nimpl SpecifiedValueInfo for String {}\nimpl SpecifiedValueInfo for crate::owned_str::OwnedStr {}\n\n#[cfg(feature = \"servo\")]\nimpl SpecifiedValueInfo for ::stylo_atoms::Atom {}\n#[cfg(feature = \"servo\")]\nimpl SpecifiedValueInfo for ::url::Url {}\n\nimpl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {\n    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        T::collect_completion_keywords(f);\n    }\n}\n\nimpl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {\n    const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        T::collect_completion_keywords(f);\n    }\n}\n\nmacro_rules! impl_generic_specified_value_info {\n    ($ty:ident<$param:ident>) => {\n        impl<$param: SpecifiedValueInfo> SpecifiedValueInfo for $ty<$param> {\n            const SUPPORTED_TYPES: u8 = $param::SUPPORTED_TYPES;\n            fn collect_completion_keywords(f: KeywordsCollectFn) {\n                $param::collect_completion_keywords(f);\n            }\n        }\n    };\n}\nimpl_generic_specified_value_info!(Option<T>);\nimpl_generic_specified_value_info!(OwnedSlice<T>);\nimpl_generic_specified_value_info!(Vec<T>);\nimpl_generic_specified_value_info!(ThinVec<T>);\nimpl_generic_specified_value_info!(Arc<T>);\nimpl_generic_specified_value_info!(StdArc<T>);\nimpl_generic_specified_value_info!(ArcSlice<T>);\nimpl_generic_specified_value_info!(Range<Idx>);\n\nimpl<T1, T2> SpecifiedValueInfo for (T1, T2)\nwhere\n    T1: SpecifiedValueInfo,\n    T2: SpecifiedValueInfo,\n{\n    const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;\n\n    fn collect_completion_keywords(f: KeywordsCollectFn) {\n        T1::collect_completion_keywords(f);\n        T2::collect_completion_keywords(f);\n    }\n}\n"
  },
  {
    "path": "style_traits/values.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Helper types and traits for the handling of CSS values.\n\nuse app_units::Au;\nuse cssparser::ToCss as CssparserToCss;\nuse cssparser::{serialize_string, ParseError, Parser, Token, UnicodeRange};\nuse servo_arc::Arc;\nuse std::fmt::{self, Write};\nuse thin_vec::ThinVec;\n\n/// Serialises a value according to its CSS representation.\n///\n/// This trait is implemented for `str` and its friends, serialising the string\n/// contents as a CSS quoted string.\n///\n/// This trait is derivable with `#[derive(ToCss)]`, with the following behaviour:\n/// * unit variants get serialised as the `snake-case` representation\n///   of their name;\n/// * unit variants whose name starts with \"Moz\" or \"Webkit\" are prepended\n///   with a \"-\";\n/// * if `#[css(comma)]` is found on a variant, its fields are separated by\n///   commas, otherwise, by spaces;\n/// * if `#[css(function)]` is found on a variant, the variant name gets\n///   serialised like unit variants and its fields are surrounded by parentheses;\n/// * if `#[css(iterable)]` is found on a function variant, that variant needs\n///   to have a single member, and that member needs to be iterable. The\n///   iterable will be serialized as the arguments for the function;\n/// * an iterable field can also be annotated with `#[css(if_empty = \"foo\")]`\n///   to print `\"foo\"` if the iterator is empty;\n/// * if `#[css(dimension)]` is found on a variant, that variant needs\n///   to have a single member. The variant would be serialized as a CSS\n///   dimension token, like: <member><identifier>;\n/// * if `#[css(skip)]` is found on a field, the `ToCss` call for that field\n///   is skipped;\n/// * if `#[css(skip_if = \"function\")]` is found on a field, the `ToCss` call\n///   for that field is skipped if `function` returns true. This function is\n///   provided the field as an argument;\n/// * if `#[css(contextual_skip_if = \"function\")]` is found on a field, the\n///   `ToCss` call for that field is skipped if `function` returns true. This\n///   function is given all the fields in the current struct or variant as an\n///   argument;\n/// * `#[css(represents_keyword)]` can be used on bool fields in order to\n///   serialize the field name if the field is true, or nothing otherwise.  It\n///   also collects those keywords for `SpecifiedValueInfo`.\n/// * `#[css(bitflags(single=\"\", mixed=\"\", validate_mixed=\"\", overlapping_bits)]` can\n///   be used to derive parse / serialize / etc on bitflags. The rules for parsing\n///   bitflags are the following:\n///\n///     * `single` flags can only appear on their own. It's common that bitflags\n///       properties at least have one such value like `none` or `auto`.\n///     * `mixed` properties can appear mixed together, but not along any other\n///       flag that shares a bit with itself. For example, if you have three\n///       bitflags like:\n///\n///         FOO = 1 << 0;\n///         BAR = 1 << 1;\n///         BAZ = 1 << 2;\n///         BAZZ = BAR | BAZ;\n///\n///       Then the following combinations won't be valid:\n///\n///         * foo foo: (every flag shares a bit with itself)\n///         * bar bazz: (bazz shares a bit with bar)\n///\n///       But `bar baz` will be valid, as they don't share bits, and so would\n///       `foo` with any other flag, or `bazz` on its own.\n///    * `validate_mixed` can be used to reject invalid mixed combinations, and also to simplify\n///      the type or add default ones if needed.\n///    * `overlapping_bits` enables some tracking during serialization of mixed flags to avoid\n///       serializing variants that can subsume other variants.\n///       In the example above, you could do:\n///         mixed=\"foo,bazz,bar,baz\", overlapping_bits\n///       to ensure that if bazz is serialized, bar and baz aren't, even though\n///       their bits are set. Note that the serialization order is canonical,\n///       and thus depends on the order you specify the flags in.\n///\n/// * finally, one can put `#[css(derive_debug)]` on the whole type, to\n///   implement `Debug` by a single call to `ToCss::to_css`.\npub trait ToCss {\n    /// Serialize `self` in CSS syntax, writing to `dest`.\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write;\n\n    /// Serialize `self` in CSS syntax and return a string.\n    ///\n    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)\n    #[inline]\n    fn to_css_string(&self) -> String {\n        let mut s = String::new();\n        self.to_css(&mut CssWriter::new(&mut s)).unwrap();\n        s\n    }\n\n    /// Serialize `self` in CSS syntax and return a CssString.\n    ///\n    /// (This is a convenience wrapper for `to_css` and probably should not be overridden.)\n    #[inline]\n    fn to_css_cssstring(&self) -> CssString {\n        let mut s = CssString::new();\n        self.to_css(&mut CssWriter::new(&mut s)).unwrap();\n        s\n    }\n}\n\nimpl<'a, T> ToCss for &'a T\nwhere\n    T: ToCss + ?Sized,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        (*self).to_css(dest)\n    }\n}\n\nimpl ToCss for crate::owned_str::OwnedStr {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_string(self, dest)\n    }\n}\n\nimpl ToCss for str {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_string(self, dest)\n    }\n}\n\nimpl ToCss for String {\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        serialize_string(self, dest)\n    }\n}\n\nimpl<T> ToCss for Option<T>\nwhere\n    T: ToCss,\n{\n    #[inline]\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.as_ref().map_or(Ok(()), |value| value.to_css(dest))\n    }\n}\n\nimpl ToCss for () {\n    #[inline]\n    fn to_css<W>(&self, _: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        Ok(())\n    }\n}\n\n/// A writer tailored for serialising CSS.\n///\n/// Coupled with SequenceWriter, this allows callers to transparently handle\n/// things like comma-separated values etc.\npub struct CssWriter<'w, W: 'w> {\n    inner: &'w mut W,\n    prefix: Option<&'static str>,\n}\n\nimpl<'w, W> CssWriter<'w, W>\nwhere\n    W: Write,\n{\n    /// Creates a new `CssWriter`.\n    #[inline]\n    pub fn new(inner: &'w mut W) -> Self {\n        Self {\n            inner,\n            prefix: Some(\"\"),\n        }\n    }\n}\n\nimpl<'w, W> Write for CssWriter<'w, W>\nwhere\n    W: Write,\n{\n    #[inline]\n    fn write_str(&mut self, s: &str) -> fmt::Result {\n        if s.is_empty() {\n            return Ok(());\n        }\n        if let Some(prefix) = self.prefix.take() {\n            // We are going to write things, but first we need to write\n            // the prefix that was set by `SequenceWriter::item`.\n            if !prefix.is_empty() {\n                self.inner.write_str(prefix)?;\n            }\n        }\n        self.inner.write_str(s)\n    }\n\n    #[inline]\n    fn write_char(&mut self, c: char) -> fmt::Result {\n        if let Some(prefix) = self.prefix.take() {\n            // See comment in `write_str`.\n            if !prefix.is_empty() {\n                self.inner.write_str(prefix)?;\n            }\n        }\n        self.inner.write_char(c)\n    }\n}\n\n/// To avoid accidentally instantiating multiple monomorphizations of large\n/// serialization routines, we define explicit concrete types and require\n/// them in those routines. This avoids accidental mixing of String and\n/// nsACString arguments in Gecko, which would cause code size to blow up.\n#[cfg(feature = \"gecko\")]\npub type CssStringWriter = ::nsstring::nsACString;\n\n/// String type that coerces to CssStringWriter, used when serialization code\n/// needs to allocate a temporary string. In Gecko, this is backed by\n/// nsCString, which allows the result to be passed directly to C++ without\n/// conversion or copying. This makes it suitable not only for temporary\n/// serialization but also for values that need to cross the Rust/C++ boundary.\n#[cfg(feature = \"gecko\")]\npub type CssString = ::nsstring::nsCString;\n\n/// String. The comments for the Gecko types explain the need for this abstraction.\n#[cfg(feature = \"servo\")]\npub type CssStringWriter = String;\n\n/// String. The comments for the Gecko types explain the need for this abstraction.\n#[cfg(feature = \"servo\")]\npub type CssString = String;\n\n/// Convenience wrapper to serialise CSS values separated by a given string.\npub struct SequenceWriter<'a, 'b: 'a, W: 'b> {\n    inner: &'a mut CssWriter<'b, W>,\n    separator: &'static str,\n}\n\nimpl<'a, 'b, W> SequenceWriter<'a, 'b, W>\nwhere\n    W: Write + 'b,\n{\n    /// Returns whether this writer has written any item.\n    pub fn has_written(&self) -> bool {\n        // See comment in item()\n        self.inner.prefix.is_none()\n    }\n\n    /// Create a new sequence writer.\n    #[inline]\n    pub fn new(inner: &'a mut CssWriter<'b, W>, separator: &'static str) -> Self {\n        if inner.prefix.is_none() {\n            // See comment in `item`.\n            inner.prefix = Some(\"\");\n        }\n        Self { inner, separator }\n    }\n\n    /// Serialize the CSS Value with the specific serialization function.\n    #[inline]\n    pub fn write_item<F>(&mut self, f: F) -> fmt::Result\n    where\n        F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,\n    {\n        // Separate non-generic functions so that this code is not repeated\n        // in every monomorphization with a different type `F` or `W`.\n        // https://github.com/servo/servo/issues/26713\n        fn before(\n            prefix: &mut Option<&'static str>,\n            separator: &'static str,\n        ) -> Option<&'static str> {\n            let old_prefix = *prefix;\n            if old_prefix.is_none() {\n                // If there is no prefix in the inner writer, a previous\n                // call to this method produced output, which means we need\n                // to write the separator next time we produce output again.\n                *prefix = Some(separator);\n            }\n            old_prefix\n        }\n        fn after(\n            old_prefix: Option<&'static str>,\n            prefix: &mut Option<&'static str>,\n            separator: &'static str,\n        ) {\n            match (old_prefix, *prefix) {\n                (_, None) => {\n                    // This call produced output and cleaned up after itself.\n                },\n                (None, Some(p)) => {\n                    // Some previous call to `item` produced output,\n                    // but this one did not, prefix should be the same as\n                    // the one we set.\n                    debug_assert_eq!(separator, p);\n                    // We clean up here even though it's not necessary just\n                    // to be able to do all these assertion checks.\n                    *prefix = None;\n                },\n                (Some(old), Some(new)) => {\n                    // No previous call to `item` produced output, and this one\n                    // either.\n                    debug_assert_eq!(old, new);\n                },\n            }\n        }\n\n        let old_prefix = before(&mut self.inner.prefix, self.separator);\n        f(self.inner)?;\n        after(old_prefix, &mut self.inner.prefix, self.separator);\n        Ok(())\n    }\n\n    /// Serialises a CSS value, writing any separator as necessary.\n    ///\n    /// The separator is never written before any `item` produces any output,\n    /// and is written in subsequent calls only if the `item` produces some\n    /// output on its own again. This lets us handle `Option<T>` fields by\n    /// just not printing anything on `None`.\n    #[inline]\n    pub fn item<T>(&mut self, item: &T) -> fmt::Result\n    where\n        T: ToCss,\n    {\n        self.write_item(|inner| item.to_css(inner))\n    }\n\n    /// Writes a string as-is (i.e. not escaped or wrapped in quotes)\n    /// with any separator as necessary.\n    ///\n    /// See SequenceWriter::item.\n    #[inline]\n    pub fn raw_item(&mut self, item: &str) -> fmt::Result {\n        self.write_item(|inner| inner.write_str(item))\n    }\n}\n\n/// Type used as the associated type in the `OneOrMoreSeparated` trait on a\n/// type to indicate that a serialized list of elements of this type is\n/// separated by commas.\npub struct Comma;\n\n/// Type used as the associated type in the `OneOrMoreSeparated` trait on a\n/// type to indicate that a serialized list of elements of this type is\n/// separated by spaces.\npub struct Space;\n\n/// Type used as the associated type in the `OneOrMoreSeparated` trait on a\n/// type to indicate that a serialized list of elements of this type is\n/// separated by commas, but spaces without commas are also allowed when\n/// parsing.\npub struct CommaWithSpace;\n\n/// A trait satisfied by the types corresponding to separators.\npub trait Separator {\n    /// The separator string that the satisfying separator type corresponds to.\n    fn separator() -> &'static str;\n\n    /// Parses a sequence of values separated by this separator.\n    ///\n    /// The given closure is called repeatedly for each item in the sequence.\n    ///\n    /// Successful results are accumulated in a vector.\n    ///\n    /// This method returns `Err(_)` the first time a closure does or if\n    /// the separators aren't correct.\n    fn parse<'i, 't, F, T, E>(\n        parser: &mut Parser<'i, 't>,\n        parse_one: F,\n    ) -> Result<Vec<T>, ParseError<'i, E>>\n    where\n        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>;\n}\n\nimpl Separator for Comma {\n    fn separator() -> &'static str {\n        \", \"\n    }\n\n    fn parse<'i, 't, F, T, E>(\n        input: &mut Parser<'i, 't>,\n        parse_one: F,\n    ) -> Result<Vec<T>, ParseError<'i, E>>\n    where\n        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,\n    {\n        input.parse_comma_separated(parse_one)\n    }\n}\n\nimpl Separator for Space {\n    fn separator() -> &'static str {\n        \" \"\n    }\n\n    fn parse<'i, 't, F, T, E>(\n        input: &mut Parser<'i, 't>,\n        mut parse_one: F,\n    ) -> Result<Vec<T>, ParseError<'i, E>>\n    where\n        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,\n    {\n        input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.\n        let mut results = vec![parse_one(input)?];\n        loop {\n            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.\n            if let Ok(item) = input.try_parse(&mut parse_one) {\n                results.push(item);\n            } else {\n                return Ok(results);\n            }\n        }\n    }\n}\n\nimpl Separator for CommaWithSpace {\n    fn separator() -> &'static str {\n        \", \"\n    }\n\n    fn parse<'i, 't, F, T, E>(\n        input: &mut Parser<'i, 't>,\n        mut parse_one: F,\n    ) -> Result<Vec<T>, ParseError<'i, E>>\n    where\n        F: for<'tt> FnMut(&mut Parser<'i, 'tt>) -> Result<T, ParseError<'i, E>>,\n    {\n        input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.\n        let mut results = vec![parse_one(input)?];\n        loop {\n            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.\n            let comma_location = input.current_source_location();\n            let comma = input.try_parse(|i| i.expect_comma()).is_ok();\n            input.skip_whitespace(); // Unnecessary for correctness, but may help try_parse() rewind less.\n            if let Ok(item) = input.try_parse(&mut parse_one) {\n                results.push(item);\n            } else if comma {\n                return Err(comma_location.new_unexpected_token_error(Token::Comma));\n            } else {\n                break;\n            }\n        }\n        Ok(results)\n    }\n}\n\n/// Marker trait on T to automatically implement ToCss for Vec<T> when T's are\n/// separated by some delimiter `delim`.\npub trait OneOrMoreSeparated {\n    /// Associated type indicating which separator is used.\n    type S: Separator;\n}\n\nimpl OneOrMoreSeparated for UnicodeRange {\n    type S = Comma;\n}\n\nimpl<T> ToCss for Vec<T>\nwhere\n    T: ToCss + OneOrMoreSeparated,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        let mut iter = self.iter();\n        iter.next().unwrap().to_css(dest)?;\n        for item in iter {\n            dest.write_str(<T as OneOrMoreSeparated>::S::separator())?;\n            item.to_css(dest)?;\n        }\n        Ok(())\n    }\n}\n\nimpl<T> ToCss for Box<T>\nwhere\n    T: ?Sized + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        (**self).to_css(dest)\n    }\n}\n\nimpl<T> ToCss for Arc<T>\nwhere\n    T: ?Sized + ToCss,\n{\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        (**self).to_css(dest)\n    }\n}\n\nimpl ToCss for Au {\n    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n    where\n        W: Write,\n    {\n        self.to_f64_px().to_css(dest)?;\n        dest.write_str(\"px\")\n    }\n}\n\nmacro_rules! impl_to_css_for_predefined_type {\n    ($name: ty) => {\n        impl<'a> ToCss for $name {\n            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result\n            where\n                W: Write,\n            {\n                ::cssparser::ToCss::to_css(self, dest)\n            }\n        }\n    };\n}\n\nimpl_to_css_for_predefined_type!(f32);\nimpl_to_css_for_predefined_type!(i8);\nimpl_to_css_for_predefined_type!(i32);\nimpl_to_css_for_predefined_type!(u8);\nimpl_to_css_for_predefined_type!(u16);\nimpl_to_css_for_predefined_type!(u32);\nimpl_to_css_for_predefined_type!(::cssparser::Token<'a>);\nimpl_to_css_for_predefined_type!(::cssparser::UnicodeRange);\n\n/// Helper types for the handling of specified values.\npub mod specified {\n    use crate::ParsingMode;\n\n    /// Whether to allow negative lengths or not.\n    #[repr(u8)]\n    #[derive(\n        Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, PartialEq, PartialOrd, Serialize, ToShmem,\n    )]\n    pub enum AllowedNumericType {\n        /// Allow all kind of numeric values.\n        All,\n        /// Allow only non-negative numeric values.\n        NonNegative,\n        /// Allow only numeric values greater or equal to 1.0.\n        AtLeastOne,\n        /// Allow only numeric values from 0 to 1.0.\n        ZeroToOne,\n    }\n\n    impl Default for AllowedNumericType {\n        #[inline]\n        fn default() -> Self {\n            AllowedNumericType::All\n        }\n    }\n\n    impl AllowedNumericType {\n        /// Whether the value fits the rules of this numeric type.\n        #[inline]\n        pub fn is_ok(&self, parsing_mode: ParsingMode, val: f32) -> bool {\n            if parsing_mode.allows_all_numeric_values() {\n                return true;\n            }\n            match *self {\n                AllowedNumericType::All => true,\n                AllowedNumericType::NonNegative => val >= 0.0,\n                AllowedNumericType::AtLeastOne => val >= 1.0,\n                AllowedNumericType::ZeroToOne => val >= 0.0 && val <= 1.0,\n            }\n        }\n\n        /// Clamp the value following the rules of this numeric type.\n        #[inline]\n        pub fn clamp(&self, val: f32) -> f32 {\n            match *self {\n                AllowedNumericType::All => val,\n                AllowedNumericType::NonNegative => val.max(0.),\n                AllowedNumericType::AtLeastOne => val.max(1.),\n                AllowedNumericType::ZeroToOne => val.max(0.).min(1.),\n            }\n        }\n    }\n}\n\n/// A single segment of an unparsed Typed OM value.\n///\n/// This corresponds to the `CSSUnparsedSegment` union in the Typed OM\n/// specification. Unparsed values are represented as a list of string\n/// fragments and variable references.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub enum UnparsedSegment {\n    /// A string fragment.\n    ///\n    /// This corresponds to the string branch of `CSSUnparsedSegment` and is\n    /// used for the non-variable parts of a `CSSUnparsedValue`.\n    String(CssString),\n\n    /// A `var()` reference segment.\n    ///\n    /// This corresponds to `CSSVariableReferenceValue` in the Typed OM\n    /// specification.\n    VariableReference(VariableReferenceValue),\n}\n\n/// An unparsed value used by the Typed OM.\n///\n/// This corresponds to `CSSUnparsedValue` in the Typed OM specification. It\n/// is used for values that cannot be reified into a more specific\n/// property-agnostic representation and therefore need to preserve their\n/// token-like structure as a sequence of string fragments and variable\n/// references.\n///\n/// The underlying list of segments corresponds to the `[[tokens]]` internal\n/// slot of `CSSUnparsedValue`.\n///\n/// This is represented as a type alias over `ThinVec<UnparsedSegment>` rather\n/// than a dedicated struct. This avoids the need for additional wrapper types\n/// when embedding unparsed values within other structures, while still\n/// allowing recursive representations via the segment list.\npub type UnparsedValue = ThinVec<UnparsedSegment>;\n\n/// A variable reference inside an unparsed Typed OM value.\n///\n/// This corresponds to `CSSVariableReferenceValue` in the Typed OM\n/// specification.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub struct VariableReferenceValue {\n    /// The referenced custom property name.\n    ///\n    /// This corresponds to the `variable` attribute of\n    /// `CSSVariableReferenceValue`.\n    pub variable: CssString,\n\n    /// The fallback value, if present.\n    ///\n    /// This corresponds to the `fallback` attribute of\n    /// `CSSVariableReferenceValue`. When `has_fallback` is false, this value\n    /// must be ignored. When `has_fallback` is true, this contains the\n    /// fallback tokens (which may be empty).\n    pub fallback: UnparsedValue,\n\n    /// Whether a fallback was explicitly provided.\n    ///\n    /// This is needed to distinguish between the absence of a fallback\n    /// (`var(--a)`) and an explicitly empty fallback (`var(--a,)`), which are\n    /// observable via Typed OM.\n    pub has_fallback: bool,\n}\n\n/// A keyword value used by the Typed OM.\n///\n/// This corresponds to `CSSKeywordValue` in the Typed OM specification.\n/// The keyword is stored as a `CssString` so it can be represented and\n/// transferred independently of any specific property (e.g. `\"none\"`,\n/// `\"block\"`, `\"thin\"`).\n#[derive(Clone, Debug)]\n#[repr(C)]\npub struct KeywordValue(pub CssString);\n\n/// A single numeric value with an associated unit.\n///\n/// This corresponds to `CSSUnitValue` in the Typed OM specification. The\n/// numeric component is stored separately from the textual unit identifier.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub struct UnitValue {\n    /// The numeric component of the value.\n    pub value: f32,\n\n    /// The textual unit string (e.g. `\"px\"`, `\"em\"`, `\"%\"`, `\"deg\"`).\n    pub unit: CssString,\n}\n\n/// A sum of numeric values.\n///\n/// This corresponds to `CSSMathSum` in the Typed OM specification. A sum\n/// value represents an expression such as `10px + 2em`. Each entry is itself\n/// a `NumericValue`, allowing nested sums if needed.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub struct MathSum {\n    /// The list of numeric terms that make up the sum.\n    pub values: ThinVec<NumericValue>,\n}\n\n/// A numeric value used by the Typed OM.\n///\n/// This corresponds to `CSSNumericValue` and its subclasses in the Typed OM\n/// specification. It represents numbers that can appear in CSS values,\n/// including both simple unit quantities and composite expressions.\n///\n/// Unlike the parser-level representation, `NumericValue` is property-agnostic\n/// and suitable for conversion to or from the `CSSNumericValue` family of DOM\n/// objects.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub enum NumericValue {\n    /// A single numeric value with a concrete unit.\n    ///\n    /// This corresponds to `CSSUnitValue`.\n    Unit(UnitValue),\n\n    /// A sum of numeric values.\n    ///\n    /// This corresponds to `CSSMathSum`.\n    Sum(MathSum),\n}\n\n/// A property-agnostic representation of a value, used by Typed OM.\n///\n/// `TypedValue` is the internal counterpart of the various `CSSStyleValue`\n/// subclasses defined by the Typed OM specification. It captures values that\n/// can be represented independently of any particular property.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub enum TypedValue {\n    /// An unparsed value consisting of string fragments and variable\n    /// references.\n    ///\n    /// This corresponds to `CSSUnparsedValue` in the Typed OM specification.\n    Unparsed(UnparsedValue),\n\n    /// A keyword value such as `\"block\"`, `\"none\"`, or `\"thin\"`.\n    ///\n    /// This corresponds to `CSSKeywordValue` in the Typed OM specification.\n    /// Keywords are represented as a standalone `KeywordValue` so they can\n    /// be carried and compared independently of any particular property.\n    Keyword(KeywordValue),\n\n    /// A numeric value such as a length, angle, time, or a sum thereof.\n    ///\n    /// This corresponds to the `CSSNumericValue` hierarchy in the Typed OM\n    /// specification, including `CSSUnitValue` and `CSSMathSum`.\n    Numeric(NumericValue),\n}\n\n/// A list of property-agnostic values used by the Typed OM.\n///\n/// `TypedValueList` is the internal counterpart of CSS value lists exposed by\n/// Typed OM. It stores one or more [`TypedValue`] items in source order and\n/// is used when a value reifies to multiple property-agnostic components.\n#[derive(Clone, Debug)]\n#[repr(C)]\npub struct TypedValueList {\n    /// The list of reified values.\n    pub values: ThinVec<TypedValue>,\n}\n\n/// Reifies a value into its Typed OM representation.\n///\n/// This trait is the Typed OM analogue of [`ToCss`]. Instead of serializing\n/// values into CSS syntax, it converts them into [`TypedValue`]s that can be\n/// exposed to the DOM as `CSSStyleValue` subclasses.\n///\n/// Most consumers should use [`ToTyped::to_typed_value`] or\n/// [`ToTyped::to_typed_value_list`], depending on whether they need a single\n/// reified value or the full list of reified values.\n///\n/// This trait is derivable with `#[derive(ToTyped)]`. The derived\n/// implementation currently supports:\n///\n/// * Keyword enums: Enums whose variants are all unit variants are\n///   automatically reified as [`TypedValue::Keyword`], using the same\n///   serialization logic as [`ToCss`].\n///\n/// * Structs and data-carrying variants: By default, the derive attempts to\n///   call `.to_typed()` recursively on supported fields or variant payloads,\n///   producing [`TypedValue`]s when possible. This recursion can be disabled\n///   with `#[typed(skip_derive_fields)]`.\n///\n/// * Other cases: If no automatic mapping is defined, or recursion is\n///   explicitly disabled, the derived implementation falls back to the\n///   default method (which returns `Err(())`, and thus `to_typed_value()`\n///   returns `None`).\n///\n/// Over time, the derive may be extended to handle additional CSS value\n/// categories such as numeric, color, and transform types.\n///\n/// Summary of derive attributes recognized by `#[derive(ToTyped)]`:\n///\n/// * `#[typed(skip_derive_fields)]` on the type disables recursion for\n///   structs and data-carrying enum variants.\n///\n/// * `#[css(skip)]`, `#[typed(skip)]`, or `#[typed(todo)]` on a variant cause\n///   that variant to be treated as unsupported (the derived implementation\n///   returns `Err(())`).\n///\n/// * `#[css(skip)]` on a field causes that field to be ignored during\n///   reification.\n///\n/// * `#[typed(skip_if = \"...\")]` on a field conditionally disables reification\n///   for that field. If the provided function returns `true` for the field\n///   value, the field is ignored.\n///\n/// * `#[css(keyword = \"...\")]` on a unit variant overrides the keyword that\n///   would otherwise be derived from the Rust identifier.\n///\n/// * `#[css(comma)]` on the variant indicates that supported fields may reify\n///   to multiple separate values. When this attribute is present, multiple\n///   [`TypedValue`] items may be produced. If it is not present and the\n///   derived implementation would produce more than one item, it returns\n///   `Err(())`.\n///\n/// * `#[css(iterable)]` on a field indicates that the field represents a list\n///   of values. Each item in the iterable is reified individually by calling\n///   `ToTyped::to_typed` on the element type.\n///\n/// * `#[css(if_empty = \"...\")]` on an iterable field specifies a keyword\n///   value that should be produced when the iterable is empty.\npub trait ToTyped {\n    /// Attempt to convert `self` into one or more [`TypedValue`] items.\n    ///\n    /// Implementations append any resulting values to `dest`. This is the\n    /// low-level entry point used by the Typed OM reification infrastructure.\n    /// Most callers should prefer [`ToTyped::to_typed_value`] or\n    /// [`ToTyped::to_typed_value_list`].\n    ///\n    /// Returning `Err(())` indicates that the value cannot be represented as\n    /// a property-agnostic Typed OM value.\n    fn to_typed(&self, _dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        Err(())\n    }\n\n    /// Attempt to convert `self` into a [`TypedValue`].\n    ///\n    /// Returns the first reified value as `Some(TypedValue)` if the value can\n    /// be reified into a property-agnostic CSSStyleValue subclass. Returns\n    /// `None` if the value is unrepresentable, in which case consumers\n    /// produce a property-tied CSSStyleValue instead.\n    fn to_typed_value(&self) -> Option<TypedValue> {\n        let mut dest = ThinVec::new();\n        self.to_typed(&mut dest).ok()?;\n        dest.into_iter().next()\n    }\n\n    /// Attempt to convert `self` into a [`TypedValueList`].\n    ///\n    /// Returns `Some(TypedValueList)` if the value can be reified into one or\n    /// more property-agnostic Typed OM values. Returns `None` if the value is\n    /// unrepresentable, in which case consumers produce a property-tied\n    /// `CSSStyleValue` instead.\n    fn to_typed_value_list(&self) -> Option<TypedValueList> {\n        let mut dest = ThinVec::new();\n        self.to_typed(&mut dest).ok()?;\n        Some(TypedValueList { values: dest })\n    }\n}\n\nimpl<'a, T> ToTyped for &'a T\nwhere\n    T: ToTyped + ?Sized,\n{\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        (*self).to_typed(dest)\n    }\n}\n\nimpl<T> ToTyped for Box<T>\nwhere\n    T: ?Sized + ToTyped,\n{\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        (**self).to_typed(dest)\n    }\n}\n\nimpl<T> ToTyped for Arc<T>\nwhere\n    T: ?Sized + ToTyped,\n{\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        (**self).to_typed(dest)\n    }\n}\n\nimpl ToTyped for Au {\n    fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n        let value = self.to_f32_px();\n        let unit = CssString::from(\"px\");\n        dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {\n            value,\n            unit,\n        })));\n        Ok(())\n    }\n}\n\nmacro_rules! impl_to_typed_for_predefined_type {\n    ($name: ty) => {\n        impl<'a> ToTyped for $name {\n            fn to_typed(&self, dest: &mut ThinVec<TypedValue>) -> Result<(), ()> {\n                dest.push(TypedValue::Numeric(NumericValue::Unit(UnitValue {\n                    value: *self as f32,\n                    unit: CssString::from(\"number\"),\n                })));\n                Ok(())\n            }\n        }\n    };\n}\n\nimpl_to_typed_for_predefined_type!(f32);\nimpl_to_typed_for_predefined_type!(i8);\nimpl_to_typed_for_predefined_type!(i32);\n"
  },
  {
    "path": "stylo_atoms/Cargo.toml",
    "content": "[package]\nname = \"stylo_atoms\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\ndocumentation = \"https://docs.rs/stylo_atoms/\"\ndescription = \"Interned string type for the Servo and Stylo projects\"\nrepository = \"https://github.com/servo/stylo\"\nlicense = \"MPL-2.0\"\nedition = \"2018\"\nbuild = \"build.rs\"\nreadme = \"../README.md\"\n\n[lib]\npath = \"lib.rs\"\n\n[dependencies]\nstring_cache = \"0.9\"\n\n[build-dependencies]\nstring_cache_codegen = \"0.6.1\"\n"
  },
  {
    "path": "stylo_atoms/build.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse std::env;\nuse std::fs::File;\nuse std::io::{BufRead, BufReader};\nuse std::path::Path;\n\nfn main() {\n    let static_atoms =\n        Path::new(&env::var_os(\"CARGO_MANIFEST_DIR\").unwrap()).join(\"static_atoms.txt\");\n    let static_atoms = BufReader::new(File::open(&static_atoms).unwrap());\n    let mut atom_type = string_cache_codegen::AtomType::new(\"Atom\", \"atom!\");\n\n    macro_rules! predefined {\n        ($($name: expr,)+) => {\n            {\n                $(\n                    atom_type.atom($name);\n                )+\n            }\n        }\n    }\n    include!(\"./predefined_counter_styles.rs\");\n\n    atom_type\n        .atoms(static_atoms.lines().map(Result::unwrap))\n        .write_to_file(&Path::new(&env::var_os(\"OUT_DIR\").unwrap()).join(\"atom.rs\"))\n        .unwrap();\n}\n"
  },
  {
    "path": "stylo_atoms/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\ninclude!(concat!(env!(\"OUT_DIR\"), \"/atom.rs\"));\n"
  },
  {
    "path": "stylo_atoms/predefined_counter_styles.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n // THIS FILE IS DUPLICATED FROM style/counter_style/predefined.rs.\n // TO UPDATE IT:\n //   - Run `python style/counter_style/updated_predefined.py`\n //   - Re-copy style/counter_style/predefined.rs to this location\n\npredefined! {\n    \"decimal\",\n    \"decimal-leading-zero\",\n    \"arabic-indic\",\n    \"armenian\",\n    \"upper-armenian\",\n    \"lower-armenian\",\n    \"bengali\",\n    \"cambodian\",\n    \"khmer\",\n    \"cjk-decimal\",\n    \"devanagari\",\n    \"georgian\",\n    \"gujarati\",\n    \"gurmukhi\",\n    \"hebrew\",\n    \"kannada\",\n    \"lao\",\n    \"malayalam\",\n    \"mongolian\",\n    \"myanmar\",\n    \"oriya\",\n    \"persian\",\n    \"lower-roman\",\n    \"upper-roman\",\n    \"tamil\",\n    \"telugu\",\n    \"thai\",\n    \"tibetan\",\n    \"lower-alpha\",\n    \"lower-latin\",\n    \"upper-alpha\",\n    \"upper-latin\",\n    \"cjk-earthly-branch\",\n    \"cjk-heavenly-stem\",\n    \"lower-greek\",\n    \"hiragana\",\n    \"hiragana-iroha\",\n    \"katakana\",\n    \"katakana-iroha\",\n    \"disc\",\n    \"circle\",\n    \"square\",\n    \"disclosure-open\",\n    \"disclosure-closed\",\n    \"japanese-informal\",\n    \"japanese-formal\",\n    \"korean-hangul-formal\",\n    \"korean-hanja-informal\",\n    \"korean-hanja-formal\",\n    \"simp-chinese-informal\",\n    \"simp-chinese-formal\",\n    \"trad-chinese-informal\",\n    \"trad-chinese-formal\",\n    \"cjk-ideographic\",\n    \"ethiopic-numeric\",\n}\n"
  },
  {
    "path": "stylo_atoms/static_atoms.txt",
    "content": "-moz-content-preferred-color-scheme\n-moz-device-pixel-ratio\n-moz-fixed-pos-containing-block\n-moz-gtk-csd-close-button-position\n-moz-gtk-csd-maximize-button-position\n-moz-gtk-csd-menu-radius\n-moz-gtk-csd-minimize-button-position\n-moz-gtk-csd-titlebar-button-spacing\n-moz-gtk-csd-titlebar-radius\n-moz-gtk-csd-tooltip-radius\n-moz-gtk-menu-radius\n-moz-mac-titlebar-height\n-moz-overlay-scrollbar-fade-duration\nDOMContentLoaded\nabort\nactivate\naddtrack\nall\nanimationcancel\nanimationend\nanimationiteration\nanimationstart\naspect-ratio\nbeforeinput\nbeforetoggle\nbeforeunload\nblock-size\nbutton\ncanplay\ncanplaythrough\ncenter\nchange\ncharacteristicvaluechanged\ncheckbox\ncancel\nclick\nclose\nclosing\ncolor\ncommand\ncomplete\ncompositionend\ncompositionstart\ncompositionupdate\ncontrollerchange\ncursive\ndark\ndatachannel\ndate\ndatetime-local\ndir\ndevice-pixel-ratio\ndurationchange\nemail\nemptied\nend\nended\nerror\nfantasy\nfetch\nfile\nfill\nfill-opacity\nformdata\nfullscreenchange\nfullscreenerror\ngattserverdisconnected\nhairline\nhashchange\nheight\nhidden\nicecandidate\niceconnectionstatechange\nicegatheringstatechange\nimage\ninline-size\ninput\ninputsourceschange\ninvalid\nkeydown\nkeypress\nkind\nleft\nlight\nltr\nload\nloadeddata\nloadedmetadata\nloadend\nloadstart\nmatch-element\nmessage\nmessage\nmessageerror\nmonospace\nmonth\nmousedown\nmousemove\nmouseover\nmouseup\nnegotiationneeded\nnone\nnormal\nnumber\nonchange\nopen\norientation\npagehide\npageshow\npassword\npause\nplay\nplaying\npopstate\npostershown\nprefers-color-scheme\nprint\nprogress\nradio\nrange\nratechange\nreadystatechange\nreferrer\nreftest-wait\nrejectionhandled\nremovetrack\nreset\nresize\nresolution\nresourcetimingbufferfull\nright\nrtl\nsans-serif\nsafe-area-inset-top\nsafe-area-inset-bottom\nsafe-area-inset-left\nsafe-area-inset-right\nscan\nscreen\nscroll-position\nscrollbar-inline-size\nsearch\nseeked\nseeking\nselect\nselectend\nselectionchange\nselectstart\nserif\nsessionavailable\nshow\nsignalingstatechange\nslotchange\nsqueeze\nsqueezeend\nsqueezestart\nsrclang\nstatechange\nstroke\nstroke-opacity\nstorage\nsubmit\nsuspend\nsystem-ui\ntel\ntext\ntime\ntimeupdate\ntoggle\ntrack\ntransitioncancel\ntransitionend\ntransitionrun\ntransitionstart\nuncapturederror\nunhandledrejection\nunload\nurl\nvisibilitychange\nvolumechange\nwaiting\nwebglcontextcreationerror\nwebkitAnimationEnd\nwebkitAnimationIteration\nwebkitAnimationStart\nwebkitTransitionEnd\nwebkitTransitionRun\nweek\nwidth\n"
  },
  {
    "path": "stylo_dom/Cargo.toml",
    "content": "[package]\nname = \"stylo_dom\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\ndocumentation = \"https://docs.rs/stylo_dom/\"\ndescription = \"DOM state types for Stylo\"\nrepository = \"https://github.com/servo/stylo\"\nkeywords = [\"css\", \"style\"]\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nreadme = \"../README.md\"\n\n[lib]\npath = \"lib.rs\"\n\n[dependencies]\nbitflags = \"2\"\nmalloc_size_of = { workspace = true }\n"
  },
  {
    "path": "stylo_dom/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse bitflags::bitflags;\nuse malloc_size_of::malloc_size_of_is_0;\n\npub const HEADING_LEVEL_OFFSET: usize = 52;\n\n// DOM types to be shared between Rust and C++.\nbitflags! {\n    /// Event-based element states.\n    #[repr(C)]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct ElementState: u64 {\n        /// The mouse is down on this element.\n        /// <https://html.spec.whatwg.org/multipage/#selector-active>\n        /// FIXME(#7333): set/unset this when appropriate\n        const ACTIVE = 1 << 0;\n        /// This element has focus.\n        /// <https://html.spec.whatwg.org/multipage/#selector-focus>\n        const FOCUS = 1 << 1;\n        /// The mouse is hovering over this element.\n        /// <https://html.spec.whatwg.org/multipage/#selector-hover>\n        const HOVER = 1 << 2;\n        /// Content is enabled (and can be disabled).\n        /// <http://www.whatwg.org/html/#selector-enabled>\n        const ENABLED = 1 << 3;\n        /// Content is disabled.\n        /// <http://www.whatwg.org/html/#selector-disabled>\n        const DISABLED = 1 << 4;\n        /// Content is checked.\n        /// <https://html.spec.whatwg.org/multipage/#selector-checked>\n        const CHECKED = 1 << 5;\n        /// <https://html.spec.whatwg.org/multipage/#selector-indeterminate>\n        const INDETERMINATE = 1 << 6;\n        /// <https://html.spec.whatwg.org/multipage/#selector-placeholder-shown>\n        const PLACEHOLDER_SHOWN = 1 << 7;\n        /// <https://html.spec.whatwg.org/multipage/#selector-target>\n        const URLTARGET = 1 << 8;\n        /// <https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class>\n        const FULLSCREEN = 1 << 9;\n        /// <https://html.spec.whatwg.org/multipage/#selector-valid>\n        const VALID = 1 << 10;\n        /// <https://html.spec.whatwg.org/multipage/#selector-invalid>\n        const INVALID = 1 << 11;\n        /// <https://drafts.csswg.org/selectors-4/#user-valid-pseudo>\n        const USER_VALID = 1 << 12;\n        /// <https://drafts.csswg.org/selectors-4/#user-invalid-pseudo>\n        const USER_INVALID = 1 << 13;\n        /// All the validity bits at once.\n        const VALIDITY_STATES = Self::VALID.bits() | Self::INVALID.bits() | Self::USER_VALID.bits() | Self::USER_INVALID.bits();\n        /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken\n        const BROKEN = 1 << 14;\n        /// <https://html.spec.whatwg.org/multipage/#selector-required>\n        const REQUIRED = 1 << 15;\n        /// <https://html.spec.whatwg.org/multipage/#selector-optional>\n        /// We use an underscore to workaround a silly windows.h define.\n        const OPTIONAL_ = 1 << 16;\n        /// <https://html.spec.whatwg.org/multipage/#selector-defined>\n        const DEFINED = 1 << 17;\n        /// <https://html.spec.whatwg.org/multipage/#selector-visited>\n        const VISITED = 1 << 18;\n        /// <https://html.spec.whatwg.org/multipage/#selector-link>\n        const UNVISITED = 1 << 19;\n        /// <https://drafts.csswg.org/selectors-4/#the-any-link-pseudo>\n        const VISITED_OR_UNVISITED = Self::VISITED.bits() | Self::UNVISITED.bits();\n        /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over\n        const DRAGOVER = 1 << 20;\n        /// <https://html.spec.whatwg.org/multipage/#selector-in-range>\n        const INRANGE = 1 << 21;\n        /// <https://html.spec.whatwg.org/multipage/#selector-out-of-range>\n        const OUTOFRANGE = 1 << 22;\n        /// <https://html.spec.whatwg.org/multipage/#selector-read-only>\n        const READONLY = 1 << 23;\n        /// <https://html.spec.whatwg.org/multipage/#selector-read-write>\n        const READWRITE = 1 << 24;\n        /// <https://html.spec.whatwg.org/multipage/#selector-default>\n        const DEFAULT = 1 << 25;\n        /// Non-standard & undocumented.\n        const OPTIMUM = 1 << 26;\n        /// Non-standard & undocumented.\n        const SUB_OPTIMUM = 1 << 27;\n        /// Non-standard & undocumented.\n        const SUB_SUB_OPTIMUM = 1 << 28;\n        /// All the above <meter> bits in one place.\n        const METER_OPTIMUM_STATES = Self::OPTIMUM.bits() | Self::SUB_OPTIMUM.bits() | Self::SUB_SUB_OPTIMUM.bits();\n        /// Non-standard & undocumented.\n        const INCREMENT_SCRIPT_LEVEL = 1 << 29;\n        /// <https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo>\n        const FOCUSRING = 1 << 30;\n        /// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo>\n        const FOCUS_WITHIN = 1u64 << 31;\n        /// :dir matching; the states are used for dynamic change detection.\n        /// State that elements that match :dir(ltr) are in.\n        const LTR = 1u64 << 32;\n        /// State that elements that match :dir(rtl) are in.\n        const RTL = 1u64 << 33;\n        /// State that HTML elements that have a \"dir\" attr are in.\n        const HAS_DIR_ATTR = 1u64 << 34;\n        /// State that HTML elements with dir=\"ltr\" (or something\n        /// case-insensitively equal to \"ltr\") are in.\n        const HAS_DIR_ATTR_LTR = 1u64 << 35;\n        /// State that HTML elements with dir=\"rtl\" (or something\n        /// case-insensitively equal to \"rtl\") are in.\n        const HAS_DIR_ATTR_RTL = 1u64 << 36;\n        /// State that HTML <bdi> elements without a valid-valued \"dir\" attr or\n        /// any HTML elements (including <bdi>) with dir=\"auto\" (or something\n        /// case-insensitively equal to \"auto\") are in.\n        const HAS_DIR_ATTR_LIKE_AUTO = 1u64 << 37;\n        /// Non-standard & undocumented.\n        const AUTOFILL = 1u64 << 38;\n        /// Non-standard & undocumented.\n        const AUTOFILL_PREVIEW = 1u64 << 39;\n        /// State for modal elements:\n        /// <https://drafts.csswg.org/selectors-4/#modal-state>\n        const MODAL = 1u64 << 40;\n        /// <https://html.spec.whatwg.org/multipage/#inert-subtrees>\n        const INERT = 1u64 << 41;\n        /// State for the topmost modal element in top layer\n        const TOPMOST_MODAL = 1u64 << 42;\n        /// Initially used for the devtools highlighter, but now somehow only\n        /// used for the devtools accessibility inspector.\n        const DEVTOOLS_HIGHLIGHTED = 1u64 << 43;\n        /// Used for the devtools style editor. Probably should go away.\n        const STYLEEDITOR_TRANSITIONING = 1u64 << 44;\n        /// For :-moz-value-empty (to show widgets like the reveal password\n        /// button or the clear button).\n        const VALUE_EMPTY = 1u64 << 45;\n        /// For :-moz-revealed.\n        const REVEALED = 1u64 << 46;\n        /// https://html.spec.whatwg.org/#selector-popover-open\n        /// Match element's popover visibility state of showing\n        const POPOVER_OPEN = 1u64 << 47;\n        /// https://drafts.csswg.org/css-scoping-1/#the-has-slotted-pseudo\n        /// Match whether a slot element has assigned nodes\n        const HAS_SLOTTED = 1u64 << 48;\n        /// https://drafts.csswg.org/selectors-4/#open-state\n        /// Match whether an openable element is currently open\n        const OPEN = 1u64 << 49;\n        /// For :active-view-transition.\n        /// <https://www.w3.org/TR/css-view-transitions-2/#the-active-view-transition-pseudo>\n        const ACTIVE_VIEW_TRANSITION = 1u64 << 50;\n        /// For :-moz-suppress-for-print-selection.\n        const SUPPRESS_FOR_PRINT_SELECTION = 1u64 << 51;\n        /// https://drafts.csswg.org/selectors-5/#headings\n        /// These 4 bits are used to pack the elements heading level into the element state\n        /// Heading levels can be from 1-9 so 4 bits allows us to express the full range.\n        const HEADING_LEVEL_BITS = 0b1111u64 << HEADING_LEVEL_OFFSET;\n\n        /// Some convenience unions.\n        const DIR_STATES = Self::LTR.bits() | Self::RTL.bits();\n\n        const DIR_ATTR_STATES = Self::HAS_DIR_ATTR.bits() |\n                                Self::HAS_DIR_ATTR_LTR.bits() |\n                                Self::HAS_DIR_ATTR_RTL.bits() |\n                                Self::HAS_DIR_ATTR_LIKE_AUTO.bits();\n\n        const DISABLED_STATES = Self::DISABLED.bits() | Self::ENABLED.bits();\n\n        const REQUIRED_STATES = Self::REQUIRED.bits() | Self::OPTIONAL_.bits();\n    }\n}\n\nbitflags! {\n    /// Event-based document states.\n    #[repr(C)]\n    #[derive(Clone, Copy, Debug, Eq, PartialEq)]\n    pub struct DocumentState: u64 {\n        /// Window activation status\n        const WINDOW_INACTIVE = 1 << 0;\n        /// RTL locale: specific to the XUL localedir attribute\n        const RTL_LOCALE = 1 << 1;\n        /// LTR locale: specific to the XUL localedir attribute\n        const LTR_LOCALE = 1 << 2;\n\n        const ALL_LOCALEDIR_BITS = Self::LTR_LOCALE.bits() | Self::RTL_LOCALE.bits();\n    }\n}\n\nmalloc_size_of_is_0!(ElementState, DocumentState);\n"
  },
  {
    "path": "stylo_static_prefs/Cargo.toml",
    "content": "[package]\nname = \"stylo_static_prefs\"\nversion.workspace = true\nauthors = [\"The Servo Project Developers\"]\ndocumentation = \"https://docs.rs/stylo_static_prefs/\"\ndescription = \"Configuration for Stylo\"\nrepository = \"https://github.com/servo/stylo\"\nkeywords = [\"css\", \"style\"]\nlicense = \"MPL-2.0\"\nedition = \"2021\"\nreadme = \"../README.md\"\n"
  },
  {
    "path": "stylo_static_prefs/src/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse std::collections::HashMap;\nuse std::sync::{LazyLock, RwLock};\n\n/// Returns the default value for a preference exposed to the style crate.\n/// This is what will be used if the embedder has not set the preference.\n#[macro_export]\nmacro_rules! default_value {\n    (\"dom.select.customizable_select.enabled\") => {\n        false\n    };\n    (\"dom.viewTransitions.cross-document.enabled\") => {\n        false\n    };\n    (\"layout.columns.enabled\") => {\n        false\n    };\n    (\"layout.container-queries.enabled\") => {\n        false\n    };\n    (\"layout.css.anchor-positioning.enabled\") => {\n        false\n    };\n    (\"layout.css.appearance-base.enabled\") => {\n        false\n    };\n    (\"layout.css.at-scope.enabled\") => {\n        false\n    };\n    (\"layout.css.attr.enabled\") => {\n        false\n    };\n    (\"layout.css.basic-shape-shape.enabled\") => {\n        false\n    };\n    (\"layout.css.color-mix-multi-color.enabled\") => {\n        true\n    };\n    (\"layout.css.content.alt-text.enabled\") => {\n        false\n    };\n    (\"layout.css.contrast-color.enabled\") => {\n        true\n    };\n    (\"layout.css.custom-media.enabled\") => {\n        false\n    };\n    (\"layout.css.fit-content-function.enabled\") => {\n        true\n    };\n    (\"layout.css.font-palette.enabled\") => {\n        false\n    };\n    (\"layout.css.font-tech.enabled\") => {\n        false\n    };\n    (\"layout.css.font-variations.enabled\") => {\n        true\n    };\n    (\"layout.css.gradient-color-interpolation-method.enabled\") => {\n        true\n    };\n    (\"layout.css.light-dark.images.enabled\") => {\n        false\n    };\n    (\"layout.css.margin-rules.enabled\") => {\n        false\n    };\n    (\"layout.css.marker.restricted\") => {\n        true\n    };\n    (\"layout.css.motion-path-url.enabled\") => {\n        false\n    };\n    (\"layout.css.outline-offset.snapping\") => {\n        1\n    };\n    (\"layout.css.properties-and-values.enabled\") => {\n        true\n    };\n    (\"layout.css.relative-color-syntax.enabled\") => {\n        true\n    };\n    (\"layout.css.revert-rule.enabled\") => {\n        true\n    };\n    (\"layout.css.scroll-driven-animations.enabled\") => {\n        false\n    };\n    (\"layout.css.scroll-state.enabled\") => {\n        false\n    };\n    (\"layout.css.starting-style-at-rules.enabled\") => {\n        false\n    };\n    (\"layout.css.stretch-size-keyword.enabled\") => {\n        true\n    };\n    (\"layout.css.style-queries.enabled\") => {\n        false\n    };\n    (\"layout.css.stylo-local-work-queue.in-main-thread\") => {\n        32\n    };\n    (\"layout.css.stylo-local-work-queue.in-worker\") => {\n        0\n    };\n    (\"layout.css.stylo-work-unit-size\") => {\n        16\n    };\n    (\"layout.css.system-ui.enabled\") => {\n        true\n    };\n    (\"layout.css.webkit-fill-available.all-size-properties.enabled\") => {\n        true\n    };\n    (\"layout.css.webkit-fill-available.enabled\") => {\n        true\n    };\n    (\"layout.grid.enabled\") => {\n        false\n    };\n    (\"layout.threads\") => {\n        // Negative means auto, 0 disables the thread-pool (main-thread styling),\n        // other numbers override as specified.\n        -1\n    };\n    (\"layout.unimplemented\") => {\n        false\n    };\n    (\"layout.variable_fonts.enabled\") => {\n        false\n    };\n    (\"layout.writing-mode.enabled\") => {\n        false\n    };\n}\n\n/// Returns the value of a preference exposed to the style crate. If the embedder\n/// has not set a value for it, this returns the default value of the preference.\n#[macro_export]\nmacro_rules! pref {\n    ($string:tt) => {\n        $crate::Preference::get($string, $crate::default_value!($string))\n    };\n}\n\n/// Sets a preference to the provided value.\n#[macro_export]\nmacro_rules! set_pref {\n    ($string:tt, $($value:expr)+) => {\n        let value = $($value)+;\n        // This comparison ensures that the provided value belongs to the expected type.\n        let _ = $crate::default_value!($string) == value;\n        $crate::Preference::set($string, value)\n    };\n}\n\nstatic PREFS: LazyLock<Preferences> = LazyLock::new(Preferences::default);\n\n#[derive(Debug, Default)]\nstruct Preferences {\n    bool_prefs: RwLock<HashMap<String, bool>>,\n    i32_prefs: RwLock<HashMap<String, i32>>,\n}\n\nimpl Preferences {\n    pub fn get_bool(&self, key: &str, default: bool) -> bool {\n        let prefs = self.bool_prefs.read().expect(\"RwLock is poisoned\");\n        *prefs.get(key).unwrap_or(&default)\n    }\n\n    pub fn get_i32(&self, key: &str, default: i32) -> i32 {\n        let prefs = self.i32_prefs.read().expect(\"RwLock is poisoned\");\n        *prefs.get(key).unwrap_or(&default)\n    }\n\n    pub fn set_bool(&self, key: &str, value: bool) {\n        let mut prefs = self.bool_prefs.write().expect(\"RwLock is poisoned\");\n\n        // Avoid cloning the key if it exists.\n        if let Some(pref) = prefs.get_mut(key) {\n            *pref = value;\n        } else {\n            prefs.insert(key.to_owned(), value);\n        }\n    }\n\n    pub fn set_i32(&self, key: &str, value: i32) {\n        let mut prefs = self.i32_prefs.write().expect(\"RwLock is poisoned\");\n\n        // Avoid cloning the key if it exists.\n        if let Some(pref) = prefs.get_mut(key) {\n            *pref = value;\n        } else {\n            prefs.insert(key.to_owned(), value);\n        }\n    }\n}\n\npub trait Preference: Sized {\n    /// Gets the value of a preference, falling back to the provided default.\n    /// Prefer using [`pref!()`] instead, since it ensures that the preference\n    /// exists and uses the correct default automatically.\n    fn get(key: &str, default: Self) -> Self;\n\n    /// Sets a preference to the provided value. Prefer using [`set_pref!()`]\n    /// instead, since it ensures that the preference exists and the value\n    /// belongs to the expected type.\n    fn set(key: &str, value: Self);\n}\n\nimpl Preference for bool {\n    fn get(key: &str, default: Self) -> Self {\n        PREFS.get_bool(key, default)\n    }\n    fn set(key: &str, value: Self) {\n        PREFS.set_bool(key, value)\n    }\n}\n\nimpl Preference for i32 {\n    fn get(key: &str, default: Self) -> Self {\n        PREFS.get_i32(key, default)\n    }\n    fn set(key: &str, value: Self) {\n        PREFS.set_i32(key, value)\n    }\n}\n\n#[test]\nfn test() {\n    let prefs = Preferences::default();\n\n    // We get the default value when the pref is not set.\n    assert_eq!(prefs.get_bool(\"foo\", false), false);\n    assert_eq!(prefs.get_bool(\"foo\", true), true);\n    assert_eq!(prefs.get_i32(\"bar\", 0), 0);\n    assert_eq!(prefs.get_i32(\"bar\", 1), 1);\n    assert_eq!(prefs.get_i32(\"bar\", 2), 2);\n\n    // Prefs can be set and retrieved.\n    prefs.set_bool(\"foo\", true);\n    prefs.set_i32(\"bar\", 1);\n    assert_eq!(prefs.get_bool(\"foo\", false), true);\n    assert_eq!(prefs.get_bool(\"foo\", true), true);\n    assert_eq!(prefs.get_i32(\"bar\", 0), 1);\n    assert_eq!(prefs.get_i32(\"bar\", 1), 1);\n    assert_eq!(prefs.get_i32(\"bar\", 2), 1);\n    prefs.set_bool(\"foo\", false);\n    prefs.set_i32(\"bar\", 2);\n    assert_eq!(prefs.get_bool(\"foo\", false), false);\n    assert_eq!(prefs.get_bool(\"foo\", true), false);\n    assert_eq!(prefs.get_i32(\"bar\", 0), 2);\n    assert_eq!(prefs.get_i32(\"bar\", 1), 2);\n    assert_eq!(prefs.get_i32(\"bar\", 2), 2);\n\n    // Each value type currently has an independent namespace.\n    prefs.set_i32(\"foo\", 3);\n    prefs.set_bool(\"bar\", true);\n    assert_eq!(prefs.get_i32(\"foo\", 0), 3);\n    assert_eq!(prefs.get_bool(\"foo\", false), false);\n    assert_eq!(prefs.get_bool(\"bar\", false), true);\n    assert_eq!(prefs.get_i32(\"bar\", 0), 2);\n}\n"
  },
  {
    "path": "sync.sh",
    "content": "#!/bin/sh\n# Usage: sync.sh <path/to/filtered>\nset -eu\n\nroot=$(pwd)\nmkdir -p \"$1\"\ncd -- \"$1\"\nfiltered=$(pwd)\nmkdir -p \"$root/_cache\"\ncd \"$root/_cache\"\nexport PATH=\"$PWD:$PATH\"\n\nstep() {\n    if [ \"${TERM-}\" != '' ]; then\n        tput setaf 12\n    fi\n    >&2 printf '* %s\\n' \"$*\"\n    if [ \"${TERM-}\" != '' ]; then\n        tput sgr0\n    fi\n}\n\nstep Downloading git-filter-repo if needed\nif ! git filter-repo --version 2> /dev/null; then\n    curl -O https://raw.githubusercontent.com/newren/git-filter-repo/v2.38.0/git-filter-repo\n    chmod +x git-filter-repo\n\n    git filter-repo --version\nfi\n\nstep Cloning upstream if needed\nif ! [ -e upstream ]; then\n    git clone --bare --single-branch --branch main --progress https://github.com/mozilla-firefox/firefox.git upstream\nfi\n\nstep Updating upstream\nbranch=$(git -C upstream rev-parse --abbrev-ref HEAD)\ngit -C upstream fetch origin $branch:$branch\n\nstep Filtering upstream\n# Cloning and filtering is much faster than git filter-repo --source --target.\ngit clone --bare upstream -- \"$filtered\"\ngit -C \"$filtered\" filter-repo --force --paths-from-file \"$root/style.paths\"\n"
  },
  {
    "path": "to_shmem/Cargo.toml",
    "content": "[package]\nname = \"to_shmem\"\nversion = \"0.4.0\"\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/servo/stylo\"\nedition = \"2021\"\ndescription = \"Trait to write to a contiguous chunk of shared memory\"\n\n[lib]\nname = \"to_shmem\"\npath = \"lib.rs\"\n\n[features]\nservo = [\"cssparser/serde\", \"cssparser\", \"servo_arc\", \"smallbitvec\", \"smallvec\", \"string_cache\", \"thin-vec\"]\ngecko = [\"thin-vec/gecko-ffi\", \"cssparser\", \"servo_arc\", \"smallbitvec\", \"smallvec\", \"thin-vec\"]\n\ncssparser = [\"dep:cssparser\"]\nservo_arc = [\"dep:servo_arc\"]\nsmallbitvec = [\"dep:smallbitvec\"]\nsmallvec = [\"dep:smallvec\"]\nstring_cache = [\"dep:string_cache\"]\nthin-vec = [\"dep:thin-vec\"]\n\n[dependencies]\ncssparser = { version = \"0.37\", optional = true }\nservo_arc = { workspace = true, optional = true }\nsmallbitvec = { version = \"2.3.0\", optional = true }\nsmallvec = { version = \"1.13\", optional = true }\nstring_cache = { version = \"0.9\", optional = true }\nthin-vec = { version = \"0.2.1\", optional = true }\n"
  },
  {
    "path": "to_shmem/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n//! Trait for cloning data into a shared memory buffer.\n//!\n//! This module contains the SharedMemoryBuilder type and ToShmem trait.\n//!\n//! We put them here (and not in style_traits) so that we can derive ToShmem\n//! from the selectors and style crates.\n\n#![crate_name = \"to_shmem\"]\n#![crate_type = \"rlib\"]\n\nuse std::alloc::Layout;\nuse std::collections::HashSet;\nuse std::ffi::CString;\nuse std::isize;\nuse std::marker::PhantomData;\nuse std::mem::{self, ManuallyDrop};\nuse std::num::Wrapping;\nuse std::ops::Range;\nuse std::os::raw::c_char;\nuse std::ptr::{self, NonNull};\nuse std::slice;\nuse std::str;\n\n/// Result type for ToShmem::to_shmem.\n///\n/// The String is an error message describing why the call failed.\npub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;\n\n// Various pointer arithmetic functions in this file can be replaced with\n// functions on `Layout` once they have stabilized:\n//\n// https://github.com/rust-lang/rust/issues/55724\n\n/// A builder object that transforms and copies values into a fixed size buffer.\npub struct SharedMemoryBuilder {\n    /// The buffer into which values will be copied.\n    buffer: *mut u8,\n    /// The size of the buffer.\n    capacity: usize,\n    /// The current position in the buffer, where the next value will be written\n    /// at.\n    index: usize,\n    /// Pointers to every shareable value that we store in the shared memory\n    /// buffer.  We use this to assert against encountering the same value\n    /// twice, e.g. through another Arc reference, so that we don't\n    /// inadvertently store duplicate copies of values.\n    #[cfg(all(debug_assertions, feature = \"servo_arc\"))]\n    shared_values: HashSet<*const std::os::raw::c_void>,\n}\n\n/// Amount of padding needed after `size` bytes to ensure that the following\n/// address will satisfy `align`.\nfn padding_needed_for(size: usize, align: usize) -> usize {\n    padded_size(size, align).wrapping_sub(size)\n}\n\n/// Rounds up `size` so that the following address will satisfy `align`.\nfn padded_size(size: usize, align: usize) -> usize {\n    size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)\n}\n\nimpl SharedMemoryBuilder {\n    /// Creates a new SharedMemoryBuilder using the specified buffer.\n    pub unsafe fn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {\n        SharedMemoryBuilder {\n            buffer,\n            capacity,\n            index: 0,\n            #[cfg(all(debug_assertions, feature = \"servo_arc\"))]\n            shared_values: HashSet::new(),\n        }\n    }\n\n    /// Returns the number of bytes currently used in the buffer.\n    #[inline]\n    pub fn len(&self) -> usize {\n        self.index\n    }\n\n    /// Writes a value into the shared memory buffer and returns a pointer to\n    /// it in the buffer.\n    ///\n    /// The value is cloned and converted into a form suitable for placing into\n    /// a shared memory buffer by calling ToShmem::to_shmem on it.\n    ///\n    /// Panics if there is insufficient space in the buffer.\n    pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {\n        // Reserve space for the value.\n        let dest: *mut T = self.alloc_value();\n\n        // Make a clone of the value with all of its heap allocations\n        // placed in the shared memory buffer.\n        let value = value.to_shmem(self)?;\n\n        unsafe {\n            // Copy the value into the buffer.\n            ptr::write(dest, ManuallyDrop::into_inner(value));\n        }\n\n        // Return a pointer to the shared value.\n        Ok(dest)\n    }\n\n    /// Reserves space in the shared memory buffer to fit a value of type T,\n    /// and returns a pointer to that reserved space.\n    ///\n    /// Panics if there is insufficient space in the buffer.\n    pub fn alloc_value<T>(&mut self) -> *mut T {\n        self.alloc(Layout::new::<T>())\n    }\n\n    /// Reserves space in the shared memory buffer to fit an array of values of\n    /// type T, and returns a pointer to that reserved space.\n    ///\n    /// Panics if there is insufficient space in the buffer.\n    pub fn alloc_array<T>(&mut self, len: usize) -> *mut T {\n        if len == 0 {\n            return NonNull::dangling().as_ptr();\n        }\n\n        let size = mem::size_of::<T>();\n        let align = mem::align_of::<T>();\n\n        self.alloc(Layout::from_size_align(padded_size(size, align) * len, align).unwrap())\n    }\n\n    /// Reserves space in the shared memory buffer that conforms to the\n    /// specified layout, and returns a pointer to that reserved space.\n    ///\n    /// Panics if there is insufficient space in the buffer.\n    pub fn alloc<T>(&mut self, layout: Layout) -> *mut T {\n        // Amount of padding to align the value.\n        //\n        // The addition can't overflow, since self.index <= self.capacity, and\n        // for us to have successfully allocated the buffer, `buffer + capacity`\n        // can't overflow.\n        let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());\n\n        // Reserve space for the padding.\n        let start = self.index.checked_add(padding).unwrap();\n        assert!(start <= std::isize::MAX as usize); // for the cast below\n\n        // Reserve space for the value.\n        let end = start.checked_add(layout.size()).unwrap();\n        assert!(end <= self.capacity);\n\n        self.index = end;\n        unsafe { self.buffer.add(start) as *mut T }\n    }\n}\n\n/// A type that can be copied into a SharedMemoryBuilder.\npub trait ToShmem: Sized {\n    /// Clones this value into a form suitable for writing into a\n    /// SharedMemoryBuilder.\n    ///\n    /// If this value owns any heap allocations, they should be written into\n    /// `builder` so that the return value of this function can point to the\n    /// copy in the shared memory buffer.\n    ///\n    /// The return type is wrapped in ManuallyDrop to make it harder to\n    /// accidentally invoke the destructor of the value that is produced.\n    ///\n    /// Returns a Result so that we can gracefully recover from unexpected\n    /// content.\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;\n}\n\n#[macro_export]\nmacro_rules! impl_trivial_to_shmem {\n    ($($ty:ty),*) => {\n        $(\n            impl $crate::ToShmem for $ty {\n                fn to_shmem(\n                    &self,\n                    _builder: &mut $crate::SharedMemoryBuilder,\n                ) -> $crate::Result<Self> {\n                    $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))\n                }\n            }\n        )*\n    };\n}\n\nimpl_trivial_to_shmem!(\n    (),\n    bool,\n    f32,\n    f64,\n    i8,\n    i16,\n    i32,\n    i64,\n    u8,\n    u16,\n    u32,\n    u64,\n    isize,\n    usize,\n    std::num::NonZeroUsize\n);\n\nimpl<T> ToShmem for PhantomData<T> {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        Ok(ManuallyDrop::new(*self))\n    }\n}\n\nimpl<T: ToShmem> ToShmem for Range<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        Ok(ManuallyDrop::new(Range {\n            start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),\n            end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),\n        }))\n    }\n}\n\nimpl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        Ok(ManuallyDrop::new((\n            ManuallyDrop::into_inner(self.0.to_shmem(builder)?),\n            ManuallyDrop::into_inner(self.1.to_shmem(builder)?),\n        )))\n    }\n}\n\nimpl<T: ToShmem> ToShmem for Wrapping<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(\n            self.0.to_shmem(builder)?,\n        ))))\n    }\n}\n\nimpl<T: ToShmem> ToShmem for Box<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        // Reserve space for the boxed value.\n        let dest: *mut T = builder.alloc_value();\n\n        // Make a clone of the boxed value with all of its heap allocations\n        // placed in the shared memory buffer.\n        let value = (**self).to_shmem(builder)?;\n\n        unsafe {\n            // Copy the value into the buffer.\n            ptr::write(dest, ManuallyDrop::into_inner(value));\n\n            Ok(ManuallyDrop::new(Box::from_raw(dest)))\n        }\n    }\n}\n\n/// Converts all the items in `src` into shared memory form, writes them into\n/// the specified buffer, and returns a pointer to the slice.\nunsafe fn to_shmem_slice_ptr<'a, T, I>(\n    src: I,\n    dest: *mut T,\n    builder: &mut SharedMemoryBuilder,\n) -> std::result::Result<*mut [T], String>\nwhere\n    T: 'a + ToShmem,\n    I: ExactSizeIterator<Item = &'a T>,\n{\n    let dest = slice::from_raw_parts_mut(dest, src.len());\n\n    // Make a clone of each element from the iterator with its own heap\n    // allocations placed in the buffer, and copy that clone into the buffer.\n    for (src, dest) in src.zip(dest.iter_mut()) {\n        ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));\n    }\n\n    Ok(dest)\n}\n\n/// Writes all the items in `src` into a slice in the shared memory buffer and\n/// returns a pointer to the slice.\npub unsafe fn to_shmem_slice<'a, T, I>(\n    src: I,\n    builder: &mut SharedMemoryBuilder,\n) -> std::result::Result<*mut [T], String>\nwhere\n    T: 'a + ToShmem,\n    I: ExactSizeIterator<Item = &'a T>,\n{\n    let dest = builder.alloc_array(src.len());\n    to_shmem_slice_ptr(src, dest, builder)\n}\n\nimpl<T: ToShmem> ToShmem for Box<[T]> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        unsafe {\n            let dest = to_shmem_slice(self.iter(), builder)?;\n            Ok(ManuallyDrop::new(Box::from_raw(dest)))\n        }\n    }\n}\n\nimpl ToShmem for Box<str> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        // Reserve space for the string bytes.\n        let dest: *mut u8 = builder.alloc_array(self.len());\n\n        unsafe {\n            // Copy the value into the buffer.\n            ptr::copy(self.as_ptr(), dest, self.len());\n\n            Ok(ManuallyDrop::new(Box::from_raw(\n                str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),\n            )))\n        }\n    }\n}\n\nimpl ToShmem for String {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        // Reserve space for the string bytes.\n        let dest: *mut u8 = builder.alloc_array(self.len());\n\n        unsafe {\n            // Copy the value into the buffer.\n            ptr::copy(self.as_ptr(), dest, self.len());\n\n            Ok(ManuallyDrop::new(String::from_raw_parts(\n                dest,\n                self.len(),\n                self.len(),\n            )))\n        }\n    }\n}\n\nimpl ToShmem for CString {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        let len = self.as_bytes_with_nul().len();\n\n        // Reserve space for the string bytes.\n        let dest: *mut c_char = builder.alloc_array(len);\n\n        unsafe {\n            // Copy the value into the buffer.\n            ptr::copy(self.as_ptr(), dest, len);\n\n            Ok(ManuallyDrop::new(CString::from_raw(dest)))\n        }\n    }\n}\n\nimpl<T: ToShmem> ToShmem for Vec<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        unsafe {\n            let dest = to_shmem_slice(self.iter(), builder)? as *mut T;\n            let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());\n            Ok(ManuallyDrop::new(dest_vec))\n        }\n    }\n}\n\nimpl<T: ToShmem, S> ToShmem for HashSet<T, S>\nwhere\n    Self: Default,\n{\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        if !self.is_empty() {\n            return Err(format!(\n                \"ToShmem failed for HashSet: We only support empty sets \\\n                 (we don't expect custom properties in UA sheets, they're observable by content)\",\n            ));\n        }\n        Ok(ManuallyDrop::new(Self::default()))\n    }\n}\n\nimpl<T: ToShmem> ToShmem for Option<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        let v = match self {\n            Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),\n            None => None,\n        };\n\n        Ok(ManuallyDrop::new(v))\n    }\n}\n\n#[cfg(feature = \"smallvec\")]\nimpl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        let dest_vec = unsafe {\n            if self.spilled() {\n                // Place the items in a separate allocation in the shared memory\n                // buffer.\n                let dest = to_shmem_slice(self.iter(), builder)? as *mut T;\n                Self::from_raw_parts(dest, self.len(), self.len())\n            } else {\n                // Place the items inline.\n                let mut s = Self::new();\n                to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;\n                s.set_len(self.len());\n                s\n            }\n        };\n\n        Ok(ManuallyDrop::new(dest_vec))\n    }\n}\n\n#[cfg(feature = \"servo_arc\")]\nimpl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B>\nwhere\n    servo_arc::Arc<A>: ToShmem,\n    servo_arc::Arc<B>: ToShmem,\n{\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        use servo_arc::ArcUnionBorrow;\n\n        Ok(ManuallyDrop::new(match self.borrow() {\n            ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(\n                first.with_arc(|a| a.to_shmem(builder))?,\n            )),\n            ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(\n                second.with_arc(|a| a.to_shmem(builder))?,\n            )),\n        }))\n    }\n}\n#[cfg(feature = \"servo_arc\")]\nimpl<T: ToShmem> ToShmem for servo_arc::Arc<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        // Assert that we don't encounter any shared references to values we\n        // don't expect.\n        #[cfg(debug_assertions)]\n        assert!(\n            !builder.shared_values.contains(&self.heap_ptr()),\n            \"ToShmem failed for Arc<{}>: encountered a value with multiple \\\n            references.\",\n            std::any::type_name::<T>()\n        );\n\n        // Make a clone of the Arc-owned value with all of its heap allocations\n        // placed in the shared memory buffer.\n        let value = (**self).to_shmem(builder)?;\n\n        // Create a new Arc with the shared value and have it place its\n        // ArcInner in the shared memory buffer.\n        unsafe {\n            let static_arc = Self::new_static(\n                |layout| builder.alloc(layout),\n                ManuallyDrop::into_inner(value),\n            );\n\n            #[cfg(debug_assertions)]\n            builder.shared_values.insert(self.heap_ptr());\n\n            Ok(ManuallyDrop::new(static_arc))\n        }\n    }\n}\n#[cfg(feature = \"servo_arc\")]\nimpl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        // We don't currently have any shared ThinArc values in stylesheets,\n        // so don't support them for now.\n        #[cfg(debug_assertions)]\n        assert!(\n            !builder.shared_values.contains(&self.heap_ptr()),\n            \"ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \\\n             is not currently supported\",\n        );\n\n        // Make a clone of the Arc-owned header and slice values with all of\n        // their heap allocations placed in the shared memory buffer.\n        let header = self.header.to_shmem(builder)?;\n        let mut values = Vec::with_capacity(self.len());\n        for v in self.slice().iter() {\n            values.push(v.to_shmem(builder)?);\n        }\n\n        // Create a new ThinArc with the shared value and have it place\n        // its ArcInner in the shared memory buffer.\n        let len = values.len();\n        let static_arc = Self::from_header_and_iter_alloc(\n            |layout| builder.alloc(layout),\n            ManuallyDrop::into_inner(header),\n            values.into_iter().map(ManuallyDrop::into_inner),\n            len,\n            /* is_static = */ true,\n        );\n\n        #[cfg(debug_assertions)]\n        builder.shared_values.insert(self.heap_ptr());\n\n        Ok(ManuallyDrop::new(static_arc))\n    }\n}\n\n#[cfg(feature = \"thin-vec\")]\nimpl<T: ToShmem> ToShmem for thin_vec::ThinVec<T> {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        assert_eq!(mem::size_of::<Self>(), mem::size_of::<*const ()>());\n\n        // NOTE: We need to do the work of allocating the header in shared memory even if the\n        // length is zero, because an empty ThinVec, even though it doesn't allocate, references\n        // static memory which will not be mapped to other processes, see bug 1841011.\n        let len = self.len();\n\n        // nsTArrayHeader size.\n        // FIXME: Would be nice not to hard-code this, but in practice thin-vec crate also relies\n        // on this.\n        let header_size = 2 * mem::size_of::<u32>();\n        let header_align = mem::size_of::<u32>();\n\n        let item_size = mem::size_of::<T>();\n        let item_align = mem::align_of::<T>();\n\n        // We don't need to support underalignment for now, this could be supported if needed.\n        assert!(item_align >= header_align);\n\n        // This is explicitly unsupported by ThinVec, see:\n        // https://searchfox.org/mozilla-central/rev/ad732108b073742d7324f998c085f459674a6846/third_party/rust/thin-vec/src/lib.rs#375-386\n        assert!(item_align <= header_size);\n        let header_padding = 0;\n\n        let layout = Layout::from_size_align(\n            header_size + header_padding + padded_size(item_size, item_align) * len,\n            item_align,\n        )\n        .unwrap();\n\n        let shmem_header_ptr = builder.alloc::<u8>(layout);\n        let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };\n\n        let data_ptr = self.as_ptr() as *const T as *const u8;\n        let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };\n\n        unsafe {\n            // Copy the header. Note this might copy a wrong capacity, but it doesn't matter,\n            // because shared memory ptrs are immutable anyways, and we can't relocate.\n            ptr::copy(header_ptr, shmem_header_ptr, header_size);\n            // ToShmem + copy the contents into the shared buffer.\n            to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?;\n            // Return the new ThinVec, which is just a pointer to the shared memory buffer.\n            let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);\n\n            // Sanity-check that the ptr and length match.\n            debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);\n            debug_assert_eq!(shmem_thinvec.len(), len);\n\n            Ok(ManuallyDrop::new(shmem_thinvec))\n        }\n    }\n}\n\n#[cfg(feature = \"smallbitvec\")]\nimpl ToShmem for smallbitvec::SmallBitVec {\n    fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        use smallbitvec::InternalStorage;\n\n        let storage = match self.clone().into_storage() {\n            InternalStorage::Spilled(vs) => {\n                // Reserve space for the boxed slice values.\n                let len = vs.len();\n                let dest: *mut usize = builder.alloc_array(len);\n\n                unsafe {\n                    // Copy the value into the buffer.\n                    let src = vs.as_ptr() as *const usize;\n                    ptr::copy(src, dest, len);\n\n                    let dest_slice =\n                        Box::from_raw(slice::from_raw_parts_mut(dest, len) as *mut [usize]);\n                    InternalStorage::Spilled(dest_slice)\n                }\n            },\n            InternalStorage::Inline(x) => InternalStorage::Inline(x),\n        };\n        Ok(ManuallyDrop::new(unsafe { Self::from_storage(storage) }))\n    }\n}\n\n#[cfg(feature = \"string_cache\")]\nimpl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {\n    fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {\n        // NOTE(emilio): In practice, this can be implemented trivially if\n        // string_cache could expose the implementation detail of static atoms\n        // being an index into the static table (and panicking in the\n        // non-static, non-inline cases).\n        unimplemented!(\n            \"If servo wants to share stylesheets across processes, \\\n             then ToShmem for Atom needs to be implemented\"\n        )\n    }\n}\n\n#[cfg(feature = \"cssparser\")]\nimpl_trivial_to_shmem!(\n    cssparser::SourceLocation,\n    cssparser::SourcePosition,\n    cssparser::TokenSerializationType\n);\n#[cfg(feature = \"cssparser\")]\nimpl ToShmem for cssparser::UnicodeRange {\n    fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {\n        Ok(ManuallyDrop::new(Self {\n            start: self.start,\n            end: self.end,\n        }))\n    }\n}\n"
  },
  {
    "path": "to_shmem_derive/Cargo.toml",
    "content": "[package]\nname = \"to_shmem_derive\"\nversion = \"0.1.0\"\nauthors = [\"The Servo Project Developers\"]\nlicense = \"MPL-2.0\"\nrepository = \"https://github.com/servo/stylo\"\nedition = \"2021\"\ndescription = \"Allows deriving the to_shmem trait.\"\n\n[lib]\npath = \"lib.rs\"\nproc-macro = true\n\n[dependencies]\ndarling = { version = \"0.20\", default-features = false }\nproc-macro2 = \"1\"\nquote = \"1\"\nsyn = { version = \"2\", default-features = false, features = [\"derive\", \"parsing\"] }\nsynstructure = \"0.13\"\n"
  },
  {
    "path": "to_shmem_derive/lib.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\n#![recursion_limit = \"128\"]\n\nuse proc_macro::TokenStream;\n\nmod to_shmem;\nmod util;\n\n#[proc_macro_derive(ToShmem, attributes(shmem))]\npub fn derive_to_shmem(stream: TokenStream) -> TokenStream {\n    let input = syn::parse(stream).unwrap();\n    to_shmem::derive(input).into()\n}\n"
  },
  {
    "path": "to_shmem_derive/to_shmem.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse crate::util::{add_predicate, fmap2_match, parse_field_attrs, parse_input_attrs};\nuse darling::{FromDeriveInput, FromField};\nuse proc_macro2::TokenStream;\nuse quote::quote;\nuse syn::{self, parse_quote};\nuse synstructure::{BindStyle, Structure};\n\npub fn derive(mut input: syn::DeriveInput) -> TokenStream {\n    let mut where_clause = input.generics.where_clause.take();\n    let attrs = parse_input_attrs::<ShmemInputAttrs>(&input);\n    if !attrs.no_bounds {\n        for param in input.generics.type_params() {\n            add_predicate(&mut where_clause, parse_quote!(#param: ::to_shmem::ToShmem));\n        }\n    }\n    for variant in Structure::new(&input).variants() {\n        for binding in variant.bindings() {\n            let attrs = parse_field_attrs::<ShmemFieldAttrs>(&binding.ast());\n            if attrs.field_bound {\n                let ty = &binding.ast().ty;\n                add_predicate(&mut where_clause, parse_quote!(#ty: ::to_shmem::ToShmem))\n            }\n        }\n    }\n\n    input.generics.where_clause = where_clause;\n\n    // Do all of the `to_shmem()?` calls before the `ManuallyDrop::into_inner()`\n    // calls, so that we don't drop a value in the shared memory buffer if one\n    // of the `to_shmem`s fails.\n    let match_body = fmap2_match(\n        &input,\n        BindStyle::Ref,\n        |binding| {\n            quote! {\n                ::to_shmem::ToShmem::to_shmem(#binding, builder)?\n            }\n        },\n        |binding| {\n            Some(quote! {\n                ::std::mem::ManuallyDrop::into_inner(#binding)\n            })\n        },\n    );\n\n    let name = &input.ident;\n    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();\n\n    quote! {\n        impl #impl_generics ::to_shmem::ToShmem for #name #ty_generics #where_clause {\n            #[allow(unused_variables, unreachable_code)]\n            fn to_shmem(\n                &self,\n                builder: &mut ::to_shmem::SharedMemoryBuilder,\n            ) -> ::to_shmem::Result<Self> {\n                Ok(::std::mem::ManuallyDrop::new(\n                    match *self {\n                        #match_body\n                    }\n                ))\n            }\n        }\n    }\n}\n\n#[derive(Default, FromDeriveInput)]\n#[darling(attributes(shmem), default)]\npub struct ShmemInputAttrs {\n    pub no_bounds: bool,\n}\n\n#[derive(Default, FromField)]\n#[darling(attributes(shmem), default)]\npub struct ShmemFieldAttrs {\n    pub field_bound: bool,\n}\n"
  },
  {
    "path": "to_shmem_derive/util.rs",
    "content": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/. */\n\nuse darling::{FromDeriveInput, FromField};\nuse proc_macro2::{Span, TokenStream};\nuse quote::{quote, TokenStreamExt};\nuse syn::{self, parse_quote, DeriveInput, Field, Ident, WherePredicate};\nuse synstructure::{self, BindStyle, BindingInfo, VariantInfo};\n\npub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A\nwhere\n    A: FromDeriveInput,\n{\n    match A::from_derive_input(input) {\n        Ok(attrs) => attrs,\n        Err(e) => panic!(\"failed to parse input attributes: {}\", e),\n    }\n}\n\npub(crate) fn parse_field_attrs<A>(field: &Field) -> A\nwhere\n    A: FromField,\n{\n    match A::from_field(field) {\n        Ok(attrs) => attrs,\n        Err(e) => panic!(\"failed to parse field attributes: {}\", e),\n    }\n}\n\npub(crate) fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {\n    where_clause\n        .get_or_insert(parse_quote!(where))\n        .predicates\n        .push(pred);\n}\n\npub(crate) fn fmap2_match<F, G>(\n    input: &DeriveInput,\n    bind_style: BindStyle,\n    mut f: F,\n    mut g: G,\n) -> TokenStream\nwhere\n    F: FnMut(&BindingInfo) -> TokenStream,\n    G: FnMut(&BindingInfo) -> Option<TokenStream>,\n{\n    let mut s = synstructure::Structure::new(input);\n    s.variants_mut().iter_mut().for_each(|v| {\n        v.bind_with(|_| bind_style);\n    });\n    s.each_variant(|variant| {\n        let (mapped, mapped_fields) = value(variant, \"mapped\");\n        let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());\n        let mut computations = quote!();\n        computations.append_all(fields_pairs.map(|(field, mapped_field)| {\n            let expr = f(field);\n            quote! { let #mapped_field = #expr; }\n        }));\n        computations.append_all(\n            mapped_fields\n                .iter()\n                .map(|mapped_field| match g(mapped_field) {\n                    Some(expr) => quote! { let #mapped_field = #expr; },\n                    None => quote!(),\n                }),\n        );\n        computations.append_all(mapped);\n        Some(computations)\n    })\n}\n\nfn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {\n    let mut v = variant.clone();\n    v.bindings_mut().iter_mut().for_each(|b| {\n        b.binding = Ident::new(&format!(\"{}_{}\", b.binding, prefix), Span::call_site())\n    });\n    v.bind_with(|_| BindStyle::Move);\n    (v.pat(), v.bindings().to_vec())\n}\n"
  }
]