diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..fa6c67068c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + merge_group: + types: [checks_requested] + + +jobs: + linux-debug: + name: Linux (Debug) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --features servo + env: + RUST_BACKTRACE: 1 + + linux-release: + name: Linux (Release) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --release --features servo + env: + RUST_BACKTRACE: 1 + + macos-debug: + name: macOS (Debug) + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --features servo + env: + RUST_BACKTRACE: 1 + + windows-debug: + name: Windows (Debug) + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Run Tests + run: cargo build --features servo + env: + RUST_BACKTRACE: 1 + + build-result: + name: Result + runs-on: ubuntu-latest + if: ${{ always() }} + needs: + - linux-debug + - linux-release + steps: + - name: Success + if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} + run: exit 0 + - name: Failure + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} + run: exit 1 + diff --git a/.github/workflows/mirror-to-release-branch.yml b/.github/workflows/mirror-to-release-branch.yml new file mode 100644 index 0000000000..c8593195da --- /dev/null +++ b/.github/workflows/mirror-to-release-branch.yml @@ -0,0 +1,26 @@ +name: 🪞 Mirror `main` +on: + push: + branches: + - main + +jobs: + mirror: + name: Mirror + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Get branch name + id: branch-name + run: | + first_commit=$(git log --pretty=\%H --grep='Servo initial downstream commit') + upstream_base="$first_commit~" + echo BRANCH_NAME=$(git log -n1 --pretty='%as' $upstream_base) >> $GITHUB_OUTPUT + - uses: google/mirror-branch-action@v1.0 + name: Mirror to ${{ steps.branch-name.outputs.BRANCH_NAME }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + source: main + dest: ${{ steps.branch-name.outputs.BRANCH_NAME }} diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml new file mode 100644 index 0000000000..adf329ffa6 --- /dev/null +++ b/.github/workflows/sync-upstream.yml @@ -0,0 +1,23 @@ +name: Sync upstream with mozilla-central + +on: + schedule: + - cron: '0 13 * * *' + workflow_dispatch: + +jobs: + sync: + name: Sync + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + - uses: actions/cache@v3 + with: + path: _cache/upstream + key: upstream + - run: | + ./sync.sh _filtered + git fetch -f --progress ./_filtered main:upstream + git push -fu --progress origin upstream diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..fc3c2f9b3c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/_cache/ +/_filtered/ +/target/ +/style/properties/__pycache__/ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..93acb338d2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,36 @@ +[workspace] +resolver = "2" +members = [ + "stylo_atoms", + "stylo_dom", + "malloc_size_of", + "rustfmt.toml", + "selectors", + "servo_arc", + "style", + "style_derive", + "stylo_static_prefs", + "style_traits", + "to_shmem", + "to_shmem_derive", +] +default-members = ["style"] + +[workspace.package] +version = "0.12.0" + +[workspace.dependencies] +# in-repo dependencies (separately versioned) +servo_arc = { version = "0.4.3", path = "./servo_arc" } +selectors = { version = "0.35.0", path = "./selectors" } +to_shmem = { version = "0.3.0", path = "./to_shmem", features = ["servo"] } +to_shmem_derive = { version = "0.1.0", path = "./to_shmem_derive" } + +# in-repo dependencies (main version) +malloc_size_of = { version = "0.12.0", path = "./malloc_size_of", package = "stylo_malloc_size_of", features = ["servo"] } +static_prefs = { version = "0.12.0", path = "./stylo_static_prefs", package = "stylo_static_prefs" } +stylo_atoms = { version = "0.12.0", path = "./stylo_atoms" } +dom = { version = "0.12.0", path = "./stylo_dom", package = "stylo_dom" } +style_traits = { version = "0.12.0", path = "./style_traits", features = ["servo"], package = "stylo_traits"} +style_derive = { version = "0.12.0", path = "./style_derive", package = "stylo_derive"} +stylo = { version = "0.12.0", path = "./style" } diff --git a/README.md b/README.md new file mode 100644 index 0000000000..bfc3eb5b28 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +Stylo +===== + +**High-Performance CSS Style Engine** + +[![Build Status](https://github.com/servo/stylo/actions/workflows/main.yml/badge.svg)](https://github.com/servo/stylo/actions) +[![Crates.io](https://img.shields.io/crates/v/stylo.svg)](https://crates.io/crates/stylo) +[![Docs](https://docs.rs/stylo/badge.svg)](https://docs.rs/stylo) +![Crates.io License](https://img.shields.io/crates/l/stylo) + +Stylo 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. + +Coordination of Stylo development happens: + +- Here in Github Issues +- In the [#stylo](https://servo.zulipchat.com/#narrow/channel/417109-stylo) channel of the [Servo Zulip](https://servo.zulipchat.com/) +- In the [#layout](https://chat.mozilla.org/#/room/#layout:mozilla.org) room of the Mozilla Matrix instance (matrix.mozilla.org) + +## High-Level Documentation + +- 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. +- There is a [chapter](https://book.servo.org/architecture/style.html) in the Servo Book (although it is a little out of date). + +## Branches + +The branches are as follows: + +- [**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. +- [**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**. + +> [!WARNING] +> 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`). + +More information on the syncing process is available in [SYNCING.md](SYNCING.md) + +## Crates + +A guide to the crates contained within this repo + +### Stylo Crates + +These crates are largely implementation details of Stylo, although you may need to use some of them directly if you use Stylo. + +| Directory | Crate | Notes | +| --- | --- | --- | +| 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 | +| 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. | +| 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) | +| 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 | +| 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) | +| 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 | + +### Standalone Crates + +These crates form part of Stylo but are also be useful standalone. + +| Directory | Crate | Notes | +| --- | --- | --- | +| selectors | [![Crates.io](https://img.shields.io/crates/v/selectors.svg)](https://crates.io/crates/selectors) | CSS Selector matching | +| servo_arc | [![Crates.io](https://img.shields.io/crates/v/servo_arc.svg)](https://crates.io/crates/servo_arc) | A variant on `std::Arc` | + +You may also be interested in the `cssparser` crate which lives in the [servo/rust-cssparser](https://github.com/servo/rust-cssparser) repo. + +### Support Crates + +Low-level crates which could technically be used standalone but are unlikely to be generally useful in practice. + +| Directory | Crate | Notes | +| --- | --- | --- | +| 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 | +| 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. | +| 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 | + +## Building Servo Against a Local Copy of Stylo + +Assuming your local `servo` and `stylo` directories are siblings, you can build `servo` against `stylo` by adding the following to `servo/Cargo.toml`: + +```toml +[patch."https://github.com/servo/stylo"] +selectors = { path = "../stylo/selectors" } +servo_arc = { path = "../stylo/servo_arc" } +stylo = { path = "../stylo/style" } +stylo_atoms = { path = "../stylo/stylo_atoms" } +stylo_dom = { path = "../stylo/stylo_dom" } +stylo_malloc_size_of = { path = "../stylo/malloc_size_of" } +stylo_static_prefs = { path = "../stylo/stylo_static_prefs" } +stylo_traits = { path = "../stylo/style_traits" } +``` + +## Releases + +Releases 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: + +- selectors +- stylo_static_prefs +- stylo_atoms +- stylo_malloc_size_of +- stylo_dom +- stylo_derive +- stylo_traits +- stylo + +## License + +Stylo is licensed under MPL 2.0 diff --git a/SYNCING.md b/SYNCING.md new file mode 100644 index 0000000000..72a0a53d1d --- /dev/null +++ b/SYNCING.md @@ -0,0 +1,63 @@ +# Syncing + +This file documents the process of syncing this repository with the upstream copy of Stylo in mozilla-central. + +## Syncing `upstream` with mozilla-central + +Start by generating a filtered copy of mozilla-central. This will cache the raw mozilla-central in `_cache/upstream`, storing the result in `_filtered`: + +```sh +$ ./sync.sh _filtered +``` + +If `_filtered` already exists, you will need to delete it and try again: + +```sh +$ rm -Rf _filtered +``` + +Now overwrite our `upstream` with those commits and push: + +```sh +$ git fetch -f --progress ./_filtered main:upstream +$ git push -fu --progress origin upstream +``` + +## Rebasing `main` onto `upstream` + +Start by fetching `upstream` into your local repo: + +```sh +$ git fetch -f origin upstream:upstream +``` + +In general, the filtering process is deterministic, yielding the same commit hashes each time, so we can rebase normally: + +```sh +$ git rebase upstream +``` + +But 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 `~`): + +```sh +$ git log --pretty=\%H --grep='Servo initial downstream commit' +e62d7f0090941496e392e1dc91df103a38e3f488 + +$ git rebase --onto upstream e62d7f0090941496e392e1dc91df103a38e3f488~ +Successfully rebased and updated refs/heads/main. +``` + +`start-rebase.sh` takes care of this automatically, but you should still use `git rebase` for subsequent steps like `--continue` and `--abort`: + +```sh +$ ./start-rebase.sh upstream +$ ./start-rebase.sh upstream -i # interactive +$ git rebase --continue # not ./start-rebase.sh --continue +$ git rebase --abort # not ./start-rebase.sh --abort +``` + +Or if we aren’t ready to rebase onto the tip of upstream: + +```sh +$ ./start-rebase.sh upstream~10 -i +``` diff --git a/commit-from-merge.sh b/commit-from-merge.sh new file mode 100755 index 0000000000..94aa606f02 --- /dev/null +++ b/commit-from-merge.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# Usage: commit-from-merge.sh [extra git-commit(1) arguments ...] +# Given a merge commit made by bors, runs git-commit(1) with your local changes +# while borrowing the author name/email from the right-hand parent of the merge, +# and the author date from the committer date of the merge. +set -eu + +lookup_repo=$1; shift +merge_commit=$1; shift +author_name_email=$(git -C "$lookup_repo" log -n1 --pretty='%aN <%aE>' "$merge_commit"\^2) +committer_date=$(git -C "$lookup_repo" log -n1 --pretty='%cd' "$merge_commit") + +set -- git commit --author="$author_name_email" --date="$committer_date" "$@" +echo "$@" +"$@" diff --git a/commit-from-squashed.sh b/commit-from-squashed.sh new file mode 100755 index 0000000000..004e0f7840 --- /dev/null +++ b/commit-from-squashed.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Usage: commit-from-squashed.sh [extra git-commit(1) arguments ...] +# Given a squashed commit made by the GitHub merge queue, runs git-commit(1) with your local changes +# while borrowing our author name/email from that commit, our author date from its committer date, +# and our commit message from that commit. +set -eu + +squashed_commit=$1; shift +committer_date=$(git log -n1 --pretty='%cd' "$squashed_commit") + +# -c is equivalent to --author=$(...'%aN <%aE>') -m $(...'%B'), but allows editing +set -- git commit -c "$squashed_commit" --date="$committer_date" "$@" +echo "$@" +"$@" diff --git a/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index 4859a962c0..66288a8204 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "malloc_size_of" -version = "0.0.1" +name = "stylo_malloc_size_of" +version.workspace = true authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" @@ -18,10 +18,10 @@ servo = ["string_cache"] app_units = "0.7" cssparser = "0.36" euclid = "0.22" -selectors = { path = "../selectors" } -servo_arc = { path = "../servo_arc" } +selectors = { workspace = true } +servo_arc = { workspace = true } smallbitvec = "2.3.0" -smallvec = "1.0" -string_cache = { version = "0.8", optional = true } -thin-vec = { version = "0.2.1" } +smallvec = "1.13" +string_cache = { version = "0.9", optional = true } +thin-vec = { version = "0.2.13" } void = "1.0.2" diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml index de502baffd..d566b36fe8 100644 --- a/selectors/Cargo.toml +++ b/selectors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "selectors" -version = "0.26.0" +version = "0.35.0" authors = ["The Servo Project Developers"] documentation = "https://docs.rs/selectors/" description = "CSS Selectors matching for Rust" @@ -27,10 +27,10 @@ rustc-hash = "2.1.1" log = "0.4" phf = "0.13" precomputed-hash = "0.1" -servo_arc = { version = "0.4", path = "../servo_arc" } +servo_arc = { workspace = true } smallvec = "1.0" -to_shmem = { version = "0.1", path = "../to_shmem", features = ["servo_arc"], optional = true } -to_shmem_derive = { version = "0.1", path = "../to_shmem_derive", optional = true } +to_shmem = { workspace = true, optional = true } +to_shmem_derive = { workspace = true, optional = true } new_debug_unreachable = "1" [build-dependencies] diff --git a/servo_arc/Cargo.toml b/servo_arc/Cargo.toml index 8b0976b75d..f5ae244a50 100644 --- a/servo_arc/Cargo.toml +++ b/servo_arc/Cargo.toml @@ -1,17 +1,19 @@ [package] name = "servo_arc" -version = "0.4.0" +version = "0.4.3" authors = ["The Servo Project Developers"] license = "MIT OR Apache-2.0" repository = "https://github.com/servo/stylo" description = "A fork of std::sync::Arc with some extra functionality and without weak references" edition = "2021" +readme = "../README.md" [lib] name = "servo_arc" path = "lib.rs" [features] +default = ["track_alloc_size"] gecko_refcount_logging = [] servo = ["serde", "track_alloc_size"] track_alloc_size = [] diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..2c96e200aa --- /dev/null +++ b/shell.nix @@ -0,0 +1,6 @@ +with import (builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/46ae0210ce163b3cba6c7da08840c1d63de9c701.tar.gz"; +}) {}; +stdenv.mkDerivation rec { + name = "style-sync-shell"; +} diff --git a/start-rebase.sh b/start-rebase.sh new file mode 100755 index 0000000000..fe417f7f08 --- /dev/null +++ b/start-rebase.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# Usage: start-rebase.sh [extra git-rebase(1) arguments ...] +# Equivalent to git rebase --onto . +set -eu + +new_base=$1; shift +first_commit=$(git log --pretty=\%H --grep='Servo initial downstream commit') +old_base=$first_commit~ + +git rebase --onto "$new_base" "$old_base" "$@" diff --git a/style.paths b/style.paths new file mode 100644 index 0000000000..d1d2d02638 --- /dev/null +++ b/style.paths @@ -0,0 +1,8 @@ +# Filters and renames use git-filter-repo(1) --paths-from-file: +# https://htmlpreview.github.io/?https://github.com/newren/git-filter-repo/blob/docs/html/git-filter-repo.html#_filtering_based_on_many_paths + +servo/components/ +servo/rustfmt.toml + +regex:servo/components/(.+)==>\1 +servo/rustfmt.toml==>rustfmt.toml diff --git a/style/Cargo.toml b/style/Cargo.toml index 1c37aa62be..669fa6876a 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style" -version = "0.0.1" +name = "stylo" +version.workspace = true authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "The Stylo CSS engine" +readme = "../README.md" build = "build.rs" @@ -18,6 +19,7 @@ path = "lib.rs" doctest = false [features] +default = ["servo"] gecko = [ "bindgen", "malloc_size_of/gecko", @@ -31,15 +33,14 @@ gecko = [ "to_shmem/gecko", ] servo = [ - "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "web_atoms", + "mime", "serde", "servo_arc/servo", "stylo_atoms", - "servo_config", "string_cache", "style_traits/servo", "url", @@ -48,6 +49,7 @@ servo = [ ] gecko_debug = [] gecko_refcount_logging = [] +nsstring = [] [dependencies] app_units = "0.7.8" @@ -57,20 +59,20 @@ bitflags = "2" byteorder = "1.0" cssparser = "0.36" derive_more = { version = "2", features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { path = "../../../dom/base/rust" } +dom = { workspace = true } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" rustc-hash = "2.1.1" -icu_segmenter = { version = "2.0", default-features = false, features = ["auto", "compiled_data"] } +icu_segmenter = { version = ">= 1.5, <= 2.*", default-features = false, features = ["auto", "compiled_data"] } indexmap = {version = "2", features = ["std"]} itertools = "0.14" itoa = "1.0" log = "0.4" -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -web_atoms = { version = "0.1", optional = true } -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +malloc_size_of = { workspace = true } +malloc_size_of_derive = "0.1" +web_atoms = { version = "0.2.0", optional = true } +mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} num-integer = "0.1" num-traits = "0.2" @@ -79,26 +81,24 @@ parking_lot = "0.12" precomputed-hash = "0.1.1" rayon = "1" rayon-core = "1" -selectors = { path = "../selectors" } +selectors = { workspace = true } serde = {version = "1.0", optional = true, features = ["derive"]} -servo_arc = { path = "../servo_arc" } -stylo_atoms = {path = "../atoms", optional = true} -servo_config = {path = "../config", optional = true} +servo_arc = { workspace = true} +stylo_atoms = { workspace = true, optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { path = "../../../modules/libpref/init/static_prefs" } -string_cache = { version = "0.8", optional = true } +static_prefs = { workspace = true} +string_cache = { version = "0.9", optional = true } strum = "0.27" strum_macros = "0.27" -style_derive = {path = "../style_derive"} -style_traits = {path = "../style_traits"} -to_shmem = {path = "../to_shmem"} -to_shmem_derive = {path = "../to_shmem_derive"} -thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } +style_derive = { workspace = true } +style_traits = { workspace = true } +to_shmem = { workspace = true} +to_shmem_derive = { workspace = true } +thin-vec = "0.2.1" uluru = "3.0" void = "1.0.2" -gecko-profiler = { path = "../../../tools/profiler/rust-api" } url = { version = "2.5", optional = true, features = ["serde"] } [build-dependencies] diff --git a/style/build.rs b/style/build.rs index fb1f5e36ea..26176df9ad 100644 --- a/style/build.rs +++ b/style/build.rs @@ -19,7 +19,7 @@ mod build_gecko { pub static PYTHON: LazyLock = LazyLock::new(|| { env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python3.exe"] + ["python.exe"] } else { ["python3"] }; @@ -56,6 +56,12 @@ fn generate_properties(engine: &str) { .join("build.py"); let status = Command::new(&*PYTHON) + // `cargo publish` isn't happy with the `__pycache__` files that are created + // when we run the property generator. + // + // TODO(mrobinson): Is this happening because of how we run this script? It + // would be better to ensure are just placed in the output directory. + .env("PYTHONDONTWRITEBYTECODE", "1") .arg(&script) .arg(engine) .arg("style-crate") diff --git a/style/dom.rs b/style/dom.rs index 5ad759737b..c976e1217c 100644 --- a/style/dom.rs +++ b/style/dom.rs @@ -996,7 +996,7 @@ pub struct AttributeTracker<'a> { /// The element that queries for attributes. pub provider: &'a dyn AttributeProvider, /// The set of attributes we have queried. - pub references: Box>, + pub references: Box>, } impl<'a> AttributeTracker<'a> { @@ -1017,7 +1017,7 @@ impl<'a> AttributeTracker<'a> { } /// Extract the queried references and consume self - pub fn finalize(self) -> Box> { + pub fn finalize(self) -> Box> { self.references } diff --git a/style/gecko/media_features.rs b/style/gecko/media_features.rs index 9db79f8686..500d57fcb5 100644 --- a/style/gecko/media_features.rs +++ b/style/gecko/media_features.rs @@ -10,7 +10,10 @@ use crate::gecko_bindings::structs; use crate::media_queries::{Device, MediaType}; use crate::parser::ParserContext; use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::{Orientation, PrefersColorScheme}; +use crate::queries::values::{ + Orientation, PrefersColorScheme, PrefersContrast, PrefersReducedMotion, + PrefersReducedTransparency, +}; use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; use crate::values::specified::color::ForcedColors; use app_units::Au; @@ -183,20 +186,6 @@ fn eval_resolution(context: &Context) -> Resolution { Resolution::from_dppx(resolution_dppx) } -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum PrefersReducedMotion { - NoPreference, - Reduce, -} - -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] -#[repr(u8)] -enum PrefersReducedTransparency { - NoPreference, - Reduce, -} - /// Values for the dynamic-range and video-dynamic-range media features. /// https://drafts.csswg.org/mediaqueries-5/#dynamic-range /// This implements PartialOrd so that lower values will correctly match @@ -246,21 +235,6 @@ fn eval_prefers_reduced_transparency( } } -/// Possible values for prefers-contrast media query. -/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -pub enum PrefersContrast { - /// More contrast is preferred. - More, - /// Low contrast is preferred. - Less, - /// Custom (not more, not less). - Custom, - /// The default value if neither high or low contrast is enabled. - NoPreference, -} - /// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast fn eval_prefers_contrast(context: &Context, query_value: Option) -> bool { let prefers_contrast = diff --git a/style/global_style_data.rs b/style/global_style_data.rs index c1ab0f7c6e..3775f69ed5 100644 --- a/style/global_style_data.rs +++ b/style/global_style_data.rs @@ -156,7 +156,7 @@ impl StyleThreadPool { #[cfg(feature = "servo")] fn stylo_threads_pref() -> i32 { - style_config::get_i32("layout.threads") + static_prefs::pref!("layout.threads") } #[cfg(feature = "gecko")] diff --git a/style/lib.rs b/style/lib.rs index 8510b01591..80e51f9f54 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -17,6 +17,8 @@ //! //! [recalc_style_at]: traversal/fn.recalc_style_at.html //! +//! A list of supported style properties can be found as [docs::supported_properties] +//! //! Major dependencies are the [cssparser][cssparser] and [selectors][selectors] //! crates. //! @@ -37,6 +39,7 @@ extern crate gecko_profiler; pub mod gecko_string_cache; #[macro_use] extern crate log; +#[macro_use] extern crate serde; pub use servo_arc; #[cfg(feature = "servo")] @@ -116,6 +119,16 @@ pub mod use_counters; #[allow(non_camel_case_types)] pub mod values; +#[cfg(all(doc, feature = "servo"))] +/// Documentation +pub mod docs { + /// The CSS properties supported by the style system. + /// Generated from the `properties.mako.rs` template by `build.rs` + pub mod supported_properties { + #![doc = include_str!(concat!(env!("OUT_DIR"), "/css-properties.html"))] + } +} + #[cfg(feature = "gecko")] pub use crate::gecko_string_cache as string_cache; #[cfg(feature = "gecko")] diff --git a/style/parallel.rs b/style/parallel.rs index d05524363b..40946a8cbe 100644 --- a/style/parallel.rs +++ b/style/parallel.rs @@ -34,8 +34,21 @@ pub const STYLE_THREAD_STACK_SIZE_KB: usize = 256; /// The minimum stack size for a thread in the styling pool, in kilobytes. /// Servo requires a bigger stack in debug builds. +/// We allow configuring the size, since running with ASAN requires an even larger +/// stack size. #[cfg(feature = "servo")] -pub const STYLE_THREAD_STACK_SIZE_KB: usize = 512; +pub const STYLE_THREAD_STACK_SIZE_KB: usize = const { + let default_stack_size = 512; + if let Some(user_def_size) = option_env!("SERVO_STYLE_THREAD_STACK_SIZE_KB") { + if let Ok(user_def_size) = usize::from_str_radix(user_def_size, 10) { + user_def_size + } else { + panic!("SERVO_STYLE_THREAD_STACK_SIZE_KB must be a valid integer") + } + } else { + default_stack_size + } +}; /// The stack margin. If we get this deep in the stack, we will skip recursive /// optimizations to ensure that there is sufficient room for non-recursive work. diff --git a/style/properties/build.py b/style/properties/build.py index e6b4bb63e1..5ad2bc0666 100644 --- a/style/properties/build.py +++ b/style/properties/build.py @@ -8,6 +8,9 @@ import sys BASE = os.path.dirname(__file__.replace("\\", "/")) +sys.path.insert(0, os.path.join(BASE, "vendored_python", "mako-1.3.10-py3-none-any.whl")) +sys.path.insert(0, os.path.join(BASE, "vendored_python", "toml-0.10.2-py2.py3-none-any.whl")) +sys.path.insert(0, os.path.join(BASE, "vendored_python")) # For importing markupsafe sys.path.insert(0, BASE) # For importing `data.py` from mako import exceptions @@ -72,6 +75,7 @@ def main(): write(doc_servo, "css-properties.html", as_html) write(doc_servo, "css-properties.json", as_json) write(OUT_DIR, "css-properties.json", as_json) + write(OUT_DIR, "css-properties.html", as_html) def abort(message): diff --git a/style/properties/data.py b/style/properties/data.py index cbc22414c7..7d17ac857d 100644 --- a/style/properties/data.py +++ b/style/properties/data.py @@ -980,10 +980,11 @@ def add_prefixed_aliases(self, property): for prefix, pref in property.extra_prefixes: property.aliases.append(("-%s-%s" % (prefix, property.name), pref)) - def declare_longhand(self, style_struct, name, engine=None, **kwargs): + def declare_longhand(self, style_struct, name, extra_gecko_aliases=None, engine=None, **kwargs): if engine and self.engine != engine: return - + if extra_gecko_aliases and self.engine == "gecko": + kwargs.setdefault('aliases', []).extend(extra_gecko_aliases) longhand = Longhand(style_struct, name, **kwargs) self.add_prefixed_aliases(longhand) longhand.aliases = [Alias(xp[0], longhand, xp[1]) for xp in longhand.aliases] @@ -998,9 +999,14 @@ def declare_longhand(self, style_struct, name, engine=None, **kwargs): return longhand - def declare_shorthand(self, name, sub_properties, engine=None, *args, **kwargs): + def declare_shorthand(self, name, sub_properties, extra_gecko_sub_properties=None, extra_gecko_aliases=None, engine=None, *args, **kwargs): if engine and self.engine != engine: return + if self.engine == "gecko": + if extra_gecko_sub_properties: + sub_properties.extend(extra_gecko_sub_properties) + if extra_gecko_aliases: + kwargs.setdefault('aliases', []).extend(extra_gecko_aliases) sub_properties = [self.longhands_by_name[s] for s in sub_properties] shorthand = Shorthand(name, sub_properties, *args, **kwargs) self.add_prefixed_aliases(shorthand) diff --git a/style/properties/longhands.toml b/style/properties/longhands.toml index 8deee42e7c..25aad3d9e8 100644 --- a/style/properties/longhands.toml +++ b/style/properties/longhands.toml @@ -44,6 +44,7 @@ # logical_group - Logical property group name # flags - Space-separated flags (e.g., "CAN_ANIMATE_ON_COMPOSITOR") # aliases - Property aliases +# extra_gecko_aliases - Additional property aliases for Gecko, not for Servo. # extra_prefixes - Vendor prefixes # ignored_when_colors_disabled - true/false # has_effect_on_gecko_scrollbars - true/false @@ -496,9 +497,9 @@ affects = "layout" type = "color::CaretColor" initial = "generics::color::CaretColor::auto()" struct = "inherited_ui" -engine = "gecko" spec = "https://drafts.csswg.org/css-ui/#caret-color" ignored_when_colors_disabled = true +servo_restyle_damage = "repaint" affects = "paint" [clear] @@ -1275,7 +1276,6 @@ affects = "layout" type = "ListStyleType" initial = "computed::ListStyleType::disc()" struct = "list" -engine = "gecko" initial_specified_value = "specified::ListStyleType::disc()" animation_type = "discrete" spec = "https://drafts.csswg.org/css-lists/#propdef-list-style-type" @@ -1749,10 +1749,10 @@ affects = "layout" type = "PositionTryFallbacks" initial = "computed::PositionTryFallbacks::none()" struct = "position" -engine = "gecko" initial_specified_value = "specified::PositionTryFallbacks::none()" animation_type = "discrete" gecko_pref = "layout.css.anchor-positioning.enabled" +servo_pref = "layout.unimplemented" spec = "https://drafts.csswg.org/css-anchor-position-1/#position-try-fallbacks" affects = "layout" @@ -2587,10 +2587,10 @@ boxed = true type = "LineBreak" initial = "computed::LineBreak::Auto" struct = "inherited_text" -engine = "gecko" animation_type = "discrete" spec = "https://drafts.csswg.org/css-text-3/#line-break-property" affects = "layout" +servo_restyle_damage = "rebuild_box" [ruby-position] type = "RubyPosition" @@ -2892,7 +2892,7 @@ affects = "paint" type = "Color" initial = "computed_value::T::currentcolor()" struct = "border" -aliases = ["-moz-border-start-color"] +extra_gecko_aliases = ["-moz-border-start-color"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-color" logical = true logical_group = "border-color" @@ -2903,7 +2903,7 @@ affects = "paint" type = "Color" initial = "computed_value::T::currentcolor()" struct = "border" -aliases = ["-moz-border-end-color"] +extra_gecko_aliases = ["-moz-border-end-color"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-color" logical = true logical_group = "border-color" @@ -2976,7 +2976,7 @@ affects = "layout" type = "BorderStyle" initial = "specified::BorderStyle::None" struct = "border" -aliases = ["-moz-border-start-style"] +extra_gecko_aliases = ["-moz-border-start-style"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-style" animation_type = "none" gecko_ffi_name = "mBorderStyle.6" @@ -2988,7 +2988,7 @@ affects = "layout" type = "BorderStyle" initial = "specified::BorderStyle::None" struct = "border" -aliases = ["-moz-border-end-style"] +extra_gecko_aliases = ["-moz-border-end-style"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-style" animation_type = "none" gecko_ffi_name = "mBorderStyle.7" @@ -3066,7 +3066,7 @@ affects = "layout" type = "BorderSideWidth" initial = "computed::BorderSideWidth::medium()" struct = "border" -aliases = ["-moz-border-start-width"] +extra_gecko_aliases = ["-moz-border-start-width"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-start-width" logical = true logical_group = "border-width" @@ -3078,7 +3078,7 @@ affects = "layout" type = "BorderSideWidth" initial = "computed::BorderSideWidth::medium()" struct = "border" -aliases = ["-moz-border-end-width"] +extra_gecko_aliases = ["-moz-border-end-width"] spec = "https://drafts.csswg.org/css-logical-props/#propdef-border-inline-end-width" logical = true logical_group = "border-width" @@ -3250,7 +3250,7 @@ affects = "layout" type = "Margin" initial = "computed::Margin::zero()" struct = "margin" -aliases = ["-moz-margin-start"] +extra_gecko_aliases = ["-moz-margin-start"] logical = true logical_group = "margin" gecko_ffi_name = "mMargin.6" @@ -3263,7 +3263,7 @@ affects = "layout" type = "Margin" initial = "computed::Margin::zero()" struct = "margin" -aliases = ["-moz-margin-end"] +extra_gecko_aliases = ["-moz-margin-end"] logical = true logical_group = "margin" gecko_ffi_name = "mMargin.7" @@ -3426,7 +3426,7 @@ affects = "layout" type = "NonNegativeLengthPercentage" initial = "computed::NonNegativeLengthPercentage::zero()" struct = "padding" -aliases = ["-moz-padding-start"] +extra_gecko_aliases = ["-moz-padding-start"] logical = true logical_group = "padding" spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-start" @@ -3438,7 +3438,7 @@ affects = "layout" type = "NonNegativeLengthPercentage" initial = "computed::NonNegativeLengthPercentage::zero()" struct = "padding" -aliases = ["-moz-padding-end"] +extra_gecko_aliases = ["-moz-padding-end"] logical = true logical_group = "padding" spec = "https://drafts.csswg.org/css-logical-props/#propdef-padding-inline-end" @@ -3904,10 +3904,10 @@ keyword = { values = ["auto", "optimizespeed", "optimizelegibility", "geometricp [-webkit-text-security] struct = "inherited_text" -engine = "gecko" spec = "https://drafts.csswg.org/css-text/#MISSING" animation_type = "discrete" affects = "layout" +servo_restyle_damage = "rebuild_box" keyword = { values = ["none", "circle", "disc", "square"] } [text-wrap-mode] diff --git a/style/properties/properties.mako.rs b/style/properties/properties.mako.rs index e4dd60d404..6a6cb6f2ca 100644 --- a/style/properties/properties.mako.rs +++ b/style/properties/properties.mako.rs @@ -14,6 +14,7 @@ use std::{ops, ptr, fmt, mem}; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, NonCustomCSSPropertyId}; #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin; #[cfg(feature = "servo")] use crate::computed_values; +#[cfg(feature = "servo")] use crate::dom::AttributeReferences; use crate::logical_geometry::WritingMode; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use crate::computed_value_flags::*; @@ -396,7 +397,10 @@ impl NonCustomPropertyId { pref = getattr(property, "servo_pref") %> % if pref: - Some("${pref}"), + { + const_assert!(!static_prefs::default_value!("${pref}")); + Some("${pref}") + }, % else: None, % endif @@ -407,7 +411,8 @@ impl NonCustomPropertyId { Some(pref) => pref, }; - style_config::get_bool(pref) + // The assertions above guarantee that the pref defaults to false. + static_prefs::get_bool(pref, false) % endif }; @@ -1892,6 +1897,7 @@ impl ComputedValues { }), % endfor custom_properties: crate::custom_properties::ComputedCustomProperties::default(), + attribute_references: AttributeReferences::default(), writing_mode: WritingMode::empty(), rules: None, visited_style: None, @@ -2827,7 +2833,7 @@ macro_rules! longhand_properties_idents { #[cfg(feature = "gecko")] size_of_test!(ComputedValues, 256); #[cfg(feature = "servo")] -size_of_test!(ComputedValues, 216); +size_of_test!(ComputedValues, 224); // FFI relies on this. size_of_test!(Option>, 8); diff --git a/style/properties/shorthands.rs b/style/properties/shorthands.rs index cfe7537ee4..a66ebf5110 100644 --- a/style/properties/shorthands.rs +++ b/style/properties/shorthands.rs @@ -496,6 +496,7 @@ pub mod border { } } +#[cfg(feature = "gecko")] pub mod container { use super::*; pub use crate::properties::shorthands_generated::container::*; @@ -594,6 +595,7 @@ pub mod vertical_align { } } +#[cfg(feature = "gecko")] pub mod page_break_before { use super::*; pub use crate::properties::shorthands_generated::page_break_before::*; @@ -619,6 +621,7 @@ pub mod page_break_before { } } +#[cfg(feature = "gecko")] pub mod page_break_after { pub use crate::properties::shorthands_generated::page_break_after::*; @@ -644,6 +647,7 @@ pub mod page_break_after { } } +#[cfg(feature = "gecko")] pub mod page_break_inside { use super::*; pub use crate::properties::shorthands_generated::page_break_inside::*; @@ -668,6 +672,7 @@ pub mod page_break_inside { } } +#[cfg(feature = "gecko")] pub mod offset { use super::*; pub use crate::properties::shorthands_generated::offset::*; @@ -879,6 +884,7 @@ pub mod columns { } } +#[cfg(feature = "gecko")] pub mod column_rule { pub use crate::properties::shorthands_generated::column_rule::*; @@ -913,6 +919,7 @@ pub mod column_rule { } } +#[cfg(feature = "gecko")] pub mod text_wrap { pub use crate::properties::shorthands_generated::text_wrap::*; @@ -1088,6 +1095,7 @@ pub mod white_space { } } +#[cfg(feature = "gecko")] pub mod _webkit_text_stroke { pub use crate::properties::shorthands_generated::_webkit_text_stroke::*; @@ -1279,6 +1287,7 @@ pub mod gap { } } +#[cfg(feature = "gecko")] pub mod marker { pub use crate::properties::shorthands_generated::marker::*; @@ -1730,6 +1739,7 @@ pub mod grid_area { } } +#[cfg(feature = "gecko")] pub mod position_try { pub use crate::properties::shorthands_generated::position_try::*; @@ -1769,6 +1779,7 @@ pub mod position_try { } } +#[cfg(feature = "gecko")] fn timeline_to_css( name: &[specified::TimelineName], axes: &[specified::ScrollAxis], @@ -1793,6 +1804,7 @@ where Ok(()) } +#[cfg(feature = "gecko")] pub mod scroll_timeline { pub use crate::properties::shorthands_generated::scroll_timeline::*; @@ -1835,6 +1847,7 @@ pub mod scroll_timeline { } } +#[cfg(feature = "gecko")] pub mod view_timeline { pub use crate::properties::shorthands_generated::view_timeline::*; @@ -2473,12 +2486,15 @@ pub mod font { pub use crate::properties::shorthands_generated::font::*; use super::*; + #[cfg(feature = "gecko")] use crate::properties::longhands::{ - font_family, font_feature_settings, font_kerning, font_language_override, - font_optical_sizing, font_size, font_size_adjust, font_stretch, font_style, - font_variant_alternates, font_variant_caps, font_variant_east_asian, font_variant_emoji, + font_family, font_feature_settings, font_kerning, font_language_override, font_size, + font_size_adjust, font_variant_alternates, font_variant_east_asian, font_variant_emoji, font_variant_ligatures, font_variant_numeric, font_variant_position, - font_variation_settings, font_weight, + }; + use crate::properties::longhands::{ + font_optical_sizing, font_stretch, font_style, font_variant_caps, font_variation_settings, + font_weight, }; #[cfg(feature = "gecko")] use crate::values::specified::font::SystemFont; @@ -2578,19 +2594,30 @@ pub mod font { font_family: family, font_optical_sizing: font_optical_sizing::get_initial_specified_value(), font_variation_settings: font_variation_settings::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_kerning: font_kerning::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_language_override: font_language_override::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_size_adjust: font_size_adjust::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_alternates: font_variant_alternates::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_east_asian: font_variant_east_asian::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_emoji: font_variant_emoji::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_ligatures: font_variant_ligatures::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_numeric: font_variant_numeric::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_variant_position: font_variant_position::get_initial_specified_value(), + #[cfg(feature = "gecko")] font_feature_settings: font_feature_settings::get_initial_specified_value(), }) } + #[cfg(feature = "gecko")] enum CheckSystemResult { AllSystem(SystemFont), SomeSystem, @@ -2602,6 +2629,7 @@ pub mod font { where W: fmt::Write, { + #[cfg(feature = "gecko")] match self.check_system() { CheckSystemResult::AllSystem(sys) => return sys.to_css(dest), CheckSystemResult::SomeSystem => return Ok(()), @@ -2618,42 +2646,52 @@ pub mod font { return Ok(()); } } + #[cfg(feature = "gecko")] if let Some(v) = self.font_variant_emoji { if v != &font_variant_emoji::get_initial_specified_value() { return Ok(()); } } + #[cfg(feature = "gecko")] if self.font_kerning != &font_kerning::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_language_override != &font_language_override::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_size_adjust != &font_size_adjust::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_variant_alternates != &font_variant_alternates::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_variant_east_asian != &font_variant_east_asian::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_variant_ligatures != &font_variant_ligatures::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_variant_numeric != &font_variant_numeric::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_variant_position != &font_variant_position::get_initial_specified_value() { return Ok(()); } + #[cfg(feature = "gecko")] if self.font_feature_settings != &font_feature_settings::get_initial_specified_value() { return Ok(()); } @@ -2711,6 +2749,7 @@ pub mod font { } impl<'a> LonghandsToSerialize<'a> { + #[cfg(feature = "gecko")] fn check_system(&self) -> CheckSystemResult { let mut sys = None; let mut all = true; @@ -2777,13 +2816,12 @@ pub mod font_variant { pub use crate::properties::shorthands_generated::font_variant::*; use super::*; - use crate::properties::longhands::font_variant_alternates; use crate::properties::longhands::font_variant_caps; - use crate::properties::longhands::font_variant_east_asian; - use crate::properties::longhands::font_variant_emoji; - use crate::properties::longhands::font_variant_ligatures; - use crate::properties::longhands::font_variant_numeric; - use crate::properties::longhands::font_variant_position; + #[cfg(feature = "gecko")] + use crate::properties::longhands::{ + font_variant_alternates, font_variant_east_asian, font_variant_emoji, + font_variant_ligatures, font_variant_numeric, font_variant_position, + }; #[allow(unused_imports)] use crate::values::specified::FontVariantLigatures; @@ -2791,12 +2829,18 @@ pub mod font_variant { context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { + #[cfg(feature = "gecko")] let mut ligatures = None; let mut caps = None; + #[cfg(feature = "gecko")] let mut alternates = None; + #[cfg(feature = "gecko")] let mut numeric = None; + #[cfg(feature = "gecko")] let mut east_asian = None; + #[cfg(feature = "gecko")] let mut position = None; + #[cfg(feature = "gecko")] let mut emoji = None; if input @@ -2807,7 +2851,10 @@ pub mod font_variant { .try_parse(|input| input.expect_ident_matching("none")) .is_ok() { - ligatures = Some(FontVariantLigatures::NONE); + #[cfg(feature = "gecko")] + { + ligatures = Some(FontVariantLigatures::NONE); + } } else { let mut parsed = 0; loop { @@ -2821,12 +2868,18 @@ pub mod font_variant { { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } + #[cfg(feature = "gecko")] try_parse_one!(context, input, ligatures, font_variant_ligatures::parse); try_parse_one!(context, input, caps, font_variant_caps::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, alternates, font_variant_alternates::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, numeric, font_variant_numeric::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, east_asian, font_variant_east_asian::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, position, font_variant_position::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, emoji, font_variant_emoji::parse); parsed -= 1; break; @@ -2837,7 +2890,8 @@ pub mod font_variant { } } - Ok(expanded! { + #[cfg(feature = "gecko")] + return Ok(expanded! { font_variant_ligatures: unwrap_or_initial!(font_variant_ligatures, ligatures), font_variant_caps: unwrap_or_initial!(font_variant_caps, caps), font_variant_alternates: unwrap_or_initial!(font_variant_alternates, alternates), @@ -2845,7 +2899,11 @@ pub mod font_variant { font_variant_east_asian: unwrap_or_initial!(font_variant_east_asian, east_asian), font_variant_position: unwrap_or_initial!(font_variant_position, position), font_variant_emoji: unwrap_or_initial!(font_variant_emoji, emoji), - }) + }); + #[cfg(feature = "servo")] + return Ok(expanded! { + font_variant_caps: unwrap_or_initial!(font_variant_caps, caps), + }); } impl<'a> ToCss for LonghandsToSerialize<'a> { @@ -2854,9 +2912,15 @@ pub mod font_variant { where W: fmt::Write, { + #[cfg(feature = "gecko")] let has_none_ligatures = self.font_variant_ligatures == &FontVariantLigatures::NONE; + #[cfg(feature = "servo")] + let has_none_ligatures = false; + #[cfg(feature = "gecko")] const TOTAL_SUBPROPS: usize = 7; + #[cfg(feature = "servo")] + const TOTAL_SUBPROPS: usize = 1; let mut nb_normals = 0; macro_rules! count_normal { ($e: expr, $p: ident) => { @@ -2868,13 +2932,18 @@ pub mod font_variant { count_normal!(self.$v, $v); }; } + #[cfg(feature = "gecko")] count_normal!(font_variant_ligatures); count_normal!(font_variant_caps); + #[cfg(feature = "gecko")] count_normal!(font_variant_alternates); + #[cfg(feature = "gecko")] count_normal!(font_variant_numeric); + #[cfg(feature = "gecko")] count_normal!(font_variant_east_asian); + #[cfg(feature = "gecko")] count_normal!(font_variant_position); - + #[cfg(feature = "gecko")] if let Some(value) = self.font_variant_emoji { if value == &font_variant_emoji::get_initial_specified_value() { nb_normals += 1; @@ -2905,12 +2974,18 @@ pub mod font_variant { }; } + #[cfg(feature = "gecko")] write!(font_variant_ligatures); write!(font_variant_caps); + #[cfg(feature = "gecko")] write!(font_variant_alternates); + #[cfg(feature = "gecko")] write!(font_variant_numeric); + #[cfg(feature = "gecko")] write!(font_variant_east_asian); + #[cfg(feature = "gecko")] write!(font_variant_position); + #[cfg(feature = "gecko")] if let Some(v) = self.font_variant_emoji { write!(v, font_variant_emoji); } @@ -2919,6 +2994,7 @@ pub mod font_variant { } } +#[cfg(feature = "gecko")] pub mod font_synthesis { pub use crate::properties::shorthands_generated::font_synthesis::*; @@ -3026,6 +3102,7 @@ pub mod font_synthesis { } } +#[cfg(feature = "gecko")] pub mod text_emphasis { pub use crate::properties::shorthands_generated::text_emphasis::*; @@ -3060,6 +3137,7 @@ pub mod text_decoration { pub use crate::properties::shorthands_generated::text_decoration::*; use super::*; + #[cfg(feature = "gecko")] use crate::properties::longhands::text_decoration_thickness; use crate::properties::longhands::{ text_decoration_color, text_decoration_line, text_decoration_style, @@ -3072,6 +3150,7 @@ pub mod text_decoration { let mut line = None; let mut style = None; let mut color = None; + #[cfg(feature = "gecko")] let mut thickness = None; let mut parsed = 0; @@ -3080,6 +3159,7 @@ pub mod text_decoration { try_parse_one!(context, input, line, text_decoration_line::parse); try_parse_one!(context, input, style, text_decoration_style::parse); try_parse_one!(context, input, color, text_decoration_color::parse); + #[cfg(feature = "gecko")] try_parse_one!(context, input, thickness, text_decoration_thickness::parse); parsed -= 1; break; @@ -3089,12 +3169,19 @@ pub mod text_decoration { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } - Ok(expanded! { + #[cfg(feature = "gecko")] + return Ok(expanded! { text_decoration_line: unwrap_or_initial!(text_decoration_line, line), text_decoration_style: unwrap_or_initial!(text_decoration_style, style), text_decoration_color: unwrap_or_initial!(text_decoration_color, color), text_decoration_thickness: unwrap_or_initial!(text_decoration_thickness, thickness), - }) + }); + #[cfg(feature = "servo")] + return Ok(expanded! { + text_decoration_line: unwrap_or_initial!(text_decoration_line, line), + text_decoration_style: unwrap_or_initial!(text_decoration_style, style), + text_decoration_color: unwrap_or_initial!(text_decoration_color, color), + }); } impl<'a> ToCss for LonghandsToSerialize<'a> { @@ -3109,13 +3196,17 @@ pub mod text_decoration { let is_solid_style = *self.text_decoration_style == text_decoration_style::SpecifiedValue::Solid; let is_current_color = *self.text_decoration_color == Color::CurrentColor; + #[cfg(feature = "gecko")] let is_auto_thickness = self.text_decoration_thickness.is_auto(); + #[cfg(feature = "servo")] + let is_auto_thickness = true; let is_none = *self.text_decoration_line == TextDecorationLine::none(); let mut writer = SequenceWriter::new(dest, " "); if (is_solid_style && is_current_color && is_auto_thickness) || !is_none { writer.item(self.text_decoration_line)?; } + #[cfg(feature = "gecko")] if !is_auto_thickness { writer.item(self.text_decoration_thickness)?; } @@ -3387,6 +3478,7 @@ pub mod animation { } } +#[cfg(feature = "gecko")] pub mod mask { pub use crate::properties::shorthands_generated::mask::*; @@ -3667,6 +3759,7 @@ pub mod mask { } } +#[cfg(feature = "gecko")] pub mod mask_position { pub use crate::properties::shorthands_generated::mask_position::*; diff --git a/style/properties/shorthands.toml b/style/properties/shorthands.toml index d41986af8e..c51cd74ba7 100644 --- a/style/properties/shorthands.toml +++ b/style/properties/shorthands.toml @@ -18,16 +18,19 @@ rule_types_allowed = ["style", "keyframe", "page", "scope", "position-try"] kind = "two_properties" [scroll-margin] +engine = "gecko" sub_properties = ["scroll-margin-top", "scroll-margin-right", "scroll-margin-bottom", "scroll-margin-left"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin" kind = "four_sides" [scroll-margin-block] +engine = "gecko" sub_properties = ["scroll-margin-block-start", "scroll-margin-block-end"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-block" kind = "two_properties" [scroll-margin-inline] +engine = "gecko" sub_properties = ["scroll-margin-inline-start", "scroll-margin-inline-end"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-margin-inline" kind = "two_properties" @@ -51,16 +54,19 @@ rule_types_allowed = ["style", "keyframe", "page", "scope", "position-try"] kind = "two_properties" [scroll-padding] +engine = "gecko" sub_properties = ["scroll-padding-top", "scroll-padding-right", "scroll-padding-bottom", "scroll-padding-left"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding" kind = "four_sides" [scroll-padding-block] +engine = "gecko" sub_properties = ["scroll-padding-block-start", "scroll-padding-block-end"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-block" kind = "two_properties" [scroll-padding-inline] +engine = "gecko" sub_properties = ["scroll-padding-inline-start", "scroll-padding-inline-end"] spec = "https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-inline" kind = "two_properties" @@ -146,13 +152,13 @@ kind = "single_border" sub_properties = ["border-inline-start-width", "border-inline-start-style", "border-inline-start-color"] spec = "https://drafts.csswg.org/css-logical/#propdef-border-inline-start" kind = "single_border" -aliases = ["-moz-border-start"] +extra_gecko_aliases = ["-moz-border-start"] [border-inline-end] sub_properties = ["border-inline-end-width", "border-inline-end-style", "border-inline-end-color"] spec = "https://drafts.csswg.org/css-logical/#propdef-border-inline-end" kind = "single_border" -aliases = ["-moz-border-end"] +extra_gecko_aliases = ["-moz-border-end"] [border-block] sub_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"] @@ -412,7 +418,8 @@ spec = "https://drafts.csswg.org/css-multicol/#propdef-column-rule" derive_serialize = true [text-decoration] -sub_properties = ["text-decoration-color", "text-decoration-line", "text-decoration-style", "text-decoration-thickness"] +sub_properties = ["text-decoration-color", "text-decoration-line", "text-decoration-style"] +extra_gecko_sub_properties = ["text-decoration-thickness"] spec = "https://drafts.csswg.org/css-text-decor/#propdef-text-decoration" [font] @@ -425,7 +432,9 @@ sub_properties = [ "line-height", "font-family", "font-optical-sizing", - "font-variation-settings", + "font-variation-settings" +] +extra_gecko_sub_properties = [ "font-size-adjust", "font-kerning", "font-variant-alternates", @@ -441,7 +450,8 @@ spec = "https://drafts.csswg.org/css-fonts-3/#propdef-font" derive_value_info = false [font-variant] -sub_properties = ["font-variant-caps", "font-variant-alternates", "font-variant-east-asian", "font-variant-emoji", "font-variant-ligatures", "font-variant-numeric", "font-variant-position"] +sub_properties = ["font-variant-caps"] +extra_gecko_sub_properties = ["font-variant-alternates", "font-variant-east-asian", "font-variant-emoji", "font-variant-ligatures", "font-variant-numeric", "font-variant-position"] spec = "https://drafts.csswg.org/css-fonts-3/#propdef-font-variant" [font-synthesis] diff --git a/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl b/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl new file mode 100644 index 0000000000..2f85cd7b57 Binary files /dev/null and b/style/properties/vendored_python/mako-1.3.10-py3-none-any.whl differ diff --git a/style/properties/vendored_python/markupsafe/LICENSE.txt b/style/properties/vendored_python/markupsafe/LICENSE.txt new file mode 100644 index 0000000000..e270514adb --- /dev/null +++ b/style/properties/vendored_python/markupsafe/LICENSE.txt @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/style/properties/vendored_python/markupsafe/__init__.py b/style/properties/vendored_python/markupsafe/__init__.py new file mode 100644 index 0000000000..8bf187d55e --- /dev/null +++ b/style/properties/vendored_python/markupsafe/__init__.py @@ -0,0 +1,384 @@ +# Vendored from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/__init__.py +# with patched section from https://github.com/pallets/markupsafe/blob/1251593f6b0e3b45f2cc8aba662622bc22d6a5e2/src/markupsafe/_native.py +from __future__ import annotations + +import collections.abc as cabc +import string +import typing as t + +# BEGIN PATCHED SECTION +def _escape_inner(s: str, /) -> str: + return ( + s.replace("&", "&") + .replace(">", ">") + .replace("<", "<") + .replace("'", "'") + .replace('"', """) + ) +# BEGIN PATCHED SECTION + + +class _HasHTML(t.Protocol): + def __html__(self, /) -> str: ... + + +class _TPEscape(t.Protocol): + def __call__(self, s: t.Any, /) -> Markup: ... + + +def escape(s: t.Any, /) -> Markup: + """Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in + the string with HTML-safe sequences. Use this if you need to display + text that might contain such characters in HTML. + + If the object has an ``__html__`` method, it is called and the + return value is assumed to already be safe for HTML. + + :param s: An object to be converted to a string and escaped. + :return: A :class:`Markup` string with the escaped text. + """ + # If the object is already a plain string, skip __html__ check and string + # conversion. This is the most common use case. + # Use type(s) instead of s.__class__ because a proxy object may be reporting + # the __class__ of the proxied value. + if type(s) is str: + return Markup(_escape_inner(s)) + + if hasattr(s, "__html__"): + return Markup(s.__html__()) + + return Markup(_escape_inner(str(s))) + + +def escape_silent(s: t.Any | None, /) -> Markup: + """Like :func:`escape` but treats ``None`` as the empty string. + Useful with optional values, as otherwise you get the string + ``'None'`` when the value is ``None``. + + >>> escape(None) + Markup('None') + >>> escape_silent(None) + Markup('') + """ + if s is None: + return Markup() + + return escape(s) + + +def soft_str(s: t.Any, /) -> str: + """Convert an object to a string if it isn't already. This preserves + a :class:`Markup` string rather than converting it back to a basic + string, so it will still be marked as safe and won't be escaped + again. + + >>> value = escape("") + >>> value + Markup('<User 1>') + >>> escape(str(value)) + Markup('&lt;User 1&gt;') + >>> escape(soft_str(value)) + Markup('<User 1>') + """ + if not isinstance(s, str): + return str(s) + + return s + + +class Markup(str): + """A string that is ready to be safely inserted into an HTML or XML + document, either because it was escaped or because it was marked + safe. + + Passing an object to the constructor converts it to text and wraps + it to mark it safe without escaping. To escape the text, use the + :meth:`escape` class method instead. + + >>> Markup("Hello, World!") + Markup('Hello, World!') + >>> Markup(42) + Markup('42') + >>> Markup.escape("Hello, World!") + Markup('Hello <em>World</em>!') + + This implements the ``__html__()`` interface that some frameworks + use. Passing an object that implements ``__html__()`` will wrap the + output of that method, marking it safe. + + >>> class Foo: + ... def __html__(self): + ... return 'foo' + ... + >>> Markup(Foo()) + Markup('foo') + + This is a subclass of :class:`str`. It has the same methods, but + escapes their arguments and returns a ``Markup`` instance. + + >>> Markup("%s") % ("foo & bar",) + Markup('foo & bar') + >>> Markup("Hello ") + "" + Markup('Hello <foo>') + """ + + __slots__ = () + + def __new__( + cls, object: t.Any = "", encoding: str | None = None, errors: str = "strict" + ) -> te.Self: + if hasattr(object, "__html__"): + object = object.__html__() + + if encoding is None: + return super().__new__(cls, object) + + return super().__new__(cls, object, encoding, errors) + + def __html__(self, /) -> te.Self: + return self + + def __add__(self, value: str | _HasHTML, /) -> te.Self: + if isinstance(value, str) or hasattr(value, "__html__"): + return self.__class__(super().__add__(self.escape(value))) + + return NotImplemented + + def __radd__(self, value: str | _HasHTML, /) -> te.Self: + if isinstance(value, str) or hasattr(value, "__html__"): + return self.escape(value).__add__(self) + + return NotImplemented + + def __mul__(self, value: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().__mul__(value)) + + def __rmul__(self, value: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().__mul__(value)) + + def __mod__(self, value: t.Any, /) -> te.Self: + if isinstance(value, tuple): + # a tuple of arguments, each wrapped + value = tuple(_MarkupEscapeHelper(x, self.escape) for x in value) + elif hasattr(type(value), "__getitem__") and not isinstance(value, str): + # a mapping of arguments, wrapped + value = _MarkupEscapeHelper(value, self.escape) + else: + # a single argument, wrapped with the helper and a tuple + value = (_MarkupEscapeHelper(value, self.escape),) + + return self.__class__(super().__mod__(value)) + + def __repr__(self, /) -> str: + return f"{self.__class__.__name__}({super().__repr__()})" + + def join(self, iterable: cabc.Iterable[str | _HasHTML], /) -> te.Self: + return self.__class__(super().join(map(self.escape, iterable))) + + def split( # type: ignore[override] + self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1 + ) -> list[te.Self]: + return [self.__class__(v) for v in super().split(sep, maxsplit)] + + def rsplit( # type: ignore[override] + self, /, sep: str | None = None, maxsplit: t.SupportsIndex = -1 + ) -> list[te.Self]: + return [self.__class__(v) for v in super().rsplit(sep, maxsplit)] + + def splitlines( # type: ignore[override] + self, /, keepends: bool = False + ) -> list[te.Self]: + return [self.__class__(v) for v in super().splitlines(keepends)] + + def unescape(self, /) -> str: + """Convert escaped markup back into a text string. This replaces + HTML entities with the characters they represent. + + >>> Markup("Main » About").unescape() + 'Main » About' + """ + from html import unescape + + return unescape(str(self)) + + def striptags(self, /) -> str: + """:meth:`unescape` the markup, remove tags, and normalize + whitespace to single spaces. + + >>> Markup("Main »\tAbout").striptags() + 'Main » About' + """ + value = str(self) + + # Look for comments then tags separately. Otherwise, a comment that + # contains a tag would end early, leaving some of the comment behind. + + # keep finding comment start marks + while (start := value.find("", start)) == -1: + break + + value = f"{value[:start]}{value[end + 3 :]}" + + # remove tags using the same method + while (start := value.find("<")) != -1: + if (end := value.find(">", start)) == -1: + break + + value = f"{value[:start]}{value[end + 1 :]}" + + # collapse spaces + value = " ".join(value.split()) + return self.__class__(value).unescape() + + @classmethod + def escape(cls, s: t.Any, /) -> te.Self: + """Escape a string. Calls :func:`escape` and ensures that for + subclasses the correct type is returned. + """ + rv = escape(s) + + if rv.__class__ is not cls: + return cls(rv) + + return rv # type: ignore[return-value] + + def __getitem__(self, key: t.SupportsIndex | slice, /) -> te.Self: + return self.__class__(super().__getitem__(key)) + + def capitalize(self, /) -> te.Self: + return self.__class__(super().capitalize()) + + def title(self, /) -> te.Self: + return self.__class__(super().title()) + + def lower(self, /) -> te.Self: + return self.__class__(super().lower()) + + def upper(self, /) -> te.Self: + return self.__class__(super().upper()) + + def replace(self, old: str, new: str, count: t.SupportsIndex = -1, /) -> te.Self: + return self.__class__(super().replace(old, self.escape(new), count)) + + def ljust(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().ljust(width, self.escape(fillchar))) + + def rjust(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().rjust(width, self.escape(fillchar))) + + def lstrip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().lstrip(chars)) + + def rstrip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().rstrip(chars)) + + def center(self, width: t.SupportsIndex, fillchar: str = " ", /) -> te.Self: + return self.__class__(super().center(width, self.escape(fillchar))) + + def strip(self, chars: str | None = None, /) -> te.Self: + return self.__class__(super().strip(chars)) + + def translate( + self, + table: cabc.Mapping[int, str | int | None], # type: ignore[override] + /, + ) -> str: + return self.__class__(super().translate(table)) + + def expandtabs(self, /, tabsize: t.SupportsIndex = 8) -> te.Self: + return self.__class__(super().expandtabs(tabsize)) + + def swapcase(self, /) -> te.Self: + return self.__class__(super().swapcase()) + + def zfill(self, width: t.SupportsIndex, /) -> te.Self: + return self.__class__(super().zfill(width)) + + def casefold(self, /) -> te.Self: + return self.__class__(super().casefold()) + + def removeprefix(self, prefix: str, /) -> te.Self: + return self.__class__(super().removeprefix(prefix)) + + def removesuffix(self, suffix: str) -> te.Self: + return self.__class__(super().removesuffix(suffix)) + + def partition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]: + left, sep, right = super().partition(sep) + cls = self.__class__ + return cls(left), cls(sep), cls(right) + + def rpartition(self, sep: str, /) -> tuple[te.Self, te.Self, te.Self]: + left, sep, right = super().rpartition(sep) + cls = self.__class__ + return cls(left), cls(sep), cls(right) + + def format(self, *args: t.Any, **kwargs: t.Any) -> te.Self: + formatter = EscapeFormatter(self.escape) + return self.__class__(formatter.vformat(self, args, kwargs)) + + def format_map( + self, + mapping: cabc.Mapping[str, t.Any], # type: ignore[override] + /, + ) -> te.Self: + formatter = EscapeFormatter(self.escape) + return self.__class__(formatter.vformat(self, (), mapping)) + + def __html_format__(self, format_spec: str, /) -> te.Self: + if format_spec: + raise ValueError("Unsupported format specification for Markup.") + + return self + + +class EscapeFormatter(string.Formatter): + __slots__ = ("escape",) + + def __init__(self, escape: _TPEscape) -> None: + self.escape: _TPEscape = escape + super().__init__() + + def format_field(self, value: t.Any, format_spec: str) -> str: + if hasattr(value, "__html_format__"): + rv = value.__html_format__(format_spec) + elif hasattr(value, "__html__"): + if format_spec: + raise ValueError( + f"Format specifier {format_spec} given, but {type(value)} does not" + " define __html_format__. A class that defines __html__ must define" + " __html_format__ to work with format specifiers." + ) + rv = value.__html__() + else: + # We need to make sure the format spec is str here as + # otherwise the wrong callback methods are invoked. + rv = super().format_field(value, str(format_spec)) + return str(self.escape(rv)) + + +class _MarkupEscapeHelper: + """Helper for :meth:`Markup.__mod__`.""" + + __slots__ = ("obj", "escape") + + def __init__(self, obj: t.Any, escape: _TPEscape) -> None: + self.obj: t.Any = obj + self.escape: _TPEscape = escape + + def __getitem__(self, key: t.Any, /) -> te.Self: + return self.__class__(self.obj[key], self.escape) + + def __str__(self, /) -> str: + return str(self.escape(self.obj)) + + def __repr__(self, /) -> str: + return str(self.escape(repr(self.obj))) + + def __int__(self, /) -> int: + return int(self.obj) + + def __float__(self, /) -> float: + return float(self.obj) diff --git a/style/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl b/style/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl new file mode 100644 index 0000000000..2cb8dcbd80 Binary files /dev/null and b/style/properties/vendored_python/toml-0.10.2-py2.py3-none-any.whl differ diff --git a/style/queries/values.rs b/style/queries/values.rs index 98b16d76f3..2d79a4d6b2 100644 --- a/style/queries/values.rs +++ b/style/queries/values.rs @@ -44,3 +44,36 @@ pub enum PrefersColorScheme { Light, Dark, } + +/// Possible values for prefers-contrast media query. +/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] +#[repr(u8)] +pub enum PrefersContrast { + /// More contrast is preferred. + More, + /// Low contrast is preferred. + Less, + /// Custom (not more, not less). + Custom, + /// The default value if neither high or low contrast is enabled. + NoPreference, +} + +/// Values for the prefers-reduced-motion media feature. +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum PrefersReducedMotion { + NoPreference, + Reduce, +} + +/// Values for the prefers-reduced-transparency media feature. +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum PrefersReducedTransparency { + NoPreference, + Reduce, +} diff --git a/style/servo/media_queries.rs b/style/servo/media_queries.rs index da9fa7c6ff..4471b1bac1 100644 --- a/style/servo/media_queries.rs +++ b/style/servo/media_queries.rs @@ -14,7 +14,9 @@ use crate::media_queries::MediaType; use crate::properties::style_structs::Font; use crate::properties::ComputedValues; use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::PrefersColorScheme; +use crate::queries::values::{ + PrefersColorScheme, PrefersContrast, PrefersReducedMotion, PrefersReducedTransparency, +}; use crate::values::computed::font::GenericFontFamily; use crate::values::computed::{ CSSPixelLength, Context, Length, LineHeight, NonNegativeLength, Resolution, @@ -117,6 +119,15 @@ pub struct Device { /// Whether the user prefers light mode or dark mode #[ignore_malloc_size_of = "Pure stack type"] prefers_color_scheme: PrefersColorScheme, + /// The users contrast preference + #[ignore_malloc_size_of = "Pure stack type"] + prefers_contrast: PrefersContrast, + /// The users motion preference + #[ignore_malloc_size_of = "Pure stack type"] + prefers_reduced_motion: PrefersReducedMotion, + /// The users transparency preference + #[ignore_malloc_size_of = "Pure stack type"] + prefers_reduced_transparency: PrefersReducedTransparency, /// The CssEnvironment object responsible of getting CSS environment /// variables. environment: CssEnvironment, @@ -138,6 +149,9 @@ impl Device { font_metrics_provider: Box, default_computed_values: Arc, prefers_color_scheme: PrefersColorScheme, + prefers_contrast: PrefersContrast, + prefers_reduced_motion: PrefersReducedMotion, + prefers_reduced_transparency: PrefersReducedTransparency, ) -> Device { let default_values = ComputedValues::initial_values_with_font_override(Font::initial_values()); @@ -160,6 +174,9 @@ impl Device { used_font_metrics: AtomicBool::new(false), used_viewport_units: AtomicBool::new(false), prefers_color_scheme, + prefers_contrast, + prefers_reduced_motion, + prefers_reduced_transparency, environment: CssEnvironment, font_metrics_provider, default_computed_values, @@ -505,6 +522,51 @@ impl Device { false } + /// Set the [`PrefersContrast`] value on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_prefers_contrast(&mut self, new_prefers_contrast: PrefersContrast) { + self.prefers_contrast = new_prefers_contrast; + } + + /// Returns the preferred contrast of this [`Device`]. + pub fn prefers_contrast(&self) -> PrefersContrast { + self.prefers_contrast + } + + /// Set the [`PrefersReducedMotion`] value on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_prefers_reduced_motion(&mut self, new_prefers_reduced_motion: PrefersReducedMotion) { + self.prefers_reduced_motion = new_prefers_reduced_motion; + } + + /// Returns the preferred contrast of this [`Device`]. + pub fn prefers_reduced_motion(&self) -> PrefersReducedMotion { + self.prefers_reduced_motion + } + + /// Set the [`PrefersReducedTransparency`] value on this [`Device`]. + /// + /// Note that this does not update any associated `Stylist`. For this you must call + /// `Stylist::media_features_change_changed_style` and + /// `Stylist::force_stylesheet_origins_dirty`. + pub fn set_prefers_reduced_transparency( + &mut self, + new_prefers_reduced_transparency: PrefersReducedTransparency, + ) { + self.prefers_reduced_transparency = new_prefers_reduced_transparency; + } + + /// Returns the preferred contrast of this [`Device`]. + pub fn prefers_reduced_transparency(&self) -> PrefersReducedTransparency { + self.prefers_reduced_transparency + } + /// Returns safe area insets pub fn safe_area_insets(&self) -> SideOffsets2D { SideOffsets2D::zero() @@ -570,8 +632,38 @@ fn eval_prefers_color_scheme(context: &Context, query_value: Option) -> bool { + let prefers_contrast = context.device().prefers_contrast; + match query_value { + Some(v) => prefers_contrast == v, + None => prefers_contrast != PrefersContrast::NoPreference, + } +} + +fn eval_prefers_reduced_motion( + context: &Context, + query_value: Option, +) -> bool { + let prefers_reduced_motion = context.device().prefers_reduced_motion; + match query_value { + Some(v) => prefers_reduced_motion == v, + None => prefers_reduced_motion != PrefersReducedMotion::NoPreference, + } +} + +fn eval_prefers_reduced_transparency( + context: &Context, + query_value: Option, +) -> bool { + let prefers_reduced_transparency = context.device().prefers_reduced_transparency; + match query_value { + Some(v) => prefers_reduced_transparency == v, + None => prefers_reduced_transparency != PrefersReducedTransparency::NoPreference, + } +} + /// A list with all the media features that Servo supports. -pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [ +pub static MEDIA_FEATURES: [QueryFeatureDescription; 9] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -608,4 +700,25 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [ keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), FeatureFlags::empty(), ), + feature!( + atom!("prefers-contrast"), + AllowsRanges::No, + keyword_evaluator!(eval_prefers_contrast, PrefersContrast), + FeatureFlags::empty(), + ), + feature!( + atom!("prefers-reduced-motion"), + AllowsRanges::No, + keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion), + FeatureFlags::empty(), + ), + feature!( + atom!("prefers-reduced-transparency"), + AllowsRanges::No, + keyword_evaluator!( + eval_prefers_reduced_transparency, + PrefersReducedTransparency + ), + FeatureFlags::empty(), + ), ]; diff --git a/style/servo/selector_parser.rs b/style/servo/selector_parser.rs index 743f71a444..cdb4794efe 100644 --- a/style/servo/selector_parser.rs +++ b/style/servo/selector_parser.rs @@ -94,7 +94,7 @@ impl ToCss for PseudoElement { Selection => "::selection", Backdrop => "::backdrop", DetailsSummary => "::-servo-details-summary", - DetailsContent => "::-servo-details-content", + DetailsContent => "::details-content", Marker => "::marker", ColorSwatch => "::color-swatch", Placeholder => "::placeholder", @@ -248,10 +248,10 @@ impl PseudoElement { | PseudoElement::DetailsSummary | PseudoElement::Marker | PseudoElement::Placeholder + | PseudoElement::DetailsContent | PseudoElement::ServoTextControlInnerContainer | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy, - PseudoElement::DetailsContent - | PseudoElement::ServoAnonymousBox + PseudoElement::ServoAnonymousBox | PseudoElement::ServoAnonymousTable | PseudoElement::ServoAnonymousTableCell | PseudoElement::ServoAnonymousTableRow @@ -328,6 +328,7 @@ pub enum NonTSPseudoClass { MozMeterOptimum, MozMeterSubOptimum, MozMeterSubSubOptimum, + Open, Optional, OutOfRange, PlaceholderShown, @@ -406,6 +407,7 @@ impl ToCss for NonTSPseudoClass { Self::MozMeterOptimum => ":-moz-meter-optimum", Self::MozMeterSubOptimum => ":-moz-meter-sub-optimum", Self::MozMeterSubSubOptimum => ":-moz-meter-sub-sub-optimum", + Self::Open => ":open", Self::Optional => ":optional", Self::OutOfRange => ":out-of-range", Self::PlaceholderShown => ":placeholder-shown", @@ -450,6 +452,7 @@ impl NonTSPseudoClass { Self::MozMeterOptimum => ElementState::OPTIMUM, Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM, Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM, + Self::Open => ElementState::OPEN, Self::Optional => ElementState::OPTIONAL_, Self::OutOfRange => ElementState::OUTOFRANGE, Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN, @@ -571,6 +574,8 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { "indeterminate" => NonTSPseudoClass::Indeterminate, "invalid" => NonTSPseudoClass::Invalid, "link" => NonTSPseudoClass::Link, + "modal" => NonTSPseudoClass::Modal, + "open" => NonTSPseudoClass::Open, "optional" => NonTSPseudoClass::Optional, "out-of-range" => NonTSPseudoClass::OutOfRange, "placeholder-shown" => NonTSPseudoClass::PlaceholderShown, @@ -638,12 +643,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } DetailsSummary }, - "-servo-details-content" => { - if !self.in_user_agent_stylesheet() { - return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) - } - DetailsContent - }, + "details-content" => DetailsContent, "color-swatch" => ColorSwatch, "placeholder" => { if !self.in_user_agent_stylesheet() { diff --git a/style/servo/url.rs b/style/servo/url.rs index 95491dee1d..f2836ee52b 100644 --- a/style/servo/url.rs +++ b/style/servo/url.rs @@ -68,7 +68,12 @@ impl CssUrl { /// FIXME(emilio): Should honor CorsMode. pub fn parse_from_string(url: String, context: &ParserContext, _: CorsMode) -> Self { let serialization = Arc::new(url); - let resolved = context.url_data.0.join(&serialization).ok().map(Arc::new); + // Per https://drafts.csswg.org/css-values-4/#url-empty + // If the original url is empty, then the resolved url is considered invalid. + let resolved = (!serialization.is_empty()) + .then(|| context.url_data.0.join(&serialization)) + .and_then(Result::ok) + .map(Arc::new); CssUrl(Arc::new(CssUrlData { original: Some(serialization), resolved: resolved, @@ -120,7 +125,10 @@ impl CssUrl { pub fn new_for_testing(url: &str) -> Self { CssUrl(Arc::new(CssUrlData { original: Some(Arc::new(url.into())), - resolved: ::url::Url::parse(url).ok().map(Arc::new), + resolved: (!url.is_empty()) + .then(|| ::url::Url::parse(url)) + .and_then(Result::ok) + .map(Arc::new), })) } diff --git a/style/stylesheet_set.rs b/style/stylesheet_set.rs index 0b2c16ae52..327048ef7c 100644 --- a/style/stylesheet_set.rs +++ b/style/stylesheet_set.rs @@ -518,7 +518,7 @@ where debug!("DocumentStylesheetSet::flush_without_invalidation"); let mut origins = OriginSet::empty(); - self.invalidations.clear(); + std::mem::take(&mut self.invalidations); for (collection, origin) in self.collections.iter_mut_origins() { if collection.flush().dirty() { diff --git a/style/stylist.rs b/style/stylist.rs index 03b44de8bf..693eeb79d3 100644 --- a/style/stylist.rs +++ b/style/stylist.rs @@ -2042,7 +2042,6 @@ impl Default for LayerOrderedMap { } } -#[cfg(feature = "gecko")] impl LayerOrderedVec { fn clear(&mut self) { self.0.clear(); @@ -3604,7 +3603,6 @@ impl CascadeData { order.inc(); } } - #[cfg(feature = "gecko")] self.extra_data.sort_by_layer(&self.layers); self.animations .sort_with(&self.layers, compare_keyframes_in_same_layer); @@ -4533,7 +4531,6 @@ impl CascadeData { .push(ContainerConditionReference::none()); self.scope_conditions.clear(); self.scope_conditions.push(ScopeConditionReference::none()); - #[cfg(feature = "gecko")] self.extra_data.clear(); self.rules_source_order = 0; self.num_selectors = 0; diff --git a/style/values/computed/length.rs b/style/values/computed/length.rs index c53ba6c231..9db707f27f 100644 --- a/style/values/computed/length.rs +++ b/style/values/computed/length.rs @@ -13,11 +13,13 @@ use crate::values::computed::{NonNegativeNumber, Percentage, Zoom}; use crate::values::generics::length::{ GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize, }; +#[cfg(feature = "gecko")] use crate::values::generics::position::TreeScoped; use crate::values::generics::NonNegative; use crate::values::generics::{length as generics, ClampToNonNegative}; use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue}; use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase}; +#[cfg(feature = "gecko")] use crate::values::DashedIdent; use crate::values::{specified, CSSFloat}; use crate::Zero; diff --git a/style/values/computed/list.rs b/style/values/computed/list.rs index 3e5d1eb220..4426dc7dce 100644 --- a/style/values/computed/list.rs +++ b/style/values/computed/list.rs @@ -4,7 +4,6 @@ //! `list` computed values. -#[cfg(feature = "gecko")] pub use crate::values::specified::list::ListStyleType; pub use crate::values::specified::list::Quotes; diff --git a/style/values/computed/mod.rs b/style/values/computed/mod.rs index 58fcb2a3fb..c5d4d6849f 100644 --- a/style/values/computed/mod.rs +++ b/style/values/computed/mod.rs @@ -83,7 +83,6 @@ pub use self::length::{CSSPixelLength, NonNegativeLength}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, Margin, MaxSize, Size}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; -#[cfg(feature = "gecko")] pub use self::list::ListStyleType; pub use self::list::Quotes; pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate}; diff --git a/style/values/computed/position.rs b/style/values/computed/position.rs index a9eb709612..d7b734c455 100644 --- a/style/values/computed/position.rs +++ b/style/values/computed/position.rs @@ -12,10 +12,12 @@ use crate::values::computed::{ Context, Integer, LengthPercentage, NonNegativeNumber, Percentage, ToComputedValue, }; use crate::values::generics; +#[cfg(feature = "gecko")] +use crate::values::generics::position::TreeScoped; use crate::values::generics::position::{ AnchorSideKeyword, AspectRatio as GenericAspectRatio, GenericAnchorFunction, GenericAnchorSide, GenericInset, Position as GenericPosition, PositionComponent as GenericPositionComponent, - PositionOrAuto as GenericPositionOrAuto, TreeScoped, ZIndex as GenericZIndex, + PositionOrAuto as GenericPositionOrAuto, ZIndex as GenericZIndex, }; pub use crate::values::specified::position::{ AnchorName, AnchorScope, DashedIdentAndOrTryTactic, GridAutoFlow, GridTemplateAreas, diff --git a/style/values/specified/box.rs b/style/values/specified/box.rs index 170433e188..81d2a39fa6 100644 --- a/style/values/specified/box.rs +++ b/style/values/specified/box.rs @@ -28,7 +28,7 @@ fn grid_enabled() -> bool { #[cfg(feature = "servo")] fn grid_enabled() -> bool { - style_config::get_bool("layout.grid.enabled") + static_prefs::pref!("layout.grid.enabled") } /// The specified value of `overflow-clip-margin`. diff --git a/style/values/specified/font.rs b/style/values/specified/font.rs index 7182698dea..db041a26de 100644 --- a/style/values/specified/font.rs +++ b/style/values/specified/font.rs @@ -1768,7 +1768,7 @@ impl XTextScale { ToShmem, ToTyped, )] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, Eq, Hash, Serialize))] /// Internal property that reflects the lang attribute pub struct XLang(#[css(skip)] pub Atom); diff --git a/style/values/specified/list.rs b/style/values/specified/list.rs index 9d6a856b0a..f28211082d 100644 --- a/style/values/specified/list.rs +++ b/style/values/specified/list.rs @@ -8,8 +8,6 @@ use crate::counter_style::{CounterStyle, CounterStyleParsingFlags}; use crate::derives::*; use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "servo")] -use crate::properties::longhands::list_style_type::SpecifiedValue as ListStyleType; use cssparser::{Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind}; @@ -88,6 +86,127 @@ impl Parse for ListStyleType { } } +/// Specified and computed `list-style-type` property. +#[cfg(feature = "servo")] +#[derive( + Clone, + Debug, + Eq, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToCss, + ToComputedValue, + ToResolvedValue, + ToShmem, + ToTyped, +)] +pub enum ListStyleType { + /// A filled circle, similar to • U+2022 BULLET. + /// + Disc, + /// The element has no marker string. + /// + None, + /// A hollow circle, similar to ◦ U+25E6 WHITE BULLET. + /// + Circle, + /// A filled square, similar to ▪ U+25AA BLACK SMALL SQUARE. + /// + Square, + /// Symbol for indicating an open disclosure widget, such as the HTML `
` element. + /// + DisclosureOpen, + /// Symbol for indicating a closed disclosure widget, such as the HTML `
` element. + /// + DisclosureClosed, + /// Western decimal numbers (e.g., 1, 2, 3, ..., 98, 99, 100). + /// + Decimal, + /// Lowercase ASCII letters (e.g., a, b, c, ..., z, aa, ab). + /// + LowerAlpha, + /// Uppercase ASCII letters (e.g., A, B, C, ..., Z, AA, AB). + /// + UpperAlpha, + /// Arabic-indic numbering (e.g.، ١، ٢، ٣، ٤، ...، ٩٨، ٩٩، ١٠٠). + /// + ArabicIndic, + /// Bengali numbering (e.g., ১, ২, ৩, ..., ৯৮, ৯৯, ১০০). + /// + Bengali, + /// Cambodian/Khmer numbering (e.g., ១, ២, ៣, ..., ៩៨, ៩៩, ១០០). + /// + Cambodian, + /// Han decimal numbers (e.g., 一, 二, 三, ..., 九八, 九九, 一〇〇). + /// + CjkDecimal, + /// devanagari numbering (e.g., १, २, ३, ..., ९८, ९९, १००). + /// + Devanagari, + /// Gujarati numbering (e.g., ૧, ૨, ૩, ..., ૯૮, ૯૯, ૧૦૦). + /// + Gujarati, + /// Gurmukhi numbering (e.g., ੧, ੨, ੩, ..., ੯੮, ੯੯, ੧੦੦). + /// + Gurmukhi, + /// Kannada numbering (e.g., ೧, ೨, ೩, ..., ೯೮, ೯೯, ೧೦೦). + /// + Kannada, + /// Cambodian/Khmer numbering (e.g., ១, ២, ៣, ..., ៩៨, ៩៩, ១០០). + /// + Khmer, + /// Laotian numbering (e.g., ໑, ໒, ໓, ..., ໙໘, ໙໙, ໑໐໐). + /// + Malayalam, + /// Mongolian numbering (e.g., ᠑, ᠒, ᠓, ..., ᠙᠘, ᠙᠙, ᠑᠐᠐). + /// + Mongolian, + /// Myanmar (Burmese) numbering (e.g., ၁, ၂, ၃, ..., ၉၈, ၉၉, ၁၀၀). + /// + Myanmar, + /// Oriya numbering (e.g., ୧, ୨, ୩, ..., ୯୮, ୯୯, ୧୦୦). + /// + Oriya, + /// Persian numbering (e.g., ۱, ۲, ۳, ۴, ..., ۹۸, ۹۹, ۱۰۰). + /// + Persian, + /// Telugu numbering (e.g., ౧, ౨, ౩, ..., ౯౮, ౯౯, ౧౦౦). + /// + Telugu, + /// Thai (Siamese) numbering (e.g., ๑, ๒, ๓, ..., ๙๘, ๙๙, ๑๐๐). + /// + Thai, + /// Tibetan numbering (e.g., ༡, ༢, ༣, ..., ༩༨, ༩༩, ༡༠༠). + /// + Tibetan, + /// Han "Earthly Branch" ordinals (e.g., 子, 丑, 寅, ..., 亥). + /// + CjkEarthlyBranch, + /// Han "Heavenly Stem" ordinals (e.g., 甲, 乙, 丙, ..., 癸) + /// + CjkHeavenlyStem, + /// Lowercase classical Greek (e.g., α, β, γ, ..., ω, αα, αβ). + /// + LowerGreek, + /// Dictionary-order hiragana lettering (e.g., あ, い, う, ..., ん, ああ, あい). + /// + Hiragana, + /// Iroha-order hiragana lettering (e.g., い, ろ, は, ..., す, いい, いろ). + /// + HiraganaIroha, + /// Dictionary-order katakana lettering (e.g., ア, イ, ウ, ..., ン, アア, アイ). + /// + Katakana, + /// Iroha-order katakana lettering (e.g., イ, ロ, ハ, ..., ス, イイ, イロ) + /// + KatakanaIroha, +} + #[cfg(feature = "servo")] impl ListStyleType { /// Initial specified value for `list-style-type`. diff --git a/style/values/specified/mod.rs b/style/values/specified/mod.rs index f7433b82e2..66f193de2f 100644 --- a/style/values/specified/mod.rs +++ b/style/values/specified/mod.rs @@ -78,7 +78,6 @@ pub use self::length::{NoCalcLength, ViewportPercentageLength, ViewportVariant}; pub use self::length::{ NonNegativeLength, NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto, }; -#[cfg(feature = "gecko")] pub use self::list::ListStyleType; pub use self::list::Quotes; pub use self::motion::{OffsetPath, OffsetPosition, OffsetRotate}; diff --git a/style_derive/Cargo.toml b/style_derive/Cargo.toml index 8d8a85f62d..b1c84ad073 100644 --- a/style_derive/Cargo.toml +++ b/style_derive/Cargo.toml @@ -1,11 +1,12 @@ [package] -name = "style_derive" -version = "0.0.1" +name = "stylo_derive" +version.workspace = true authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "Derive crate for Stylo CSS engine" +readme = "../README.md" [lib] path = "lib.rs" diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index 9ba394fe91..323d6f0b85 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -1,33 +1,34 @@ [package] -name = "style_traits" -version = "0.0.1" +name = "stylo_traits" +version.workspace = true authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" edition = "2021" description = "Types used by the Stylo CSS engine" +readme = "../README.md" [lib] name = "style_traits" path = "lib.rs" [features] +default = ["servo"] servo = ["stylo_atoms", "cssparser/serde", "url", "euclid/serde"] -gecko = ["nsstring"] +gecko = [] [dependencies] app_units = "0.7" bitflags = "2" cssparser = "0.36" euclid = "0.22" -malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} -selectors = { path = "../selectors" } +malloc_size_of = { workspace = true} +malloc_size_of_derive = "0.1" +selectors = { workspace = true} serde = "1.0" -servo_arc = { path = "../servo_arc" } -stylo_atoms = { path = "../atoms", optional = true } +servo_arc = { workspace = true} +stylo_atoms = { workspace = true, optional = true } thin-vec = "0.2" -to_shmem = { path = "../to_shmem" } -to_shmem_derive = { path = "../to_shmem_derive" } +to_shmem = { workspace = true} +to_shmem_derive = { workspace = true} url = { version = "2.5", optional = true } diff --git a/stylo_atoms/Cargo.toml b/stylo_atoms/Cargo.toml new file mode 100644 index 0000000000..e39f28f9b1 --- /dev/null +++ b/stylo_atoms/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "stylo_atoms" +version.workspace = true +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_atoms/" +description = "Interned string type for the Servo and Stylo projects" +repository = "https://github.com/servo/stylo" +license = "MPL-2.0" +edition = "2018" +build = "build.rs" +readme = "../README.md" + +[lib] +path = "lib.rs" + +[dependencies] +string_cache = "0.9" + +[build-dependencies] +string_cache_codegen = "0.6.1" diff --git a/stylo_atoms/build.rs b/stylo_atoms/build.rs new file mode 100644 index 0000000000..b5f6775724 --- /dev/null +++ b/stylo_atoms/build.rs @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +fn main() { + let static_atoms = + Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt"); + let static_atoms = BufReader::new(File::open(&static_atoms).unwrap()); + let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!"); + + macro_rules! predefined { + ($($name: expr,)+) => { + { + $( + atom_type.atom($name); + )+ + } + } + } + include!("./predefined_counter_styles.rs"); + + atom_type + .atoms(static_atoms.lines().map(Result::unwrap)) + .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs")) + .unwrap(); +} diff --git a/stylo_atoms/lib.rs b/stylo_atoms/lib.rs new file mode 100644 index 0000000000..03560a40c0 --- /dev/null +++ b/stylo_atoms/lib.rs @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +include!(concat!(env!("OUT_DIR"), "/atom.rs")); diff --git a/stylo_atoms/predefined_counter_styles.rs b/stylo_atoms/predefined_counter_styles.rs new file mode 100644 index 0000000000..f376981e32 --- /dev/null +++ b/stylo_atoms/predefined_counter_styles.rs @@ -0,0 +1,66 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + + // THIS FILE IS DUPLICATED FROM style/counter_style/predefined.rs. + // TO UPDATE IT: + // - Run `python style/counter_style/updated_predefined.py` + // - Re-copy style/counter_style/predefined.rs to this location + +predefined! { + "decimal", + "decimal-leading-zero", + "arabic-indic", + "armenian", + "upper-armenian", + "lower-armenian", + "bengali", + "cambodian", + "khmer", + "cjk-decimal", + "devanagari", + "georgian", + "gujarati", + "gurmukhi", + "hebrew", + "kannada", + "lao", + "malayalam", + "mongolian", + "myanmar", + "oriya", + "persian", + "lower-roman", + "upper-roman", + "tamil", + "telugu", + "thai", + "tibetan", + "lower-alpha", + "lower-latin", + "upper-alpha", + "upper-latin", + "cjk-earthly-branch", + "cjk-heavenly-stem", + "lower-greek", + "hiragana", + "hiragana-iroha", + "katakana", + "katakana-iroha", + "disc", + "circle", + "square", + "disclosure-open", + "disclosure-closed", + "japanese-informal", + "japanese-formal", + "korean-hangul-formal", + "korean-hanja-informal", + "korean-hanja-formal", + "simp-chinese-informal", + "simp-chinese-formal", + "trad-chinese-informal", + "trad-chinese-formal", + "cjk-ideographic", + "ethiopic-numeric", +} diff --git a/stylo_atoms/static_atoms.txt b/stylo_atoms/static_atoms.txt new file mode 100644 index 0000000000..b51c5a0c8b --- /dev/null +++ b/stylo_atoms/static_atoms.txt @@ -0,0 +1,189 @@ +-moz-content-preferred-color-scheme +-moz-device-pixel-ratio +-moz-fixed-pos-containing-block +-moz-gtk-csd-close-button-position +-moz-gtk-csd-maximize-button-position +-moz-gtk-csd-menu-radius +-moz-gtk-csd-minimize-button-position +-moz-gtk-csd-titlebar-button-spacing +-moz-gtk-csd-titlebar-radius +-moz-gtk-csd-tooltip-radius +-moz-gtk-menu-radius +-moz-mac-titlebar-height +-moz-overlay-scrollbar-fade-duration +DOMContentLoaded +abort +activate +addtrack +animationcancel +animationend +animationiteration +animationstart +aspect-ratio +beforetoggle +beforeunload +block-size +button +canplay +canplaythrough +center +change +characteristicvaluechanged +checkbox +cancel +click +close +closing +color +command +complete +compositionend +compositionstart +compositionupdate +controllerchange +cursive +dark +datachannel +date +datetime-local +dir +device-pixel-ratio +durationchange +email +emptied +end +ended +error +fantasy +fetch +file +fill +fill-opacity +formdata +fullscreenchange +fullscreenerror +gattserverdisconnected +hairline +hashchange +height +hidden +icecandidate +iceconnectionstatechange +icegatheringstatechange +image +inline-size +input +inputsourceschange +invalid +keydown +keypress +kind +left +light +ltr +load +loadeddata +loadedmetadata +loadend +loadstart +message +message +messageerror +monospace +month +mousedown +mousemove +mouseover +mouseup +negotiationneeded +none +normal +number +onchange +open +orientation +pagehide +pageshow +password +pause +play +playing +popstate +postershown +prefers-color-scheme +prefers-contrast +prefers-reduced-motion +prefers-reduced-transparency +print +progress +radio +range +ratechange +readystatechange +referrer +reftest-wait +rejectionhandled +removetrack +reset +resize +resolution +resourcetimingbufferfull +right +rtl +sans-serif +safe-area-inset-top +safe-area-inset-bottom +safe-area-inset-left +safe-area-inset-right +scan +screen +scroll-position +scrollbar-inline-size +search +seeked +seeking +select +selectend +selectionchange +selectstart +serif +sessionavailable +show +signalingstatechange +slotchange +squeeze +squeezeend +squeezestart +srclang +statechange +stroke +stroke-opacity +storage +submit +suspend +system-ui +tel +text +time +timeupdate +toggle +track +transitioncancel +transitionend +transitionrun +transitionstart +uncapturederror +unhandledrejection +unload +url +visibilitychange +volumechange +waiting +webglcontextcreationerror +webkitAnimationEnd +webkitAnimationIteration +webkitAnimationStart +webkitTransitionEnd +webkitTransitionRun +week +width diff --git a/stylo_dom/Cargo.toml b/stylo_dom/Cargo.toml new file mode 100644 index 0000000000..5d25f57c50 --- /dev/null +++ b/stylo_dom/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "stylo_dom" +version.workspace = true +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_dom/" +description = "DOM state types for Stylo" +repository = "https://github.com/servo/stylo" +keywords = ["css", "style"] +license = "MPL-2.0" +edition = "2021" +readme = "../README.md" + +[lib] +path = "lib.rs" + +[dependencies] +bitflags = "2" +malloc_size_of = { workspace = true } diff --git a/stylo_dom/lib.rs b/stylo_dom/lib.rs new file mode 100644 index 0000000000..98a0330cf4 --- /dev/null +++ b/stylo_dom/lib.rs @@ -0,0 +1,181 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use bitflags::bitflags; +use malloc_size_of::malloc_size_of_is_0; + +pub const HEADING_LEVEL_OFFSET: usize = 52; + +// DOM types to be shared between Rust and C++. +bitflags! { + /// Event-based element states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct ElementState: u64 { + /// The mouse is down on this element. + /// + /// FIXME(#7333): set/unset this when appropriate + const ACTIVE = 1 << 0; + /// This element has focus. + /// + const FOCUS = 1 << 1; + /// The mouse is hovering over this element. + /// + const HOVER = 1 << 2; + /// Content is enabled (and can be disabled). + /// + const ENABLED = 1 << 3; + /// Content is disabled. + /// + const DISABLED = 1 << 4; + /// Content is checked. + /// + const CHECKED = 1 << 5; + /// + const INDETERMINATE = 1 << 6; + /// + const PLACEHOLDER_SHOWN = 1 << 7; + /// + const URLTARGET = 1 << 8; + /// + const FULLSCREEN = 1 << 9; + /// + const VALID = 1 << 10; + /// + const INVALID = 1 << 11; + /// + const USER_VALID = 1 << 12; + /// + const USER_INVALID = 1 << 13; + /// All the validity bits at once. + const VALIDITY_STATES = Self::VALID.bits() | Self::INVALID.bits() | Self::USER_VALID.bits() | Self::USER_INVALID.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken + const BROKEN = 1 << 14; + /// + const REQUIRED = 1 << 15; + /// + /// We use an underscore to workaround a silly windows.h define. + const OPTIONAL_ = 1 << 16; + /// + const DEFINED = 1 << 17; + /// + const VISITED = 1 << 18; + /// + const UNVISITED = 1 << 19; + /// + const VISITED_OR_UNVISITED = Self::VISITED.bits() | Self::UNVISITED.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over + const DRAGOVER = 1 << 20; + /// + const INRANGE = 1 << 21; + /// + const OUTOFRANGE = 1 << 22; + /// + const READONLY = 1 << 23; + /// + const READWRITE = 1 << 24; + /// + const DEFAULT = 1 << 25; + /// Non-standard & undocumented. + const OPTIMUM = 1 << 26; + /// Non-standard & undocumented. + const SUB_OPTIMUM = 1 << 27; + /// Non-standard & undocumented. + const SUB_SUB_OPTIMUM = 1 << 28; + /// All the above bits in one place. + const METER_OPTIMUM_STATES = Self::OPTIMUM.bits() | Self::SUB_OPTIMUM.bits() | Self::SUB_SUB_OPTIMUM.bits(); + /// Non-standard & undocumented. + const INCREMENT_SCRIPT_LEVEL = 1 << 29; + /// + const FOCUSRING = 1 << 30; + /// + const FOCUS_WITHIN = 1u64 << 31; + /// :dir matching; the states are used for dynamic change detection. + /// State that elements that match :dir(ltr) are in. + const LTR = 1u64 << 32; + /// State that elements that match :dir(rtl) are in. + const RTL = 1u64 << 33; + /// State that HTML elements that have a "dir" attr are in. + const HAS_DIR_ATTR = 1u64 << 34; + /// State that HTML elements with dir="ltr" (or something + /// case-insensitively equal to "ltr") are in. + const HAS_DIR_ATTR_LTR = 1u64 << 35; + /// State that HTML elements with dir="rtl" (or something + /// case-insensitively equal to "rtl") are in. + const HAS_DIR_ATTR_RTL = 1u64 << 36; + /// State that HTML elements without a valid-valued "dir" attr or + /// any HTML elements (including ) with dir="auto" (or something + /// case-insensitively equal to "auto") are in. + const HAS_DIR_ATTR_LIKE_AUTO = 1u64 << 37; + /// Non-standard & undocumented. + const AUTOFILL = 1u64 << 38; + /// Non-standard & undocumented. + const AUTOFILL_PREVIEW = 1u64 << 39; + /// State for modal elements: + /// + const MODAL = 1u64 << 40; + /// + const INERT = 1u64 << 41; + /// State for the topmost modal element in top layer + const TOPMOST_MODAL = 1u64 << 42; + /// Initially used for the devtools highlighter, but now somehow only + /// used for the devtools accessibility inspector. + const DEVTOOLS_HIGHLIGHTED = 1u64 << 43; + /// Used for the devtools style editor. Probably should go away. + const STYLEEDITOR_TRANSITIONING = 1u64 << 44; + /// For :-moz-value-empty (to show widgets like the reveal password + /// button or the clear button). + const VALUE_EMPTY = 1u64 << 45; + /// For :-moz-revealed. + const REVEALED = 1u64 << 46; + /// https://html.spec.whatwg.org/#selector-popover-open + /// Match element's popover visibility state of showing + const POPOVER_OPEN = 1u64 << 47; + /// https://drafts.csswg.org/css-scoping-1/#the-has-slotted-pseudo + /// Match whether a slot element has assigned nodes + const HAS_SLOTTED = 1u64 << 48; + /// https://drafts.csswg.org/selectors-4/#open-state + /// Match whether an openable element is currently open + const OPEN = 1u64 << 49; + /// For :active-view-transition. + /// + const ACTIVE_VIEW_TRANSITION = 1u64 << 50; + /// For :-moz-suppress-for-print-selection. + const SUPPRESS_FOR_PRINT_SELECTION = 1u64 << 51; + /// https://drafts.csswg.org/selectors-5/#headings + /// These 4 bits are used to pack the elements heading level into the element state + /// Heading levels can be from 1-9 so 4 bits allows us to express the full range. + const HEADING_LEVEL_BITS = 0b1111u64 << HEADING_LEVEL_OFFSET; + + /// Some convenience unions. + const DIR_STATES = Self::LTR.bits() | Self::RTL.bits(); + + const DIR_ATTR_STATES = Self::HAS_DIR_ATTR.bits() | + Self::HAS_DIR_ATTR_LTR.bits() | + Self::HAS_DIR_ATTR_RTL.bits() | + Self::HAS_DIR_ATTR_LIKE_AUTO.bits(); + + const DISABLED_STATES = Self::DISABLED.bits() | Self::ENABLED.bits(); + + const REQUIRED_STATES = Self::REQUIRED.bits() | Self::OPTIONAL_.bits(); + } +} + +bitflags! { + /// Event-based document states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct DocumentState: u64 { + /// Window activation status + const WINDOW_INACTIVE = 1 << 0; + /// RTL locale: specific to the XUL localedir attribute + const RTL_LOCALE = 1 << 1; + /// LTR locale: specific to the XUL localedir attribute + const LTR_LOCALE = 1 << 2; + + const ALL_LOCALEDIR_BITS = Self::LTR_LOCALE.bits() | Self::RTL_LOCALE.bits(); + } +} + +malloc_size_of_is_0!(ElementState, DocumentState); diff --git a/stylo_static_prefs/Cargo.toml b/stylo_static_prefs/Cargo.toml new file mode 100644 index 0000000000..3274bf47fe --- /dev/null +++ b/stylo_static_prefs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "stylo_static_prefs" +version.workspace = true +authors = ["The Servo Project Developers"] +documentation = "https://docs.rs/stylo_static_prefs/" +description = "Configuration for Stylo" +repository = "https://github.com/servo/stylo" +keywords = ["css", "style"] +license = "MPL-2.0" +edition = "2021" +readme = "../README.md" diff --git a/stylo_static_prefs/src/lib.rs b/stylo_static_prefs/src/lib.rs new file mode 100644 index 0000000000..69349ec807 --- /dev/null +++ b/stylo_static_prefs/src/lib.rs @@ -0,0 +1,181 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::collections::HashMap; +use std::sync::{LazyLock, RwLock}; + +/// Returns the default value for a preference exposed to the style crate. +/// This is what will be used if the embedder has not set the preference. +#[macro_export] +macro_rules! default_value { + ("layout.css.contrast-color.enabled") => { + true + }; + ("layout.css.fit-content-function.enabled") => { + true + }; + ("layout.css.font-variations.enabled") => { + true + }; + ("layout.css.gradient-color-interpolation-method.enabled") => { + true + }; + ("layout.css.outline-offset.snapping") => { + 1 + }; + ("layout.css.properties-and-values.enabled") => { + true + }; + ("layout.css.relative-color-syntax.enabled") => { + true + }; + ("layout.css.stretch-size-keyword.enabled") => { + true + }; + ("layout.css.stylo-local-work-queue.in-main-thread") => { + 32 + }; + ("layout.css.stylo-local-work-queue.in-worker") => { + 0 + }; + ("layout.css.stylo-work-unit-size") => { + 16 + }; + ("layout.css.system-ui.enabled") => { + true + }; + ("layout.css.webkit-fill-available.all-size-properties.enabled") => { + true + }; + ("layout.css.webkit-fill-available.enabled") => { + true + }; + ("layout.threads") => { + // Negative means auto, 0 disables the thread-pool (main-thread styling), + // other numbers override as specified. + -1 + }; + ($string:literal) => { + false + }; +} + +/// Returns the value of a preference exposed to the style crate. If the embedder +/// has not set a value for it, this returns the default value of the preference. +#[macro_export] +macro_rules! pref { + ($string:tt) => { + $crate::Getter::get($string, $crate::default_value!($string)) + }; +} + +static PREFS: LazyLock = LazyLock::new(Preferences::default); + +#[derive(Debug, Default)] +struct Preferences { + bool_prefs: RwLock>, + i32_prefs: RwLock>, +} + +impl Preferences { + pub fn get_bool(&self, key: &str, default: bool) -> bool { + let prefs = self.bool_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&default) + } + + pub fn get_i32(&self, key: &str, default: i32) -> i32 { + let prefs = self.i32_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&default) + } + + pub fn set_bool(&self, key: &str, value: bool) { + let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } + + pub fn set_i32(&self, key: &str, value: i32) { + let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } +} + +pub fn get_bool(key: &str, default: bool) -> bool { + PREFS.get_bool(key, default) +} + +pub fn get_i32(key: &str, default: i32) -> i32 { + PREFS.get_i32(key, default) +} + +pub fn set_bool(key: &str, value: bool) { + PREFS.set_bool(key, value) +} + +pub fn set_i32(key: &str, value: i32) { + PREFS.set_i32(key, value) +} + +pub trait Getter { + fn get(key: &str, default: Self) -> Self; +} + +impl Getter for bool { + fn get(key: &str, default: Self) -> Self { + get_bool(key, default) + } +} + +impl Getter for i32 { + fn get(key: &str, default: Self) -> Self { + get_i32(key, default) + } +} + +#[test] +fn test() { + let prefs = Preferences::default(); + + // We get the default value when the pref is not set. + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("foo", true), true); + assert_eq!(prefs.get_i32("bar", 0), 0); + assert_eq!(prefs.get_i32("bar", 1), 1); + assert_eq!(prefs.get_i32("bar", 2), 2); + + // Prefs can be set and retrieved. + prefs.set_bool("foo", true); + prefs.set_i32("bar", 1); + assert_eq!(prefs.get_bool("foo", false), true); + assert_eq!(prefs.get_bool("foo", true), true); + assert_eq!(prefs.get_i32("bar", 0), 1); + assert_eq!(prefs.get_i32("bar", 1), 1); + assert_eq!(prefs.get_i32("bar", 2), 1); + prefs.set_bool("foo", false); + prefs.set_i32("bar", 2); + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("foo", true), false); + assert_eq!(prefs.get_i32("bar", 0), 2); + assert_eq!(prefs.get_i32("bar", 1), 2); + assert_eq!(prefs.get_i32("bar", 2), 2); + + // Each value type currently has an independent namespace. + prefs.set_i32("foo", 3); + prefs.set_bool("bar", true); + assert_eq!(prefs.get_i32("foo", 0), 3); + assert_eq!(prefs.get_bool("foo", false), false); + assert_eq!(prefs.get_bool("bar", false), true); + assert_eq!(prefs.get_i32("bar", 0), 2); +} diff --git a/sync.sh b/sync.sh new file mode 100755 index 0000000000..68c8689c9c --- /dev/null +++ b/sync.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Usage: sync.sh +set -eu + +root=$(pwd) +mkdir -p "$1" +cd -- "$1" +filtered=$(pwd) +mkdir -p "$root/_cache" +cd "$root/_cache" +export PATH="$PWD:$PATH" + +step() { + if [ "${TERM-}" != '' ]; then + tput setaf 12 + fi + >&2 printf '* %s\n' "$*" + if [ "${TERM-}" != '' ]; then + tput sgr0 + fi +} + +step Downloading git-filter-repo if needed +if ! git filter-repo --version 2> /dev/null; then + curl -O https://raw.githubusercontent.com/newren/git-filter-repo/v2.38.0/git-filter-repo + chmod +x git-filter-repo + + git filter-repo --version +fi + +step Cloning upstream if needed +if ! [ -e upstream ]; then + git clone --bare --single-branch --branch main --progress https://github.com/mozilla-firefox/firefox.git upstream +fi + +step Updating upstream +branch=$(git -C upstream rev-parse --abbrev-ref HEAD) +git -C upstream fetch origin $branch:$branch + +step Filtering upstream +# Cloning and filtering is much faster than git filter-repo --source --target. +git clone --bare upstream -- "$filtered" +git -C "$filtered" filter-repo --force --paths-from-file "$root/style.paths" diff --git a/to_shmem/Cargo.toml b/to_shmem/Cargo.toml index 60bcd524f6..81f7fed5d5 100644 --- a/to_shmem/Cargo.toml +++ b/to_shmem/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "to_shmem" -version = "0.1.0" +version = "0.3.0" authors = ["The Servo Project Developers"] license = "MPL-2.0" repository = "https://github.com/servo/stylo" @@ -24,8 +24,8 @@ thin-vec = ["dep:thin-vec"] [dependencies] cssparser = { version = "0.36", optional = true } -servo_arc = { version = "0.4.0", path = "../servo_arc", optional = true } +servo_arc = { workspace = true, optional = true } smallbitvec = { version = "2.3.0", optional = true } smallvec = { version = "1.13", optional = true } -string_cache = { version = "0.8", optional = true } +string_cache = { version = "0.9", optional = true } thin-vec = { version = "0.2.1", optional = true }